@lblod/ember-rdfa-editor-lblod-plugins 0.3.1 → 0.4.1

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.
Files changed (269) hide show
  1. package/.release-it.json +14 -0
  2. package/.woodpecker/.release.yml +15 -0
  3. package/.woodpecker/.test.yml +23 -0
  4. package/CHANGELOG.md +95 -0
  5. package/RELEASE.md +60 -0
  6. package/addon/commands/generate-template-command.js +35 -0
  7. package/addon/commands/insert-and-collapse.js +32 -0
  8. package/addon/commands/modify-date-command.js +42 -0
  9. package/addon/components/besluit-type-plugin/besluit-type-select.hbs +28 -0
  10. package/addon/components/besluit-type-plugin/besluit-type-select.js +21 -0
  11. package/addon/components/besluit-type-plugin/toolbar-dropdown.hbs +83 -0
  12. package/addon/components/besluit-type-plugin/toolbar-dropdown.js +188 -0
  13. package/addon/components/citaten-plugin/citaat-card.hbs +76 -0
  14. package/addon/components/citaten-plugin/citaat-card.js +286 -0
  15. package/addon/components/citaten-plugin/citaat-insert.hbs +20 -0
  16. package/addon/components/citaten-plugin/citaat-insert.js +68 -0
  17. package/addon/components/citaten-plugin/citations/article-list.hbs +13 -0
  18. package/addon/components/citaten-plugin/citations/article-preview.hbs +16 -0
  19. package/addon/components/citaten-plugin/citations/decision-detail.hbs +110 -0
  20. package/addon/components/citaten-plugin/citations/decision-detail.js +94 -0
  21. package/addon/components/citaten-plugin/citations/decision-list.hbs +15 -0
  22. package/addon/components/citaten-plugin/citations/decision-preview.hbs +53 -0
  23. package/addon/components/citaten-plugin/citations/search-modal.hbs +175 -0
  24. package/addon/components/citaten-plugin/citations/search-modal.js +244 -0
  25. package/addon/components/citaten-plugin/helpers/alert-load-error.hbs +21 -0
  26. package/addon/components/citaten-plugin/helpers/alert-no-items.hbs +10 -0
  27. package/addon/components/import-snippet-plugin/card.hbs +17 -0
  28. package/addon/components/import-snippet-plugin/card.js +95 -0
  29. package/addon/components/insert-variable-plugin/insert-variable-card.hbs +33 -0
  30. package/addon/components/insert-variable-plugin/insert-variable-card.js +125 -0
  31. package/addon/components/rdfa-date-plugin/rdfa-date-plugin-card.hbs +22 -0
  32. package/addon/components/rdfa-date-plugin/rdfa-date-plugin-card.js +71 -0
  33. package/addon/components/rdfa-date-plugin/rdfa-date-plugin-insert.hbs +18 -0
  34. package/addon/components/rdfa-date-plugin/rdfa-date-plugin-insert.js +24 -0
  35. package/addon/components/rdfa-date-plugin/rdfa-date-time-picker.hbs +21 -0
  36. package/addon/components/rdfa-date-plugin/rdfa-date-time-picker.js +73 -0
  37. package/addon/components/roadsign-regulation-plugin/expanded-measure.hbs +44 -0
  38. package/addon/components/roadsign-regulation-plugin/expanded-measure.js +38 -0
  39. package/addon/components/roadsign-regulation-plugin/measure-template.hbs +5 -0
  40. package/addon/components/roadsign-regulation-plugin/measure-template.js +30 -0
  41. package/addon/components/roadsign-regulation-plugin/roadsign-regulation-card.hbs +12 -0
  42. package/addon/components/roadsign-regulation-plugin/roadsign-regulation-card.js +101 -0
  43. package/addon/components/roadsign-regulation-plugin/roadsigns-modal.hbs +92 -0
  44. package/addon/components/roadsign-regulation-plugin/roadsigns-modal.js +268 -0
  45. package/addon/components/roadsign-regulation-plugin/roadsigns-pagination.hbs +25 -0
  46. package/addon/components/roadsign-regulation-plugin/roadsigns-pagination.js +55 -0
  47. package/addon/components/roadsign-regulation-plugin/roadsigns-table.hbs +73 -0
  48. package/addon/components/roadsign-regulation-plugin/roadsigns-table.js +24 -0
  49. package/addon/components/standard-template-plugin/template-card.hbs +16 -0
  50. package/addon/components/standard-template-plugin/template-card.js +7 -0
  51. package/addon/components/standard-template-plugin/template-provider.hbs +6 -0
  52. package/addon/components/standard-template-plugin/template-provider.js +75 -0
  53. package/addon/components/table-of-contents-plugin/card.hbs +5 -0
  54. package/addon/components/table-of-contents-plugin/card.js +53 -0
  55. package/addon/components/table-of-contents-plugin/inline-components/outline.hbs +21 -0
  56. package/addon/components/table-of-contents-plugin/inline-components/table-of-contents.hbs +9 -0
  57. package/addon/components/table-of-contents-plugin/inline-components/table-of-contents.js +100 -0
  58. package/addon/components/template-variable-plugin/template-variable-card.hbs +34 -0
  59. package/addon/components/template-variable-plugin/template-variable-card.js +230 -0
  60. package/addon/constants.js +24 -0
  61. package/{dist → addon}/helpers/limit-text.js +1 -4
  62. package/addon/initializers/plugin-initializer.js +75 -0
  63. package/addon/inline-components/table-of-contents.js +43 -0
  64. package/addon/marks/citaten-mark.js +32 -0
  65. package/addon/models/instruction.js +19 -0
  66. package/addon/models/measure.js +46 -0
  67. package/{dist → addon}/models/sign.js +12 -13
  68. package/addon/models/template.js +9 -0
  69. package/addon/plugins/besluit-type-plugin.js +16 -0
  70. package/addon/plugins/citaten-plugin.js +22 -0
  71. package/addon/plugins/generate-template-plugin.js +16 -0
  72. package/addon/plugins/import-snippet-plugin.js +16 -0
  73. package/addon/plugins/insert-variable-plugin.js +19 -0
  74. package/addon/plugins/rdfa-date-plugin.js +26 -0
  75. package/addon/plugins/roadsign-regulation-plugin.js +24 -0
  76. package/addon/plugins/standard-template-plugin.js +33 -0
  77. package/addon/plugins/table-of-contents-plugin.js +27 -0
  78. package/addon/plugins/template-variable-plugin.js +21 -0
  79. package/addon/serializers/application.js +8 -0
  80. package/{dist → addon}/services/import-rdfa-snippet.js +28 -18
  81. package/{dist → addon}/services/roadsign-registry.js +67 -121
  82. package/addon/services/standard-template-plugin.js +45 -0
  83. package/addon/utils/fetchBesluitTypes.js +90 -0
  84. package/addon/utils/generate-template-plugin/default-resources.js +9 -0
  85. package/addon/utils/instantiate-uuids.js +34 -0
  86. package/addon/utils/legislation-types.js +32 -0
  87. package/addon/utils/memoize.js +8 -0
  88. package/addon/utils/processMatch.js +80 -0
  89. package/addon/utils/roadsign-regulation-plugin/fetchData.js +78 -0
  90. package/addon/utils/roadsign-regulation-plugin/includeInstructions.js +10 -0
  91. package/addon/utils/variable-plugins/default-variable-types.js +56 -0
  92. package/{dist/fetch-data-284bf106.js → addon/utils/variable-plugins/fetch-data.js} +36 -18
  93. package/{dist/vlaamse-codex-187fe526.js → addon/utils/vlaamse-codex.js} +159 -63
  94. package/app/components/besluit-type-plugin/besluit-type-select.js +1 -0
  95. package/app/components/besluit-type-plugin/toolbar-dropdown.js +1 -0
  96. package/app/components/citaten-plugin/citaat-card.js +1 -0
  97. package/app/components/citaten-plugin/citaat-insert.js +1 -0
  98. package/app/components/citaten-plugin/citations/article-list.js +1 -0
  99. package/app/components/citaten-plugin/citations/article-preview.js +1 -0
  100. package/app/components/citaten-plugin/citations/decision-detail.js +1 -0
  101. package/app/components/citaten-plugin/citations/decision-list.js +1 -0
  102. package/app/components/citaten-plugin/citations/decision-preview.js +1 -0
  103. package/app/components/citaten-plugin/citations/modal.js +1 -0
  104. package/app/components/citaten-plugin/citations/search-modal.js +1 -0
  105. package/app/components/citaten-plugin/helpers/alert-load-error.js +1 -0
  106. package/app/components/citaten-plugin/helpers/alert-no-items.js +1 -0
  107. package/app/components/import-snippet-plugin/card.js +1 -0
  108. package/app/components/insert-variable-plugin/insert-variable-card.js +1 -0
  109. package/app/components/rdfa-date-plugin/rdfa-date-plugin-card.js +1 -0
  110. package/app/components/rdfa-date-plugin/rdfa-date-plugin-insert.js +1 -0
  111. package/app/components/rdfa-date-plugin/rdfa-date-time-picker.js +1 -0
  112. package/app/components/roadsign-regulation-plugin/expanded-measure.js +1 -0
  113. package/app/components/roadsign-regulation-plugin/measure-template.js +1 -0
  114. package/app/components/roadsign-regulation-plugin/roadsign-regulation-card.js +1 -0
  115. package/app/components/roadsign-regulation-plugin/roadsigns-modal.js +1 -0
  116. package/app/components/roadsign-regulation-plugin/roadsigns-pagination.js +1 -0
  117. package/app/components/roadsign-regulation-plugin/roadsigns-table.js +1 -0
  118. package/app/components/standard-template-plugin/template-card.js +1 -0
  119. package/app/components/standard-template-plugin/template-provider.js +1 -0
  120. package/app/components/table-of-contents-plugin/card.js +1 -0
  121. package/app/components/table-of-contents-plugin/inline-components/outline.js +1 -0
  122. package/app/components/table-of-contents-plugin/inline-components/table-of-contents.js +1 -0
  123. package/app/components/template-variable-plugin/template-variable-card.js +1 -0
  124. package/app/helpers/limit-text.js +4 -0
  125. package/app/initializers/plugin-initializer.js +4 -0
  126. package/app/models/instruction.js +1 -0
  127. package/app/models/measure.js +1 -0
  128. package/app/models/sign.js +1 -0
  129. package/app/models/template.js +1 -0
  130. package/app/serializers/application.js +1 -0
  131. package/app/services/import-rdfa-snippet.js +1 -0
  132. package/app/services/rdfa-editor-roadsign-regulation-plugin.js +1 -0
  133. package/app/services/roadsign-registry.js +1 -0
  134. package/app/services/standard-template-plugin.js +1 -0
  135. package/{dist → app}/styles/citaten-plugin.scss +0 -0
  136. package/config/environment.js +0 -1
  137. package/index.js +8 -0
  138. package/package.json +110 -106
  139. package/tsconfig.json +44 -0
  140. package/types/dummy/index.d.ts +1 -0
  141. package/types/ember-data/types/registries/model.d.ts +6 -0
  142. package/types/global.d.ts +7 -0
  143. package/addon-main.js +0 -4
  144. package/dist/_app_/components/besluit-type-plugin/besluit-type-select.js +0 -1
  145. package/dist/_app_/components/besluit-type-plugin/toolbar-dropdown.js +0 -1
  146. package/dist/_app_/components/citaten-plugin/citaat-card.js +0 -1
  147. package/dist/_app_/components/citaten-plugin/citaat-insert.js +0 -1
  148. package/dist/_app_/components/citaten-plugin/citations/article-list.js +0 -1
  149. package/dist/_app_/components/citaten-plugin/citations/article-preview.js +0 -1
  150. package/dist/_app_/components/citaten-plugin/citations/decision-detail.js +0 -1
  151. package/dist/_app_/components/citaten-plugin/citations/decision-list.js +0 -1
  152. package/dist/_app_/components/citaten-plugin/citations/decision-preview.js +0 -1
  153. package/dist/_app_/components/citaten-plugin/citations/search-modal.js +0 -1
  154. package/dist/_app_/components/citaten-plugin/helpers/alert-load-error.js +0 -1
  155. package/dist/_app_/components/citaten-plugin/helpers/alert-no-items.js +0 -1
  156. package/dist/_app_/components/import-snippet-plugin/card.js +0 -1
  157. package/dist/_app_/components/insert-variable-plugin/insert-variable-card.js +0 -1
  158. package/dist/_app_/components/rdfa-date-plugin/rdfa-date-plugin-card.js +0 -1
  159. package/dist/_app_/components/rdfa-date-plugin/rdfa-date-plugin-insert.js +0 -1
  160. package/dist/_app_/components/rdfa-date-plugin/rdfa-date-time-picker.js +0 -1
  161. package/dist/_app_/components/roadsign-regulation-plugin/expanded-measure.js +0 -1
  162. package/dist/_app_/components/roadsign-regulation-plugin/measure-template.js +0 -1
  163. package/dist/_app_/components/roadsign-regulation-plugin/roadsign-regulation-card.js +0 -1
  164. package/dist/_app_/components/roadsign-regulation-plugin/roadsigns-modal.js +0 -1
  165. package/dist/_app_/components/roadsign-regulation-plugin/roadsigns-pagination.js +0 -1
  166. package/dist/_app_/components/roadsign-regulation-plugin/roadsigns-table.js +0 -1
  167. package/dist/_app_/components/standard-template-plugin/template-card.js +0 -1
  168. package/dist/_app_/components/standard-template-plugin/template-provider.js +0 -1
  169. package/dist/_app_/components/table-of-contents-plugin/card.js +0 -1
  170. package/dist/_app_/components/table-of-contents-plugin/inline-components/outline.js +0 -1
  171. package/dist/_app_/components/table-of-contents-plugin/inline-components/table-of-contents.js +0 -1
  172. package/dist/_app_/components/template-variable-plugin/template-variable-card.js +0 -1
  173. package/dist/_app_/helpers/limit-text.js +0 -1
  174. package/dist/_app_/initializers/plugin-initializer.js +0 -1
  175. package/dist/_app_/models/instruction.js +0 -1
  176. package/dist/_app_/models/measure.js +0 -1
  177. package/dist/_app_/models/sign.js +0 -1
  178. package/dist/_app_/models/template.js +0 -1
  179. package/dist/_app_/serializers/application.js +0 -1
  180. package/dist/_app_/services/import-rdfa-snippet.js +0 -1
  181. package/dist/_app_/services/roadsign-registry.js +0 -1
  182. package/dist/_app_/services/standard-template-plugin.js +0 -1
  183. package/dist/_rollupPluginBabelHelpers-537ad14b.js +0 -48
  184. package/dist/_rollupPluginBabelHelpers-537ad14b.js.map +0 -1
  185. package/dist/components/besluit-type-plugin/besluit-type-select.js +0 -30
  186. package/dist/components/besluit-type-plugin/besluit-type-select.js.map +0 -1
  187. package/dist/components/besluit-type-plugin/toolbar-dropdown.js +0 -292
  188. package/dist/components/besluit-type-plugin/toolbar-dropdown.js.map +0 -1
  189. package/dist/components/citaten-plugin/citaat-card.js +0 -367
  190. package/dist/components/citaten-plugin/citaat-card.js.map +0 -1
  191. package/dist/components/citaten-plugin/citaat-insert.js +0 -84
  192. package/dist/components/citaten-plugin/citaat-insert.js.map +0 -1
  193. package/dist/components/citaten-plugin/citations/article-list.js +0 -10
  194. package/dist/components/citaten-plugin/citations/article-list.js.map +0 -1
  195. package/dist/components/citaten-plugin/citations/article-preview.js +0 -10
  196. package/dist/components/citaten-plugin/citations/article-preview.js.map +0 -1
  197. package/dist/components/citaten-plugin/citations/decision-detail.js +0 -123
  198. package/dist/components/citaten-plugin/citations/decision-detail.js.map +0 -1
  199. package/dist/components/citaten-plugin/citations/decision-list.js +0 -10
  200. package/dist/components/citaten-plugin/citations/decision-list.js.map +0 -1
  201. package/dist/components/citaten-plugin/citations/decision-preview.js +0 -10
  202. package/dist/components/citaten-plugin/citations/decision-preview.js.map +0 -1
  203. package/dist/components/citaten-plugin/citations/search-modal.js +0 -282
  204. package/dist/components/citaten-plugin/citations/search-modal.js.map +0 -1
  205. package/dist/components/citaten-plugin/helpers/alert-load-error.js +0 -10
  206. package/dist/components/citaten-plugin/helpers/alert-load-error.js.map +0 -1
  207. package/dist/components/citaten-plugin/helpers/alert-no-items.js +0 -10
  208. package/dist/components/citaten-plugin/helpers/alert-no-items.js.map +0 -1
  209. package/dist/components/import-snippet-plugin/card.js +0 -99
  210. package/dist/components/import-snippet-plugin/card.js.map +0 -1
  211. package/dist/components/insert-variable-plugin/insert-variable-card.js +0 -190
  212. package/dist/components/insert-variable-plugin/insert-variable-card.js.map +0 -1
  213. package/dist/components/rdfa-date-plugin/rdfa-date-plugin-card.js +0 -85
  214. package/dist/components/rdfa-date-plugin/rdfa-date-plugin-card.js.map +0 -1
  215. package/dist/components/rdfa-date-plugin/rdfa-date-plugin-insert.js +0 -23
  216. package/dist/components/rdfa-date-plugin/rdfa-date-plugin-insert.js.map +0 -1
  217. package/dist/components/rdfa-date-plugin/rdfa-date-time-picker.js +0 -91
  218. package/dist/components/rdfa-date-plugin/rdfa-date-time-picker.js.map +0 -1
  219. package/dist/components/roadsign-regulation-plugin/expanded-measure.js +0 -52
  220. package/dist/components/roadsign-regulation-plugin/expanded-measure.js.map +0 -1
  221. package/dist/components/roadsign-regulation-plugin/measure-template.js +0 -42
  222. package/dist/components/roadsign-regulation-plugin/measure-template.js.map +0 -1
  223. package/dist/components/roadsign-regulation-plugin/roadsign-regulation-card.js +0 -82
  224. package/dist/components/roadsign-regulation-plugin/roadsign-regulation-card.js.map +0 -1
  225. package/dist/components/roadsign-regulation-plugin/roadsigns-modal.js +0 -251
  226. package/dist/components/roadsign-regulation-plugin/roadsigns-modal.js.map +0 -1
  227. package/dist/components/roadsign-regulation-plugin/roadsigns-pagination.js +0 -71
  228. package/dist/components/roadsign-regulation-plugin/roadsigns-pagination.js.map +0 -1
  229. package/dist/components/roadsign-regulation-plugin/roadsigns-table.js +0 -36
  230. package/dist/components/roadsign-regulation-plugin/roadsigns-table.js.map +0 -1
  231. package/dist/components/standard-template-plugin/template-card.js +0 -15
  232. package/dist/components/standard-template-plugin/template-card.js.map +0 -1
  233. package/dist/components/standard-template-plugin/template-provider.js +0 -109
  234. package/dist/components/standard-template-plugin/template-provider.js.map +0 -1
  235. package/dist/components/table-of-contents-plugin/card.js +0 -52
  236. package/dist/components/table-of-contents-plugin/card.js.map +0 -1
  237. package/dist/components/table-of-contents-plugin/inline-components/outline.js +0 -10
  238. package/dist/components/table-of-contents-plugin/inline-components/outline.js.map +0 -1
  239. package/dist/components/table-of-contents-plugin/inline-components/table-of-contents.js +0 -92
  240. package/dist/components/table-of-contents-plugin/inline-components/table-of-contents.js.map +0 -1
  241. package/dist/components/template-variable-plugin/template-variable-card.js +0 -178
  242. package/dist/components/template-variable-plugin/template-variable-card.js.map +0 -1
  243. package/dist/constants-c77fd478.js +0 -16
  244. package/dist/constants-c77fd478.js.map +0 -1
  245. package/dist/fetch-data-284bf106.js.map +0 -1
  246. package/dist/helpers/limit-text.js.map +0 -1
  247. package/dist/includeInstructions-a32c24e6.js +0 -10
  248. package/dist/includeInstructions-a32c24e6.js.map +0 -1
  249. package/dist/index.js +0 -2
  250. package/dist/index.js.map +0 -1
  251. package/dist/initializers/plugin-initializer.js +0 -401
  252. package/dist/initializers/plugin-initializer.js.map +0 -1
  253. package/dist/legislation-types-4d487cb9.js +0 -26
  254. package/dist/legislation-types-4d487cb9.js.map +0 -1
  255. package/dist/models/instruction.js +0 -18
  256. package/dist/models/instruction.js.map +0 -1
  257. package/dist/models/measure.js +0 -41
  258. package/dist/models/measure.js.map +0 -1
  259. package/dist/models/sign.js.map +0 -1
  260. package/dist/models/template.js +0 -48
  261. package/dist/models/template.js.map +0 -1
  262. package/dist/serializers/application.js +0 -10
  263. package/dist/serializers/application.js.map +0 -1
  264. package/dist/services/import-rdfa-snippet.js.map +0 -1
  265. package/dist/services/roadsign-registry.js.map +0 -1
  266. package/dist/services/standard-template-plugin.js +0 -53
  267. package/dist/services/standard-template-plugin.js.map +0 -1
  268. package/dist/styles/test.css +0 -0
  269. package/dist/vlaamse-codex-187fe526.js.map +0 -1
