@khanacademy/perseus 7.1.1 → 7.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/util.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import * as GraphieUtil from "./util.graphie";
1
2
  import type { Range } from "./perseus-types";
2
3
  import type { Widget, PerseusScore } from "./types";
3
4
  import type { KEScore } from "@khanacademy/perseus-core";
@@ -171,13 +172,7 @@ declare function extractPointerLocation(event: any): Position | null | undefined
171
172
  * avoid sending the touch to the mobile scratchpad
172
173
  */
173
174
  declare function captureScratchpadTouchStart(e: TouchEvent): void;
174
- declare function getImageSizeModern(url: string): Promise<[number, number]>;
175
175
  declare function getImageSize(url: string, callback: (width: number, height: number) => void): void;
176
- declare function getRealImageUrl(url: string): string;
177
- declare function isLabeledSVG(url: string): boolean;
178
- declare function getBaseUrl(url: string): string;
179
- declare function getSvgUrl(url: string): string;
180
- declare function getDataUrl(url: string): string;
181
176
  /**
182
177
  * Gets the word right before where the textarea cursor is
183
178
  *
@@ -234,12 +229,12 @@ declare const Util: {
234
229
  readonly supportsPassiveEvents: () => boolean;
235
230
  readonly captureScratchpadTouchStart: typeof captureScratchpadTouchStart;
236
231
  readonly getImageSize: typeof getImageSize;
237
- readonly getImageSizeModern: typeof getImageSizeModern;
238
- readonly getRealImageUrl: typeof getRealImageUrl;
239
- readonly isLabeledSVG: typeof isLabeledSVG;
240
- readonly getBaseUrl: typeof getBaseUrl;
241
- readonly getSvgUrl: typeof getSvgUrl;
242
- readonly getDataUrl: typeof getDataUrl;
232
+ readonly getImageSizeModern: typeof GraphieUtil.getImageSizeModern;
233
+ readonly getRealImageUrl: typeof GraphieUtil.getRealImageUrl;
234
+ readonly isLabeledSVG: typeof GraphieUtil.isLabeledSVG;
235
+ readonly getBaseUrl: typeof GraphieUtil.getBaseUrl;
236
+ readonly getSvgUrl: typeof GraphieUtil.getSvgUrl;
237
+ readonly getDataUrl: typeof GraphieUtil.getDataUrl;
243
238
  readonly textarea: {
244
239
  readonly getWordBeforeCursor: typeof getWordBeforeCursor;
245
240
  readonly moveCursor: typeof moveCursor;
@@ -0,0 +1,6 @@
1
+ export declare function getRealImageUrl(graphieUrl: string): string;
2
+ export declare function isLabeledSVG(graphieUrl: string): boolean;
3
+ export declare function getBaseUrl(graphieUrl: string): string;
4
+ export declare function getSvgUrl(graphieUrl: string): string;
5
+ export declare function getDataUrl(graphieUrl: string): string;
6
+ export declare function getImageSizeModern(url: string): Promise<[number, number]>;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Flowtype definitions for data
3
+ * Generated by Flowgen from a Typescript Definition
4
+ * Flowgen v1.21.0
5
+ * @flow
6
+ */
7
+ declare export function getRealImageUrl(graphieUrl: string): string;
8
+ declare export function isLabeledSVG(graphieUrl: string): boolean;
9
+ declare export function getBaseUrl(graphieUrl: string): string;
10
+ declare export function getSvgUrl(graphieUrl: string): string;
11
+ declare export function getDataUrl(graphieUrl: string): string;
12
+ declare export function getImageSizeModern(
13
+ url: string
14
+ ): Promise<[number, number]>;
package/dist/util.js.flow CHANGED
@@ -4,6 +4,7 @@
4
4
  * Flowgen v1.21.0
5
5
  * @flow
6
6
  */
7
+ import * as GraphieUtil from "./util.graphie";
7
8
  import type { Range } from "./perseus-types";
