Skip to content

Commit

Permalink
fix: allow nonASCII page numbers for book urls (BL-13882)
Browse files Browse the repository at this point in the history
  • Loading branch information
StephenMcConnel committed Nov 21, 2024
1 parent 83bba1f commit ffbceda
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 4 deletions.
8 changes: 6 additions & 2 deletions src/bloom-player-controls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import { withStyles, createTheme } from "@material-ui/core/styles";
import DragBar from "@material-ui/core/Slider";
import { bloomRed } from "./bloomPlayerTheme";
import { setDurationOfPagesWithoutNarration } from "./narration";
import { roundToNearestK } from "./utilities/mathUtils";
import { roundToNearestK, normalizeDigits } from "./utilities/mathUtils";

// This component is designed to wrap a BloomPlayer with some controls
// for things like pausing audio and motion, hiding and showing
Expand Down Expand Up @@ -1055,6 +1055,7 @@ function getExtraButtons(): IExtraButton[] {
return [];
}
}

// a bit goofy...we need some way to get react called when this code is loaded into an HTML
// document (as part of bloomPlayerControlBundle.js). When that module is loaded, any
// not-in-a-class code gets called. So we arrange in bloom-player-root.ts to call this
Expand All @@ -1068,7 +1069,10 @@ export function InitBloomPlayerControls() {
"motion",
)! as autoPlayType;
const startPageString = getQueryStringParamAndUnencode("start-page");
const startPage = startPageString ? parseInt(startPageString) : undefined;
const decimalStartPageString = normalizeDigits(startPageString);
const startPage = decimalStartPageString
? parseInt(decimalStartPageString)
: undefined;
const autoplayCountString =
getQueryStringParamAndUnencode("autoplay-count");
const autoplayCount = startPageString
Expand Down
18 changes: 17 additions & 1 deletion src/utilities/math.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { roundToNearestK } from "./mathUtils";
import { roundToNearestK, normalizeDigits } from "./mathUtils";

const roundToNearestEvenCases = [
[0, 0],
Expand Down Expand Up @@ -31,3 +31,19 @@ test.each(roundToNearest4Cases)("roundToNearestK, k=4", (input, expected) => {
const result = roundToNearestK(input, 4);
expect(result).toEqual(expected);
});

test("normalizeDigits from mathUtils.ts", () => {
expect(normalizeDigits("1234567890")).toEqual("1234567890");
expect(normalizeDigits("١٢٣٤٥٦٧٨٩٠")).toEqual("1234567890");
expect(normalizeDigits("०१२३४५६७८९")).toEqual("0123456789");
expect(normalizeDigits("٦٧٨٩٠١٢٣٤٥")).toEqual("6789012345");
expect(normalizeDigits("१२३४५६७८९०١٢٣٤٥٦٧٨٩٠")).toEqual(
"12345678901234567890",
);
expect(normalizeDigits("١٢٣٤٥٦٧٨٩٠cover")).toEqual("1234567890cover");
expect(normalizeDigits("test")).toEqual("test");
expect(normalizeDigits("cover")).toBeUndefined(); // special case
expect(normalizeDigits("")).toBeUndefined();
expect(normalizeDigits(undefined)).toBeUndefined();
expect(normalizeDigits(null)).toBeUndefined();
});
31 changes: 31 additions & 0 deletions src/utilities/mathUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,34 @@ export function roundToNearestEven(x: number): number {
export function roundToNearestK(x: number, k: number): number {
return Math.round(x / k) * k;
}

// This may not be a math function, strictly speaking, but it's at least
// math adjacent.
/**
* Convert all Unicode digits in a string to ASCII digits.
*/
// adapted from https://stackoverflow.com/questions/17024985/javascript-cant-convert-hindi-arabic-numbers-to-real-numeric-variables
export function normalizeDigits(str): string | undefined {
if (!str || str === "cover") {
return undefined; // not a number we can parse
}
if (/^\d+$/.test(str)) {
return str; // entire string is ASCII decimal digits
}
try {
// find all characters which are DecimalNumber (property Nd), except for ASCII 0-9
return str.replace(/(?![0-9])\p{Nd}/gu, (g) => {
// all Nd blocks start at 0x...0 or end at 0x...F (and starts at 0x...6)
// if it starts at 0x...0, the ASCII decimal number is (i & 0xf)
// if it ends at 0x...F, the ASCII decimal number is (i & 0xf) - 6
// we recognize the 2 cases by testing if code | 0xf == 0x...F is still a decimal number
const code = g.charCodeAt(0);
return (
(code & 0xf) -
6 * (/\p{Nd}/u.test(String.fromCodePoint(code | 0xf)) ? 1 : 0)
);
});
} catch (e) {
return undefined;
}
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"compilerOptions": {
"target": "es5",
"target": "es6",
"module": "commonjs",
"sourceMap": true,
"jsx": "react",
Expand Down

0 comments on commit ffbceda

Please sign in to comment.