@@ -0,0 +1,76 @@
1
+ {{#if this.showCard}}
2
+ <AuCard @flex={{true}} @divided={{true}} @isOpenInitially={{true}} @expandable={{true}} @shadow={{true}} @size="small" as |c|>
3
+ <c.header>
4
+ <AuHeading @level="3" @skin="6">{{t "citaten-plugin.card.title"}}</AuHeading>
5
+ </c.header>
6
+ <c.content>
7
+ <div class="au-o-flow au-o-flow--small">
8
+ <AuFormRow>
9
+ <AuLabel for="typeofdocument" @inline={{false}} @required={{false}} @error={{false}} @warning={{false}}>{{t "citaten-plugin.search.type"}}</AuLabel>
10
+ <PowerSelect
11
+ id="typeofdocument"
12
+ @allowClear={{false}}
13
+ @disabled={{false}}
14
+ @searchEnabled={{true}}
15
+ @loadingMessage="{{t "citaten-plugin.alert.loading"}}"
16
+ @noMatchesMessage="{{t "citaten-plugin.alert.no-results"}}"
17
+ @searchMessage="{{t "citaten-plugin.search.placeholder"}}"
18
+ @options={{this.legislationTypes}}
19
+ @selected={{this.legislationSelected}}
20
+ @onChange={{this.selectLegislationType}}
21
+ class="au-u-1-1"
22
+ as |type|>
23
+ {{type}}
24
+ </PowerSelect>
25
+ </AuFormRow>
26
+ <AuFormRow>
27
+ <AuLabel for="searchterm" @inline={{false}} @required={{false}} @error={{false}} @warning={{false}}>{{t "citaten-plugin.search.term"}}</AuLabel>
28
+ <AuInput
29
+ id="searchterm"
30
+ @type="text"
31
+ @width="block"
32
+ @icon="search"
33
+ @iconAlignment="right"
34
+ @value={{this.text}}
35
+ placeholder={{t "citaten-plugin.search.placeholder"}}
36
+ {{on "input" (perform this.updateSearch)}}/>
37
+ </AuFormRow>
38
+ <AuFormRow>
39
+ <AuButton @skin="link" @icon="search" @iconAlignment="left" {{on "click" this.openSearchModal}}>{{t "citaten-plugin.search.advanced"}}</AuButton>
40
+ </AuFormRow>
41
+ </div>
42
+ </c.content>
43
+ <c.footer class="au-u-background-gray-100 au-u-padding-none">
44
+ {{#if this.decisionResource.isRunning}}
45
+ <AuLoader /><span class="au-u-hidden-visually">{{t "citaten-plugin.alert.loading"}}</span>
46
+ {{else}}
47
+ {{#if this.error}}
48
+ <CitatenPlugin::Helpers::AlertLoadError @fullSize={{false}} @error={{this.error}} />
49
+ {{else}}
50
+ <AuToolbar
51
+ @border="bottom"
52
+ @skin="none"
53
+ class="au-u-padding-small" >
54
+ <AuHeading @skin="6" @level="4">{{t "citaten-plugin.card.suggestions"}}</AuHeading>
55
+ </AuToolbar>
56
+ <div class="citaten--decision-list au-u-margin-none au-u-padding-top-tiny au-u-padding-bottom-tiny">
57
+ <CitatenPlugin::Citations::DecisionList
58
+ @decisions={{this.decisionResource.value}}
59
+ @onCitationInsert={{this.insertDecisionCitation}}
60
+ @onCitationDetails={{this.openDecisionDetailModal}} />
61
+ </div>
62
+ {{/if}}
63
+ {{/if}}
64
+ </c.footer>
65
+ </AuCard>
66
+ {{/if}}
67
+
68
+ {{#if this.showModal}}
69
+ <CitatenPlugin::Citations::SearchModal
70
+ @closeModal={{this.closeModal}}
71
+ @insertDecisionCitation={{this.insertDecisionCitation}}
72
+ @insertArticleCitation={{this.insertArticleCitation}}
73
+ @selectedDecision={{this.decision}}
74
+ @legislationTypeUri={{this.legislationTypeUri}}
75
+ @text={{this.text}} />
76
+ {{/if}}
@@ -0,0 +1,286 @@
1
+ import Component from '@glimmer/component';
2
+ import { tracked } from '@glimmer/tracking';
3
+ // import { task } from 'ember-concurrency-decorators';
4
+ import { task, timeout } from 'ember-concurrency';
5
+ import { action } from '@ember/object';
6
+ import { capitalize } from '@ember/string';
7
+ import processMatch from '../../utils/processMatch';
8
+ import { cleanCaches, fetchDecisions } from '../../utils/vlaamse-codex';
9
+ import {
10
+ LEGISLATION_TYPE_CONCEPTS,
11
+ LEGISLATION_TYPES,
12
+ } from '../../utils/legislation-types';
13
+ import { task as trackedTask } from 'ember-resources/util/ember-concurrency';
14
+
15
+ const BASIC_MULTIPLANE_CHARACTER = '\u0021-\uFFFF'; // most of the characters used around the world
16
+
17
+ // Regex nicely structured:
18
+ // (
19
+ // (
20
+ // \w*decreet |
21
+ // omzendbrief |
22
+ // verdrag |
23
+ // grondwetswijziging |
24
+ // samenwerkingsakkoord |
25
+ // \w*wetboek |
26
+ // protocol |
27
+ // besluit[^\S\n]van[^\S\n]de[^\S\n]vlaamse[^\S\n]regering |
28
+ // geco[öo]rdineerde wetten |
29
+ // \w*wet |
30
+ // koninklijk[^\S\n]?besluit |
31
+ // ministerieel[^\S\n]?besluit |
32
+ // genummerd[^\S\n]?koninklijk[^\S\n]?besluit
33
+ // )
34
+ // [^\S\n]*
35
+ // (
36
+ // ([^\S\n] | [\u0021-\uFFFF\d;:'"()&\-_]){3,}
37
+ // )?
38
+ // )
39
+ const NNWS = '[^\\S\\n]';
40
+ const CITATION_REGEX = new RegExp(
41
+ `((\\w*decreet|omzendbrief|verdrag|grondwetswijziging|samenwerkingsakkoord|\\w*wetboek|protocol|besluit${NNWS}van${NNWS}de${NNWS}vlaamse${NNWS}regering|geco[öo]rdineerde${NNWS}wetten|\\w*wet|koninklijk${NNWS}?besluit|ministerieel${NNWS}?besluit|genummerd${NNWS}?koninklijk${NNWS}?besluit|\\w*${NNWS}?besluit)${NNWS}*((${NNWS}|[${BASIC_MULTIPLANE_CHARACTER};:'"()&-_]){3,})?)`,
42
+ 'uig'
43
+ );
44
+
45
+ export default class CitaatCardComponent extends Component {
46
+ @tracked pageNumber = 0;
47
+ @tracked pageSize = 5;
48
+ @tracked totalSize;
49
+ @tracked decisions = [];
50
+ @tracked error;
51
+ @tracked showModal = false;
52
+ @tracked showCard = false;
53
+ @tracked decision;
54
+ @tracked legislationTypeUri;
55
+ @tracked legislationTypeUriAfterTimeout;
56
+ @tracked text;
57
+ @tracked textAfterTimeout;
58
+ @tracked markSelected;
59
+ liveHighlights;
60
+
61
+ constructor() {
62
+ super(...arguments);
63
+ this.liveHighlights = this.args.controller.createLiveMarkSet({
64
+ datastoreQuery: (datastore) => {
65
+ const matches = datastore
66
+ .match(null, '>http://data.vlaanderen.be/ns/besluit#motivering')
67
+ .searchTextIn('predicate', CITATION_REGEX);
68
+ const resultMatches = matches.filter((match) => {
69
+ return (
70
+ datastore
71
+ .limitToRange(match.range, {
72
+ type: 'rangeTouches',
73
+ includeEndTags: true,
74
+ })
75
+ .match(null, '>http://data.europa.eu/eli/ontology#cites').size ===
76
+ 0
77
+ );
78
+ });
79
+ resultMatches.forEach((match) => (match.range = match.groupRanges[1]));
80
+ return resultMatches;
81
+ },
82
+
83
+ liveMarkSpecs: [
84
+ {
85
+ name: 'citaten',
86
+ attributesBuilder: (textMatch) => {
87
+ const result = processMatch(textMatch);
88
+ return {
89
+ setBy: 'citaten-plugin',
90
+ text: result.text,
91
+ legislationTypeUri: result.legislationTypeUri,
92
+ };
93
+ },
94
+ },
95
+ 'highlighted',
96
+ ],
97
+ });
98
+ this.controller.onEvent(
99
+ 'selectionChanged',
100
+ this.onSelectionChanged.bind(this)
101
+ );
102
+ }
103
+
104
+ onSelectionChanged() {
105
+ const range = this.controller.selection.lastRange;
106
+ if (!range) {
107
+ return;
108
+ }
109
+ const marks = range.getMarks();
110
+ let selectionMark;
111
+ for (let mark of marks) {
112
+ if (mark.name === 'citaten') {
113
+ selectionMark = mark;
114
+ break;
115
+ }
116
+ }
117
+ if (selectionMark) {
118
+ if (this.showCard) {
119
+ //Card was already shown, update search condition and trigger search debounced
120
+ this.text = selectionMark.attributes.text;
121
+ this.legislationTypeUri = selectionMark.attributes.legislationTypeUri;
122
+ this.markSelected = selectionMark;
123
+ this.updateSearch.perform();
124
+ } else {
125
+ //When card is renderend first time, the resource will automatically trigger, no updateSearch is needed, but make sure to first set the search conditions, before showing the card.
126
+ //When not first time, but reopened, search terms could not have changed yet, so also no updateSearch needed
127
+ this.text = selectionMark.attributes.text;
128
+ this.legislationTypeUri = selectionMark.attributes.legislationTypeUri;
129
+ if (
130
+ this.legislationTypeUriAfterTimeout &&
131
+ (this.legislationTypeUri !== this.legislationTypeUriAfterTimeout ||
132
+ this.text !== this.textAfterTimeout)
133
+ ) {
134
+ //Convoluted, but this is when you switch from one reference insertion to another
135
+ this.updateSearchImmediate.perform();
136
+ }
137
+ this.markSelected = selectionMark;
138
+ this.showCard = true;
139
+ }
140
+ } else {
141
+ this.showCard = false;
142
+ //Would be nice, but this triggers way to often causing the cancellation of useful requests
143
+ //this.decisionResource.cancel();
144
+ }
145
+ }
146
+
147
+ get controller() {
148
+ return this.args.controller;
149
+ }
150
+
151
+ get legislationTypes() {
152
+ return Object.keys(LEGISLATION_TYPES).map(capitalize);
153
+ }
154
+
155
+ get legislationSelected() {
156
+ const found = LEGISLATION_TYPE_CONCEPTS.find(
157
+ (c) => c.value === this.legislationTypeUri
158
+ );
159
+ return capitalize(found ? found.label : LEGISLATION_TYPE_CONCEPTS[0].label);
160
+ }
161
+
162
+ decisionResource = trackedTask(this, this.resourceSearch, () => [
163
+ this.textAfterTimeout,
164
+ this.legislationTypeUriAfterTimeout,
165
+ this.pageNumber,
166
+ this.pageSize,
167
+ ]);
168
+
169
+ @task({ restartable: true })
170
+ *updateSearch() {
171
+ yield timeout(500);
172
+ this.textAfterTimeout = this.text;
173
+ this.legislationTypeUriAfterTimeout = this.legislationTypeUri;
174
+ }
175
+
176
+ @task({ restartable: true })
177
+ *updateSearchImmediate() {
178
+ this.textAfterTimeout = this.text;
179
+ this.legislationTypeUriAfterTimeout = this.legislationTypeUri;
180
+ yield;
181
+ }
182
+
183
+ @task({ restartable: true })
184
+ *resourceSearch() {
185
+ this.error = null;
186
+ yield undefined; //To prevent other variables used below (this.text and this.legislationTypeUri) to trigger a retrigger.
187
+ const abortController = new AbortController();
188
+ const signal = abortController.signal;
189
+ try {
190
+ // Split search string by grouping on non-whitespace characters
191
+ // This probably needs to be more complex to search on group of words
192
+ const words =
193
+ (this.textAfterTimeout || this.text || '').match(/\S+/g) || [];
194
+ const filter = {
195
+ type: this.legislationTypeUriAfterTimeout || this.legislationTypeUri,
196
+ };
197
+ const results = yield fetchDecisions(
198
+ words,
199
+ filter,
200
+ this.pageNumber,
201
+ this.pageSize,
202
+ signal
203
+ );
204
+ this.totalCount = results.totalCount;
205
+ return results.decisions;
206
+ } catch (e) {
207
+ console.warn(e); // eslint-ignore-line no-console
208
+ this.totalCount = 0;
209
+ this.error = e;
210
+ return [];
211
+ } finally {
212
+ //Abort all requests now that this task has either successfully finished or has been cancelled
213
+ abortController.abort();
214
+ }
215
+ }
216
+
217
+ @action
218
+ selectLegislationType(type) {
219
+ type = type.toLowerCase();
220
+ const found = LEGISLATION_TYPE_CONCEPTS.find(
221
+ (c) => c.label.toLowerCase() === type
222
+ );
223
+ this.legislationTypeUri = found
224
+ ? found.value
225
+ : LEGISLATION_TYPE_CONCEPTS[0].value;
226
+ this.legislationTypeUriAfterTimeout = this.legislationTypeUri;
227
+ }
228
+
229
+ @action
230
+ openDecisionDetailModal(decision) {
231
+ this.decision = decision;
232
+ this.showModal = true;
233
+ }
234
+
235
+ @action
236
+ openSearchModal() {
237
+ this.decisionResource.cancel();
238
+ this.decision = null;
239
+ this.showModal = true;
240
+ }
241
+
242
+ @action
243
+ closeModal(lastSearchType, lastSearchTerm) {
244
+ this.showModal = false;
245
+ this.decision = null;
246
+ if (lastSearchType) this.legislationTypeUri = lastSearchType;
247
+ if (lastSearchTerm) this.text = lastSearchTerm;
248
+ if (lastSearchType || lastSearchTerm) this.updateSearchImmediate.perform();
249
+ }
250
+
251
+ @action
252
+ insertDecisionCitation(decision) {
253
+ const type = decision.legislationType.label;
254
+ const uri = decision.uri;
255
+ const title = decision.title;
256
+ const range = this.controller.rangeFactory.fromAroundNode(
257
+ this.markSelected.node
258
+ );
259
+ const citationHtml = `${
260
+ type ? type : ''
261
+ } <a class="annotation" href="${uri}" property="eli:cites" typeof="eli:LegalExpression">${title}</a>&nbsp;`;
262
+ this.controller.executeCommand('insert-html', citationHtml, range);
263
+ }
264
+
265
+ @action
266
+ insertArticleCitation(decision, article) {
267
+ const type = decision.legislationType.label;
268
+ const uri = article.uri;
269
+ const title = `${decision.title}, ${article.number}`;
270
+ const range = this.controller.rangeFactory.fromAroundNode(
271
+ this.markSelected.node
272
+ );
273
+ const citationHtml = `${
274
+ type ? type : ''
275
+ } <a class="annotation" href="${uri}" property="eli:cites" typeof="eli:LegalExpression">${title}</a>&nbsp;`;
276
+ this.controller.executeCommand('insert-html', citationHtml, range);
277
+ }
278
+
279
+ async willDestroy() {
280
+ // Not necessary as ember-concurrency does this for us.
281
+ // this.decisionResource.cancel();
282
+ cleanCaches();
283
+ this.liveHighlights.destroy();
284
+ super.willDestroy();
285
+ }
286
+ }
@@ -0,0 +1,20 @@
1
+ <AuList::Item>
2
+ <AuButton
3
+ @icon="add"
4
+ @iconAlignment="left"
5
+ @skin="link"
6
+ @disabled={{this.disableInsert}}
7
+ {{on 'click' this.openModal}}>
8
+ {{t "citaten-plugin.insert.title"}}
9
+ </AuButton>
10
+ </AuList::Item>
11
+
12
+ {{#if this.showModal}}
13
+ <CitatenPlugin::Citations::SearchModal
14
+ @closeModal={{this.closeModal}}
15
+ @insertDecisionCitation={{this.insertDecisionCitation}}
16
+ @insertArticleCitation={{this.insertArticleCitation}}
17
+ @legislationTypeUri={{this.legislationTypeUri}}
18
+ @text={{this.text}}
19
+ />
20
+ {{/if}}
@@ -0,0 +1,68 @@
1
+ import Component from '@glimmer/component';
2
+ import { action } from '@ember/object';
3
+ import { tracked } from '@glimmer/tracking';
4
+ import { LEGISLATION_TYPES } from '../../utils/legislation-types';
5
+ export default class EditorPluginsCitaatInsertComponent extends Component {
6
+ @tracked disableInsert = false;
7
+ @tracked showModal = false;
8
+ @tracked legislationTypeUri = LEGISLATION_TYPES.decreet;
9
+ @tracked text = '';
10
+
11
+ constructor() {
12
+ super(...arguments);
13
+ this.args.controller.onEvent(
14
+ 'selectionChanged',
15
+ this.onSelectionChanged.bind(this)
16
+ );
17
+ }
18
+
19
+ onSelectionChanged() {
20
+ const selectedRange = this.args.controller.selection.lastRange;
21
+ if (!selectedRange) {
22
+ return;
23
+ }
24
+ const limitedDatastore = this.args.controller.datastore.limitToRange(
25
+ selectedRange,
26
+ 'rangeIsInside'
27
+ );
28
+ const motivering = limitedDatastore
29
+ .match(null, '>http://data.vlaanderen.be/ns/besluit#motivering')
30
+ .asQuads()
31
+ .next().value;
32
+ this.disableInsert = motivering ? false : true;
33
+ }
34
+
35
+ @action
36
+ openModal() {
37
+ this.showModal = true;
38
+ }
39
+
40
+ @action
41
+ closeModal() {
42
+ this.showModal = false;
43
+ }
44
+
45
+ @action
46
+ insertDecisionCitation(decision) {
47
+ const type = decision.legislationType.label;
48
+ const uri = decision.uri;
49
+ const title = decision.title;
50
+ const range = this.args.controller.selection.lastRange;
51
+ const citationHtml = `${
52
+ type ? type : ''
53
+ } <a class="annotation" href="${uri}" property="eli:cites" typeof="eli:LegalExpression">${title}</a>&nbsp;`;
54
+ this.args.controller.executeCommand('insert-html', citationHtml, range);
55
+ }
56
+
57
+ @action
58
+ insertArticleCitation(decision, article) {
59
+ const type = decision.legislationType.label;
60
+ const uri = article.uri;
61
+ const title = `${decision.title}, ${article.number}`;
62
+ const range = this.args.controller.selection.lastRange;
63
+ const citationHtml = `${
64
+ type ? type : ''
65
+ } <a class="annotation" href="${uri}" property="eli:cites" typeof="eli:LegalExpression">${title}</a>&nbsp;`;
66
+ this.args.controller.executeCommand('insert-html', citationHtml, range);
67
+ }
68
+ }
@@ -0,0 +1,13 @@
1
+ {{#if @articles.length}}
2
+ <AuList @direction="vertical" @divider={{true}}>
3
+ {{#each @articles as |article|}}
4
+ <AuList::Item>
5
+ <CitatenPlugin::Citations::ArticlePreview
6
+ @insertArticleCitation={{fn @insertArticleCitation article}}
7
+ @article={{article}} />
8
+ </AuList::Item>
9
+ {{/each}}
10
+ </AuList>
11
+ {{else}}
12
+ <CitatenPlugin::Helpers::AlertNoItems @fullSize={{@fullSize}} class={{if @fullSize "au-u-margin"}}/>
13
+ {{/if}}
@@ -0,0 +1,16 @@
1
+ <div class="au-u-margin" ...attributes>
2
+ <AuHeading @level="3" @skin="5" class="au-u-margin-bottom-small">
3
+ {{@article.number}} ({{@article.dateInForce}} - {{if @article.dateNoLongerInForce @article.dateNoLongerInForce "…"}})
4
+ </AuHeading>
5
+ <p>
6
+ {{@article.content}}
7
+ </p>
8
+ <AuButton
9
+ @skin="primary"
10
+ @icon="plus-text"
11
+ @iconAlignment="left"
12
+ class="au-u-margin-top-small"
13
+ {{on "click" @insertArticleCitation}}>
14
+ {{t "citaten-plugin.references.refer-article"}}
15
+ </AuButton>
16
+ </div>
@@ -0,0 +1,110 @@
1
+ <div class="citaten--main-container au-u-flex au-u-flex--column au-u-flex--between">
2
+ <div class="citaten--container-content">
3
+ {{! Heading with title and actions }}
4
+ <div class="citaten--container-header au-u-background-gray-100 au-o-box">
5
+ <AuHeading @level="2" @skin="4">
6
+ {{capitalize @decision.fullTitle}}
7
+ </AuHeading>
8
+ {{#if @decision.publicationDate}}
9
+ <AuPill
10
+ class="au-u-margin-top-small au-u-margin-bottom-small"
11
+ @skin="border"
12
+ @icon="calendar"
13
+ @iconAlignment="left">
14
+ {{@decision.publicationDate}}
15
+ </AuPill>
16
+ {{/if}}
17
+ <br />
18
+ <AuButton
19
+ @skin="primary"
20
+ @icon="plus-text"
21
+ @iconAlignment="left"
22
+ class="au-u-margin-right"
23
+ {{on "click" (fn @insertDecisionCitation @decision)}}>
24
+ {{t "citaten-plugin.references.refer-whole"}}
25
+ </AuButton>
26
+ <AuLinkExternal
27
+ @skin="primary"
28
+ @icon="link-external"
29
+ @iconAlignment="left"
30
+ href={{@decision.uri}}
31
+ rel="noreferrer noopener">
32
+ {{t "citaten-plugin.references.lookup-codex"}}
33
+ </AuLinkExternal>
34
+ {{! Search field }}
35
+ <AuLabel class="au-u-margin-top-small au-margin-bottom-small" for="searchCodex" @inline={{true}} @required={{false}} @error={{false}} @warning={{false}}>{{t "citaten-plugin.search.article"}}</AuLabel>
36
+ <AuInput id="searchCodex" placeholder="{{t "citaten-plugin.search.article-placeholder"}}" @type="text" @width="default" @icon="search" @iconAlignment="right" @value={{this.articleFilter}} {{on "input" (perform this.updateArticleFilter)}}/>
37
+ </div>
38
+ {{! Search results }}
39
+ <div>
40
+ {{#if this.articleResource.isRunning}}
41
+ <div class="au-o-box">
42
+ <AuLoader @padding="large" />
43
+ <span class="au-u-hidden-visually">{{t "citaten-plugin.alert.loading"}}</span>
44
+ </div>
45
+ {{else}}
46
+ {{#if this.error}}
47
+ <CitatenPlugin::Helpers::AlertLoadError @fullSize={{true}} @error={{this.error}} class="au-o-box" />
48
+ {{else}}
49
+ <CitatenPlugin::Citations::ArticleList
50
+ @insertArticleCitation={{fn @insertArticleCitation @decision}}
51
+ @articles={{this.articleResource.value}}
52
+ @fullSize={{true}}/>
53
+ {{/if}}
54
+ {{/if}}
55
+ </div>
56
+ </div>
57
+ {{! Footer }}
58
+ <div>
59
+ <div class="citaten--container-footer au-u-background-gray-100">
60
+ <AuToolbar
61
+ @border="top"
62
+ @size="large"
63
+ @nowrap={{true}}>
64
+ <div class="au-c-pagination">
65
+ {{! Always shown, back to main dialog }}
66
+ <div class="au-c-pagination__list-item">
67
+ <AuButton
68
+ @skin="link"
69
+ @icon="hierarchical-back"
70
+ @iconAlignment="left"
71
+ {{on "click" this.close}}>
72
+ {{t "citaten-plugin.search.back"}}
73
+ </AuButton>
74
+ </div>
75
+ {{! Pagination of articles }}
76
+ {{#if this.articleResource.value.length}}
77
+ <p>
78
+ <span class="au-u-hidden-visually">{{t "citaten-plugin.search.results"}}</span>
79
+ <strong>
80
+ {{this.rangeStart}} - {{this.rangeEnd}}
81
+ </strong>
82
+ {{t "citaten-plugin.pagination.of"}}
83
+ {{this.totalCount}}
84
+ </p>
85
+ <div class="au-c-pagination__list-item">
86
+ {{#unless this.isFirstPage}}
87
+ <AuButton
88
+ @skin="link"
89
+ @icon="nav-left"
90
+ @iconAlignment="left"
91
+ {{on "click" this.previousPage}}>
92
+ {{t "citaten-plugin.pagination.previous"}}
93
+ </AuButton>
94
+ {{/unless}}
95
+ {{#unless this.isLastPage}}
96
+ <AuButton
97
+ @skin="link"
98
+ @icon="nav-right"
99
+ @iconAlignment="right"
100
+ {{on "click" this.nextPage}}>
101
+ {{t "citaten-plugin.pagination.next"}}
102
+ </AuButton>
103
+ {{/unless}}
104
+ </div>
105
+ {{/if}}
106
+ </div>
107
+ </AuToolbar>
108
+ </div>
109
+ </div>
110
+ </div>
@@ -0,0 +1,94 @@
1
+ import Component from '@glimmer/component';
2
+ import { timeout } from 'ember-concurrency';
3
+ import { tracked } from '@glimmer/tracking';
4
+ import { action } from '@ember/object';
5
+ import { fetchArticles } from '../../../utils/vlaamse-codex';
6
+ import { task } from 'ember-concurrency';
7
+ import { task as trackedTask } from 'ember-resources/util/ember-concurrency';
8
+
9
+ export default class EditorPluginsCitationsDecisionDetailComponent extends Component {
10
+ @tracked error;
11
+ @tracked pageNumber = 0;
12
+ @tracked pageSize = 5;
13
+ @tracked totalCount;
14
+ @tracked articles = [];
15
+ @tracked articleFilter;
16
+ @tracked articleFilterAfterTimeout;
17
+
18
+ constructor() {
19
+ super(...arguments);
20
+ }
21
+
22
+ @task({ restartable: true })
23
+ *updateArticleFilter() {
24
+ yield timeout(500);
25
+ this.pageNumber = 0;
26
+ this.articleFilterAfterTimeout = this.articleFilter;
27
+ }
28
+
29
+ articleResource = trackedTask(this, this.resourceSearch, () => [
30
+ this.pageNumber,
31
+ this.pageSize,
32
+ this.articleFilterAfterTimeout,
33
+ ]);
34
+
35
+ @task({ restartable: true })
36
+ *resourceSearch() {
37
+ this.error = null;
38
+ const abortController = new AbortController();
39
+ const signal = abortController.signal;
40
+ try {
41
+ const results = yield fetchArticles(
42
+ this.args.decision.uri,
43
+ this.pageNumber,
44
+ this.pageSize,
45
+ this.articleFilterAfterTimeout,
46
+ signal
47
+ );
48
+ this.totalCount = results.totalCount;
49
+ return results.articles;
50
+ } catch (e) {
51
+ console.warn(e); // eslint-ignore-line no-console
52
+ this.totalCount = 0;
53
+ this.error = e;
54
+ return [];
55
+ } finally {
56
+ abortController.abort();
57
+ }
58
+ }
59
+
60
+ @action
61
+ close() {
62
+ this.articleResource.cancel();
63
+ this.args.close();
64
+ }
65
+
66
+ // Pagination
67
+
68
+ @action
69
+ previousPage() {
70
+ --this.pageNumber;
71
+ }
72
+
73
+ @action
74
+ nextPage() {
75
+ ++this.pageNumber;
76
+ }
77
+
78
+ get rangeStart() {
79
+ return this.pageNumber * this.pageSize + 1;
80
+ }
81
+
82
+ get rangeEnd() {
83
+ const end = this.rangeStart + this.pageSize - 1;
84
+ return end > this.totalCount ? this.totalCount : end;
85
+ }
86
+
87
+ get isFirstPage() {
88
+ return this.pageNumber == 0;
89
+ }
90
+
91
+ get isLastPage() {
92
+ return this.rangeEnd == this.totalCount;
93
+ }
94
+ }