@docfy/ember 0.12.0 → 0.13.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.
@@ -12,9 +12,28 @@ interface DocfyLinkSignature {
12
12
  default: [];
13
13
  };
14
14
  }
15
+ /**
16
+ * A link to a Docfy page.
17
+ *
18
+ * Docfy addresses pages by URL, and `@to` is already the router's path, so
19
+ * neither the href nor the active state needs the target route resolved.
20
+ *
21
+ * That matters under Embroider's `splitAtRoutes`: `RouterService#recognize()`
22
+ * resolves the matched route handlers, and resolving a route inside a split
23
+ * bundle makes `@embroider/router` fetch that bundle. Calling it from a getter
24
+ * meant that merely rendering a link downloaded the page it pointed at, so a
25
+ * page linking to every section pulled the whole documentation site up front.
26
+ * Recognition now happens in `navigate`, where the bundle is about to be needed
27
+ * anyway.
28
+ */
15
29
  export default class DocfyLink extends Component<DocfyLinkSignature> {
16
30
  router: RouterService;
17
- get routeName(): string | undefined;
31
+ /**
32
+ * `@to` as the router would produce it. Docfy gives index pages a trailing
33
+ * slash (`/docs/getting-started/`) where `urlFor` did not, so it is dropped
34
+ * here to keep the rendered href unchanged.
35
+ */
36
+ get path(): string;
18
37
  get href(): string;
19
38
  get isActive(): boolean;
20
39
  navigate(event: MouseEvent): void;
@@ -1 +1 @@
1
- {"version":3,"file":"docfy-link.d.ts","sourceRoot":"","sources":["../../src/components/docfy-link.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAI3C,OAAO,KAAK,aAAa,MAAM,+BAA+B,CAAC;AAE/D,UAAU,aAAa;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,kBAAkB;IAC1B,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;KACb,CAAC;CACH;AAED,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,SAAS,CAAC,kBAAkB,CAAC;IACvC,MAAM,EAAE,aAAa,CAAC;IAEjD,IAAI,SAAS,IAAI,MAAM,GAAG,SAAS,CAIlC;IAED,IAAI,IAAI,IAAI,MAAM,CAWjB;IAED,IAAI,QAAQ,IAAI,OAAO,CAEtB;IAGD,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;CA4BlC"}
1
+ {"version":3,"file":"docfy-link.d.ts","sourceRoot":"","sources":["../../src/components/docfy-link.ts"],"names":[],"mappings":"AAAA,OAAO,SAAS,MAAM,oBAAoB,CAAC;AAI3C,OAAO,KAAK,aAAa,MAAM,+BAA+B,CAAC;AAE/D,UAAU,aAAa;IACrB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,UAAU,kBAAkB;IAC1B,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,iBAAiB,CAAC;IAC3B,MAAM,EAAE;QACN,OAAO,EAAE,EAAE,CAAC;KACb,CAAC;CACH;AASD;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,OAAO,OAAO,SAAU,SAAQ,SAAS,CAAC,kBAAkB,CAAC;IACvC,MAAM,EAAE,aAAa,CAAC;IAEjD;;;;OAIG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,IAAI,IAAI,IAAI,MAAM,CASjB;IAED,IAAI,QAAQ,IAAI,OAAO,CAgBtB;IAGD,QAAQ,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI;CAmClC"}
@@ -6,22 +6,40 @@ import { precompileTemplate } from '@ember/template-compilation';
6
6
  import { setComponentTemplate } from '@ember/component';
7
7
  import { g, i, n } from 'decorator-transforms/runtime';
8
8
 
9
+ /** Drop a trailing slash and any query string, so URLs compare by path. */function normalizePath(url) {
10
+ const path = url.split(/[?#]/)[0] ?? '';
11
+ return path.length > 1 ? path.replace(/\/+$/, '') : path;
12
+ }
13
+ /**
14
+ * A link to a Docfy page.
15
+ *
16
+ * Docfy addresses pages by URL, and `@to` is already the router's path, so
17
+ * neither the href nor the active state needs the target route resolved.
18
+ *
19
+ * That matters under Embroider's `splitAtRoutes`: `RouterService#recognize()`
20
+ * resolves the matched route handlers, and resolving a route inside a split
21
+ * bundle makes `@embroider/router` fetch that bundle. Calling it from a getter
22
+ * meant that merely rendering a link downloaded the page it pointed at, so a
23
+ * page linking to every section pulled the whole documentation site up front.
24
+ * Recognition now happens in `navigate`, where the bundle is about to be needed
25
+ * anyway.
26
+ */
9
27
  class DocfyLink extends Component {
10
28
  static {
11
29
  g(this.prototype, "router", [service('router')]);
12
30
  }
13
31
  #router = (i(this, "router"), void 0);
14
- get routeName() {
15
- const {
16
- to
17
- } = this.args;
18
- return this.router.recognize(to)?.name;
32
+ /**
33
+ * `@to` as the router would produce it. Docfy gives index pages a trailing
34
+ * slash (`/docs/getting-started/`) where `urlFor` did not, so it is dropped
35
+ * here to keep the rendered href unchanged.
36
+ */
37
+ get path() {
38
+ return normalizePath(this.args.to);
19
39
  }
20
40
  get href() {
21
- let url = this.args.to;
22
- if (this.routeName) {
23
- url = this.router.urlFor(this.routeName);
24
- }
41
+ const rootURL = this.router.rootURL?.replace(/\/+$/, '') ?? '';
42
+ const url = `${rootURL}${this.path}`;
25
43
  if (this.args.anchor) {
26
44
  return `${url}#${this.args.anchor}`;
27
45
  } else {
@@ -29,16 +47,31 @@ class DocfyLink extends Component {
29
47
  }
30
48
  }
31
49
  get isActive() {
32
- return this.router.currentRouteName === this.routeName;
50
+ const {
51
+ currentURL
52
+ } = this.router;
53
+ if (!currentURL) {
54
+ return false;
55
+ }
56
+ // `currentURL` is root-relative, but strip `rootURL` defensively so this
57
+ // holds however the router reports it.
58
+ const rootURL = this.router.rootURL?.replace(/\/+$/, '') ?? '';
59
+ const current = rootURL && currentURL.startsWith(rootURL) ? currentURL.slice(rootURL.length) : currentURL;
60
+ return normalizePath(current) === this.path;
33
61
  }
34
62
  navigate(event) {
35
63
  if (event.ctrlKey || event.metaKey) {
36
64
  return;
37
65
  }
38
- if (this.routeName && !this.args.anchor) {
39
- event.preventDefault();
40
- this.router.transitionTo(this.routeName);
66
+ if (this.args.anchor) {
67
+ return;
68
+ }
69
+ // An unrecognised `@to` is left to the browser, as before.
70
+ if (!this.router.recognize(this.path)) {
71
+ return;
41
72
  }
73
+ event.preventDefault();
74
+ this.router.transitionTo(this.path);
42
75
  }
43
76
  static {
44
77
  n(this.prototype, "navigate", [action]);
@@ -1 +1 @@
1
- {"version":3,"file":"docfy-link.js","sources":["../../src/components/docfy-link.gts"],"sourcesContent":["import Component from '@glimmer/component';\nimport { service } from '@ember/service';\nimport { action } from '@ember/object';\nimport { on } from '@ember/modifier';\nimport type RouterService from '@ember/routing/router-service';\n\ninterface DocfyLinkArgs {\n to: string;\n anchor?: string;\n activeClass?: string;\n}\n\ninterface DocfyLinkSignature {\n Args: DocfyLinkArgs;\n Element: HTMLAnchorElement;\n Blocks: {\n default: [];\n };\n}\n\nexport default class DocfyLink extends Component<DocfyLinkSignature> {\n @service('router') declare router: RouterService;\n\n get routeName(): string | undefined {\n const { to } = this.args;\n\n return this.router.recognize(to)?.name;\n }\n\n get href(): string {\n let url = this.args.to;\n if (this.routeName) {\n url = this.router.urlFor(this.routeName);\n }\n\n if (this.args.anchor) {\n return `${url}#${this.args.anchor}`;\n } else {\n return url;\n }\n }\n\n get isActive(): boolean {\n return this.router.currentRouteName === this.routeName;\n }\n\n @action\n navigate(event: MouseEvent): void {\n if (event.ctrlKey || event.metaKey) {\n return;\n }\n\n if (this.routeName && !this.args.anchor) {\n event.preventDefault();\n this.router.transitionTo(this.routeName);\n }\n }\n\n <template>\n <a\n class=\"docfy-link {{if this.isActive @activeClass}}\"\n ...attributes\n href={{this.href}}\n data-test-docfy-link\n data-test-to={{@to}}\n data-test-anchor={{@anchor}}\n data-test-is-active={{this.isActive}}\n {{on \"click\" this.navigate}}\n >\n {{yield}}\n </a>\n </template>\n}\n"],"names":["DocfyLink","Component","g","prototype","service","i","routeName","to","args","router","recognize","name","href","url","urlFor","anchor","isActive","currentRouteName","navigate","event","ctrlKey","metaKey","preventDefault","transitionTo","n","action","setComponentTemplate","precompileTemplate","strictMode","scope","on"],"mappings":";;;;;;;;AAoBe,MAAMA,kBAAkBC,SAAA,CAAU;AAAA,EAAA;AAAAC,IAAAA,CAAA,MAAAC,SAAA,EAAA,QAAA,EAAA,CAC9CC,OAAA,CAAQ,QAAA,CAAA,CAAA,CAAA;AAAA;EAAA,OAAA,IAAAC,CAAA,CAAA,IAAA,EAAA,QAAA,CAAA,EAAA,MAAA;EAET,IAAIC,SAAAA,GAAgC;IAClC,MAAM;AAAEC,MAAAA;KAAI,GAAG,IAAI,CAACC,IAAI;IAExB,OAAO,IAAI,CAACC,MAAM,CAACC,SAAS,CAACH,EAAA,CAAA,EAAKI,IAAA;AACpC,EAAA;EAEA,IAAIC,IAAAA,GAAe;AACjB,IAAA,IAAIC,GAAA,GAAM,IAAI,CAACL,IAAI,CAACD,EAAE;IACtB,IAAI,IAAI,CAACD,SAAS,EAAE;MAClBO,GAAA,GAAM,IAAI,CAACJ,MAAM,CAACK,MAAM,CAAC,IAAI,CAACR,SAAS,CAAA;AACzC,IAAA;AAEA,IAAA,IAAI,IAAI,CAACE,IAAI,CAACO,MAAM,EAAE;MACpB,OAAO,CAAA,EAAGF,OAAO,IAAI,CAACL,IAAI,CAACO,MAAM,CAAA,CAAE;AACrC,IAAA,CAAA,MAAO;AACL,MAAA,OAAOF,GAAA;AACT,IAAA;AACF,EAAA;EAEA,IAAIG,QAAAA,GAAoB;IACtB,OAAO,IAAI,CAACP,MAAM,CAACQ,gBAAgB,KAAK,IAAI,CAACX,SAAS;AACxD,EAAA;EAGAY,QAAAA,CAASC,KAAiB,EAAQ;AAChC,IAAA,IAAIA,KAAA,CAAMC,OAAO,IAAID,KAAA,CAAME,OAAO,EAAE;AAClC,MAAA;AACF,IAAA;IAEA,IAAI,IAAI,CAACf,SAAS,IAAI,CAAC,IAAI,CAACE,IAAI,CAACO,MAAM,EAAE;MACvCI,KAAA,CAAMG,cAAc,EAAA;MACpB,IAAI,CAACb,MAAM,CAACc,YAAY,CAAC,IAAI,CAACjB,SAAS,CAAA;AACzC,IAAA;AACF,EAAA;AAAA,EAAA;IAAAkB,CAAA,CAAA,IAAA,CAAArB,SAAA,EAAA,UAAA,EAAA,CAVCsB,MAAA,CAAA,CAAA;AAAA;AAYD,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,4PAAA,EAaA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}
1
+ {"version":3,"file":"docfy-link.js","sources":["../../src/components/docfy-link.gts"],"sourcesContent":["import Component from '@glimmer/component';\nimport { service } from '@ember/service';\nimport { action } from '@ember/object';\nimport { on } from '@ember/modifier';\nimport type RouterService from '@ember/routing/router-service';\n\ninterface DocfyLinkArgs {\n to: string;\n anchor?: string;\n activeClass?: string;\n}\n\ninterface DocfyLinkSignature {\n Args: DocfyLinkArgs;\n Element: HTMLAnchorElement;\n Blocks: {\n default: [];\n };\n}\n\n/** Drop a trailing slash and any query string, so URLs compare by path. */\nfunction normalizePath(url: string): string {\n const path = url.split(/[?#]/)[0] ?? '';\n\n return path.length > 1 ? path.replace(/\\/+$/, '') : path;\n}\n\n/**\n * A link to a Docfy page.\n *\n * Docfy addresses pages by URL, and `@to` is already the router's path, so\n * neither the href nor the active state needs the target route resolved.\n *\n * That matters under Embroider's `splitAtRoutes`: `RouterService#recognize()`\n * resolves the matched route handlers, and resolving a route inside a split\n * bundle makes `@embroider/router` fetch that bundle. Calling it from a getter\n * meant that merely rendering a link downloaded the page it pointed at, so a\n * page linking to every section pulled the whole documentation site up front.\n * Recognition now happens in `navigate`, where the bundle is about to be needed\n * anyway.\n */\nexport default class DocfyLink extends Component<DocfyLinkSignature> {\n @service('router') declare router: RouterService;\n\n /**\n * `@to` as the router would produce it. Docfy gives index pages a trailing\n * slash (`/docs/getting-started/`) where `urlFor` did not, so it is dropped\n * here to keep the rendered href unchanged.\n */\n get path(): string {\n return normalizePath(this.args.to);\n }\n\n get href(): string {\n const rootURL = this.router.rootURL?.replace(/\\/+$/, '') ?? '';\n const url = `${rootURL}${this.path}`;\n\n if (this.args.anchor) {\n return `${url}#${this.args.anchor}`;\n } else {\n return url;\n }\n }\n\n get isActive(): boolean {\n const { currentURL } = this.router;\n\n if (!currentURL) {\n return false;\n }\n\n // `currentURL` is root-relative, but strip `rootURL` defensively so this\n // holds however the router reports it.\n const rootURL = this.router.rootURL?.replace(/\\/+$/, '') ?? '';\n const current =\n rootURL && currentURL.startsWith(rootURL)\n ? currentURL.slice(rootURL.length)\n : currentURL;\n\n return normalizePath(current) === this.path;\n }\n\n @action\n navigate(event: MouseEvent): void {\n if (event.ctrlKey || event.metaKey) {\n return;\n }\n\n if (this.args.anchor) {\n return;\n }\n\n // An unrecognised `@to` is left to the browser, as before.\n if (!this.router.recognize(this.path)) {\n return;\n }\n\n event.preventDefault();\n this.router.transitionTo(this.path);\n }\n\n <template>\n <a\n class=\"docfy-link {{if this.isActive @activeClass}}\"\n ...attributes\n href={{this.href}}\n data-test-docfy-link\n data-test-to={{@to}}\n data-test-anchor={{@anchor}}\n data-test-is-active={{this.isActive}}\n {{on \"click\" this.navigate}}\n >\n {{yield}}\n </a>\n </template>\n}\n"],"names":["normalizePath","url","path","split","length","replace","DocfyLink","Component","g","prototype","service","i","args","to","href","rootURL","router","anchor","isActive","currentURL","current","startsWith","slice","navigate","event","ctrlKey","metaKey","recognize","preventDefault","transitionTo","n","action","setComponentTemplate","precompileTemplate","strictMode","scope","on"],"mappings":";;;;;;;;AAoBA,2EACA,SAASA,aAAAA,CAAcC,GAAW,EAAS;AACzC,EAAA,MAAMC,OAAOD,GAAA,CAAIE,KAAK,CAAC,MAAA,CAAO,CAAC,EAAE,IAAI,EAAA;AAErC,EAAA,OAAOD,IAAA,CAAKE,MAAM,GAAG,CAAA,GAAIF,KAAKG,OAAO,CAAC,QAAQ,EAAA,CAAA,GAAMH,IAAA;AACtD;AAEA;;;;;;;;;;;;;;AAce,MAAMI,SAAA,SAAkBC,SAAA,CAAU;AAAA,EAAA;AAAAC,IAAAA,CAAA,MAAAC,SAAA,EAAA,QAAA,EAAA,CAC9CC,OAAA,CAAQ,QAAA,CAAA,CAAA,CAAA;AAAA;EAAA,OAAA,IAAAC,CAAA,CAAA,IAAA,EAAA,QAAA,CAAA,EAAA,MAAA;AAET;;;;;EAKA,IAAIT,IAAAA,GAAe;AACjB,IAAA,OAAOF,aAAA,CAAc,IAAI,CAACY,IAAI,CAACC,EAAE,CAAA;AACnC,EAAA;EAEA,IAAIC,IAAAA,GAAe;AACjB,IAAA,MAAMC,OAAA,GAAU,IAAI,CAACC,MAAM,CAACD,OAAO,EAAEV,OAAA,CAAQ,MAAA,EAAQ,EAAA,CAAA,IAAO,EAAA;IAC5D,MAAMJ,MAAM,CAAA,EAAGc,OAAA,GAAU,IAAI,CAACb,IAAI,CAAA,CAAE;AAEpC,IAAA,IAAI,IAAI,CAACU,IAAI,CAACK,MAAM,EAAE;MACpB,OAAO,CAAA,EAAGhB,OAAO,IAAI,CAACW,IAAI,CAACK,MAAM,CAAA,CAAE;AACrC,IAAA,CAAA,MAAO;AACL,MAAA,OAAOhB,GAAA;AACT,IAAA;AACF,EAAA;EAEA,IAAIiB,QAAAA,GAAoB;IACtB,MAAM;AAAEC,MAAAA;KAAY,GAAG,IAAI,CAACH,MAAM;IAElC,IAAI,CAACG,UAAA,EAAY;AACf,MAAA,OAAO,KAAA;AACT,IAAA;AAEA;AACA;AACA,IAAA,MAAMJ,OAAA,GAAU,IAAI,CAACC,MAAM,CAACD,OAAO,EAAEV,OAAA,CAAQ,MAAA,EAAQ,EAAA,CAAA,IAAO,EAAA;IAC5D,MAAMe,OAAA,GACJL,OAAA,IAAWI,UAAA,CAAWE,UAAU,CAACN,OAAA,CAAA,GAC7BI,UAAA,CAAWG,KAAK,CAACP,OAAA,CAAQX,MAAM,CAAA,GAC/Be,UAAA;AAEN,IAAA,OAAOnB,aAAA,CAAcoB,OAAA,CAAA,KAAa,IAAI,CAAClB,IAAI;AAC7C,EAAA;EAGAqB,QAAAA,CAASC,KAAiB,EAAQ;AAChC,IAAA,IAAIA,KAAA,CAAMC,OAAO,IAAID,KAAA,CAAME,OAAO,EAAE;AAClC,MAAA;AACF,IAAA;AAEA,IAAA,IAAI,IAAI,CAACd,IAAI,CAACK,MAAM,EAAE;AACpB,MAAA;AACF,IAAA;AAEA;IACA,IAAI,CAAC,IAAI,CAACD,MAAM,CAACW,SAAS,CAAC,IAAI,CAACzB,IAAI,CAAA,EAAG;AACrC,MAAA;AACF,IAAA;IAEAsB,KAAA,CAAMI,cAAc,EAAA;IACpB,IAAI,CAACZ,MAAM,CAACa,YAAY,CAAC,IAAI,CAAC3B,IAAI,CAAA;AACpC,EAAA;AAAA,EAAA;IAAA4B,CAAA,CAAA,IAAA,CAAArB,SAAA,EAAA,UAAA,EAAA,CAjBCsB,MAAA,CAAA,CAAA;AAAA;AAmBD,EAAA;IAAAC,oBAAA,CAAAC,kBAAA,CAAA,4PAAA,EAaA;MAAAC,UAAA,EAAA,IAAA;AAAAC,MAAAA,KAAA,EAAAA,OAAA;AAAAC,QAAAA;AAAA,OAAA;KAAU,CAAA,EAAV,IAAW,CAAA;AAAD;AACZ;;;;"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@docfy/ember",
3
- "version": "0.12.0",
3
+ "version": "0.13.0",
4
4
  "description": "The default blueprint for Embroider v2 addons.",
5
5
  "keywords": [
6
6
  "ember-addon"
@@ -29,7 +29,7 @@
29
29
  "prepack": "rollup --config"
30
30
  },
31
31
  "dependencies": {
32
- "@docfy/core": "^0.11.0",
32
+ "@docfy/core": "^0.12.1",
33
33
  "@embroider/addon-shim": "^1.8.9",
34
34
  "@embroider/macros": "^1.18.1",
35
35
  "decorator-transforms": "^2.3.0"
@@ -39,12 +39,12 @@
39
39
  },
40
40
  "devDependencies": {
41
41
  "@babel/core": "^7.25.2",
42
- "@babel/eslint-parser": "^7.25.1",
42
+ "@babel/eslint-parser": "^8.0.1",
43
43
  "@babel/plugin-transform-typescript": "^7.25.2",
44
44
  "@babel/runtime": "^7.28.2",
45
45
  "@ember/library-tsconfig": "^1.0.0",
46
46
  "@embroider/addon-dev": "^8.1.0",
47
- "@eslint/js": "^9.32.0",
47
+ "@eslint/js": "^10.0.1",
48
48
  "@glimmer/component": "^2.0.0",
49
49
  "@glint/core": "1.5.2",
50
50
  "@glint/environment-ember-loose": "1.5.2",
@@ -56,17 +56,17 @@
56
56
  "concurrently": "^9.0.1",
57
57
  "ember-source": "^6.3.0",
58
58
  "ember-template-lint": "^7.9.0",
59
- "eslint": "^9.32.0",
59
+ "eslint": "^10.9.0",
60
60
  "eslint-config-prettier": "^10.1.8",
61
- "eslint-plugin-ember": "^12.7.0",
62
- "eslint-plugin-import": "^2.31.0",
63
- "eslint-plugin-n": "^17.15.1",
64
- "globals": "^16.1.0",
65
- "prettier": "^3.4.2",
61
+ "eslint-plugin-ember": "^13.5.0",
62
+ "eslint-plugin-import-x": "^4.17.1",
63
+ "eslint-plugin-n": "^18.3.0",
64
+ "globals": "^17.11.0",
65
+ "prettier": "^3.9.6",
66
66
  "prettier-plugin-ember-template-tag": "^2.1.0",
67
67
  "rollup": "^4.22.5",
68
68
  "typescript": "~5.8.3",
69
- "typescript-eslint": "^8.19.1"
69
+ "typescript-eslint": "^8.67.0"
70
70
  },
71
71
  "ember": {
72
72
  "edition": "octane"
@@ -105,5 +105,5 @@
105
105
  ]
106
106
  }
107
107
  },
108
- "gitHead": "ed6eef49ddb6bfb1ac4c91f94bc4d9291d202130"
108
+ "gitHead": "e548365e43a1a8b26170d0e5becfb577c113c313"
109
109
  }