8
9
  import type { Widget, PerseusScore } from "./types";
9
10
  import type { KEScore } from "@khanacademy/perseus-core";
@@ -236,16 +237,10 @@ declare function extractPointerLocation(event: any): Position | null | void;
236
237
  * avoid sending the touch to the mobile scratchpad
237
238
  */
238
239
  declare function captureScratchpadTouchStart(e: TouchEvent): void;
239
- declare function getImageSizeModern(url: string): Promise<[number, number]>;
240
240
  declare function getImageSize(
241
241
  url: string,
242
242
  callback: (width: number, height: number) => void
243
243
  ): void;
244
- declare function getRealImageUrl(url: string): string;
245
- declare function isLabeledSVG(url: string): boolean;
246
- declare function getBaseUrl(url: string): string;
247
- declare function getSvgUrl(url: string): string;
248
- declare function getDataUrl(url: string): string;
249
244
 
250
245
  /**
251
246
  * Gets the word right before where the textarea cursor is
@@ -308,12 +303,12 @@ declare var Util: {|
308
303
  +supportsPassiveEvents: () => boolean,
309
304
  +captureScratchpadTouchStart: typeof captureScratchpadTouchStart,
310
305
  +getImageSize: typeof getImageSize,
311
- +getImageSizeModern: typeof getImageSizeModern,
312
- +getRealImageUrl: typeof getRealImageUrl,
313
- +isLabeledSVG: typeof isLabeledSVG,
314
- +getBaseUrl: typeof getBaseUrl,
315
- +getSvgUrl: typeof getSvgUrl,
316
- +getDataUrl: typeof getDataUrl,
306
+ +getImageSizeModern: typeof GraphieUtil.getImageSizeModern,
307
+ +getRealImageUrl: typeof GraphieUtil.getRealImageUrl,
308
+ +isLabeledSVG: typeof GraphieUtil.isLabeledSVG,
309
+ +getBaseUrl: typeof GraphieUtil.getBaseUrl,
310
+ +getSvgUrl: typeof GraphieUtil.getSvgUrl,
311
+ +getDataUrl: typeof GraphieUtil.getDataUrl,
317
312
  +textarea: {|
318
313
  +getWordBeforeCursor: typeof getWordBeforeCursor,
319
314
  +moveCursor: typeof moveCursor,
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "description": "Core Perseus API (includes renderers and widgets)",
4
4
  "author": "Khan Academy",
5
5
  "license": "MIT",
6
- "version": "7.1.1",
6
+ "version": "7.2.0",
7
7
  "publishConfig": {
8
8
  "access": "public"
9
9
  },
@@ -0,0 +1,45 @@
1
+ import {describe, it} from "@jest/globals";
2
+
3
+ import {getRealImageUrl, isLabeledSVG} from "./util.graphie";
4
+
5
+ describe(".getRealImageUrl", () => {
6
+ it("when passed a graphie URL, it transforms the URL to an http url", () => {
7
+ const input =
8
+ "web+graphie://ka-perseus-graphie.s3.amazonaws.com/b4a76d480e49f89fddb973c88c53fc301ba0d927";
9
+ const result =
10
+ "https://ka-perseus-graphie.s3.amazonaws.com/b4a76d480e49f89fddb973c88c53fc301ba0d927.svg";
11
+ expect(getRealImageUrl(input)).toBe(result);
12
+ });
13
+
14
+ it("when it's passed an HTTP URL, it returns the URL", () => {
15
+ const input =
16
+ "https://cdn.kastatic.org/ka-perseus-images/c633a1d04970211586b413985a7959876ca6c909.png";
17
+ expect(getRealImageUrl(input)).toBe(input);
18
+ });
19
+ });
20
+
21
+ describe(".isLabeledSVG", () => {
22
+ it("returns true for web+graphie", () => {
23
+ expect(
24
+ isLabeledSVG(
25
+ "web+graphie://ka-perseus-graphie.s3.amazonaws.com/b4a76d480e49f89fddb973c88c53fc301ba0d927",
26
+ ),
27
+ ).toBeTruthy();
28
+ });
29
+
30
+ it("returns true for file+graphie", () => {
31
+ expect(
32
+ isLabeledSVG(
33
+ "file+graphie://b4a76d480e49f89fddb973c88c53fc301ba0d927",
34
+ ),
35
+ ).toBeTruthy();
36
+ });
37
+
38
+ it("returns false for all other protocols", () => {
39
+ expect(
40
+ isLabeledSVG(
41
+ "https://cdn.kastatic.org/ka-perseus-images/c633a1d04970211586b413985a7959876ca6c909.png",
42
+ ),
43
+ ).toBeFalsy();
44
+ });
45
+ });
@@ -0,0 +1,59 @@
1
+ const svgLabelsRegex = /^web\+graphie:/;
2
+ // For offline exercises in the mobile app, we download the graphie data
3
+ // (svgs and localized data files) and serve them from the local file
4
+ // system (with file://). We replace urls that start with `web+graphie`
5
+ // in the perseus json with this `file+graphie` prefix to indicate that
6
+ // they should have the `file://` protocol instead of `https://`.
7
+ const svgLocalLabelsRegex = /^file\+graphie:/;
8
+
9
+ // Sometimes other components want to download the actual image e.g. to
10
+ // determine its size. Here, we transform an .svg-labels url into the
11
+ // correct image url, and leave normal image urls alone
12
+ export function getRealImageUrl(graphieUrl: string): string {
13
+ if (isLabeledSVG(graphieUrl)) {
14
+ return getSvgUrl(graphieUrl);
15
+ }
16
+ return graphieUrl;
17
+ }
18
+
19
+ export function isLabeledSVG(graphieUrl: string): boolean {
20
+ return (
21
+ svgLabelsRegex.test(graphieUrl) || svgLocalLabelsRegex.test(graphieUrl)
22
+ );
23
+ }
24
+
25
+ // For each graphie, there are two urls we need to download from. This gets
26
+ // the base url without the suffix, and `getSvgUrl` and `getDataUrl` apply
27
+ // appropriate suffixes to get the image and other data
28
+ export function getBaseUrl(graphieUrl: string): string {
29
+ return graphieUrl
30
+ .replace(svgLabelsRegex, "https:")
31
+ .replace(svgLocalLabelsRegex, "file:");
32
+ }
33
+
34
+ export function getSvgUrl(graphieUrl: string): string {
35
+ return getBaseUrl(graphieUrl) + ".svg";
36
+ }
37
+
38
+ export function getDataUrl(graphieUrl: string): string {
39
+ return getBaseUrl(graphieUrl) + "-data.json";
40
+ }
41
+
42
+ export async function getImageSizeModern(
43
+ url: string,
44
+ ): Promise<[number, number]> {
45
+ const image = new Image();
46
+
47
+ return new Promise((resolve, reject) => {
48
+ // Handle the success case
49
+ image.onload = () => {
50
+ resolve([image.naturalWidth, image.naturalHeight]);
51
+ };
52
+
53
+ // Handle the error case
54
+ image.onerror = reject;
55
+
56
+ // Kick off the loading
57
+ image.src = getRealImageUrl(url);
58
+ });
59
+ }
package/src/util.ts CHANGED
@@ -3,6 +3,7 @@ import _ from "underscore";
3
3
 
4
4
  import {Errors} from "./logging/log";
5
5
  import {PerseusError} from "./perseus-error";
6
+ import * as GraphieUtil from "./util.graphie";
6
7
  import KhanAnswerTypes from "./util/answer-types";
7
8
 
8
9
  import type {Range} from "./perseus-types";
@@ -51,14 +52,6 @@ type TouchHandlers = {
51
52
 
52
53
  let supportsPassive = false;
53
54
 
54
- const svgLabelsRegex = /^web\+graphie:/;
55
- // For offline exercises in the mobile app, we download the graphie data
56
- // (svgs and localized data files) and serve them from the local file
57
- // system (with file://). We replace urls that start with `web+graphie`
58
- // in the perseus json with this `file+graphie` prefix to indicate that
59
- // they should have the `file://` protocol instead of `https://`.
60
- const svgLocalLabelsRegex = /^file\+graphie:/;
61
-
62
55
  const nestedMap = function <T, M>(
63
56
  children: T | ReadonlyArray<T>,
64
57
  func: (arg1: T) => M,
@@ -839,23 +832,6 @@ function captureScratchpadTouchStart(e: TouchEvent) {
839
832
  e.stopPropagation();
840
833
  }
841
834
 
842
- async function getImageSizeModern(url: string): Promise<[number, number]> {
843
- const image = new Image();
844
-
845
- return new Promise((resolve, reject) => {
846
- // Handle the success case
847
- image.onload = () => {
848
- resolve([image.naturalWidth, image.naturalHeight]);
849
- };
850
-
851
- // Handle the error case
852
- image.onerror = reject;
853
-
854
- // Kick off the loading
855
- image.src = url;
856
- });
857
- }
858
-
859
835
  function getImageSize(
860
836
  url: string,
861
837
  callback: (width: number, height: number) => void,
@@ -884,38 +860,7 @@ function getImageSize(
884
860
  }
885
861
  };
886
862
 
887
- img.src = getRealImageUrl(url);
888
- }
889
-
890
- // Sometimes other components want to download the actual image e.g. to
891
- // determine its size. Here, we transform an .svg-labels url into the
892
- // correct image url, and leave normal image urls alone
893
- function getRealImageUrl(url: string): string {
894
- if (isLabeledSVG(url)) {
895
- return getSvgUrl(url);
896
- }
897
- return url;
898
- }
899
-
900
- function isLabeledSVG(url: string): boolean {
901
- return svgLabelsRegex.test(url) || svgLocalLabelsRegex.test(url);
902
- }
903
-
904
- // For each svg+labels, there are two urls we need to download from. This gets
905
- // the base url without the suffix, and `getSvgUrl` and `getDataUrl` apply
906
- // appropriate suffixes to get the image and other data
907
- function getBaseUrl(url: string): string {
908
- return url
909
- .replace(svgLabelsRegex, "https:")
910
- .replace(svgLocalLabelsRegex, "file:");
911
- }
912
-
913
- function getSvgUrl(url: string): string {
914
- return getBaseUrl(url) + ".svg";
915
- }
916
-
917
- function getDataUrl(url: string): string {
918
- return getBaseUrl(url) + "-data.json";
863
+ img.src = GraphieUtil.getRealImageUrl(url);
919
864
  }
920
865
 
921
866
  /**
@@ -1007,12 +952,12 @@ const Util = {
1007
952
  supportsPassiveEvents,
1008
953
  captureScratchpadTouchStart,
1009
954
  getImageSize,
1010
- getImageSizeModern,
1011
- getRealImageUrl,
1012
- isLabeledSVG,
1013
- getBaseUrl,
1014
- getSvgUrl,
1015
- getDataUrl,
955
+ getImageSizeModern: GraphieUtil.getImageSizeModern,
956
+ getRealImageUrl: GraphieUtil.getRealImageUrl,
957
+ isLabeledSVG: GraphieUtil.isLabeledSVG,
958
+ getBaseUrl: GraphieUtil.getBaseUrl,
959
+ getSvgUrl: GraphieUtil.getSvgUrl,
960
+ getDataUrl: GraphieUtil.getDataUrl,
1016
961
  textarea,
1017
962
  unescapeMathMode,
1018
963
  random,