@teipublisher/pb-components 2.26.1-next.3 → 3.0.0-next-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 (262) hide show
  1. package/.github/workflows/docker-cypress.yml +54 -0
  2. package/.github/workflows/main.yml +6 -4
  3. package/.github/workflows/node.js.yml +56 -21
  4. package/.github/workflows/release.js.yml +19 -17
  5. package/.releaserc.json +1 -1
  6. package/CHANGELOG.md +346 -11
  7. package/Dockerfile +78 -70
  8. package/README.md +112 -4
  9. package/css/components.css +5 -5
  10. package/css/gridjs/mermaid.min.css +1 -1
  11. package/css/leaflet/Control.Geocoder.css +1 -126
  12. package/css/leaflet/images/layers.png +0 -0
  13. package/css/tify/tify.css +6 -5
  14. package/css/tom-select/tom-select.bootstrap4.min.css +1 -1
  15. package/css/tom-select/tom-select.bootstrap5.min.css +1 -1
  16. package/css/tom-select/tom-select.default.min.css +1 -1
  17. package/css/tom-select/tom-select.default.min.css.map +1 -0
  18. package/css/tom-select/tom-select.min.css +1 -1
  19. package/cypress.config.js +84 -0
  20. package/dist/api.html +1 -1
  21. package/dist/css/design-system.css +607 -0
  22. package/dist/demo/bundle-test.html +4 -3
  23. package/dist/demo/components.css +46 -1
  24. package/dist/demo/design-system.html +710 -0
  25. package/dist/demo/dts-client.html +2 -2
  26. package/dist/demo/pb-autocomplete.html +23 -11
  27. package/dist/demo/pb-autocomplete2.html +66 -55
  28. package/dist/demo/pb-autocomplete3.html +17 -8
  29. package/dist/demo/pb-blacklab-highlight.html +28 -11
  30. package/dist/demo/pb-blacklab-results.html +3 -2
  31. package/dist/demo/pb-browse-docs.html +24 -24
  32. package/dist/demo/pb-browse-docs2.html +3 -3
  33. package/dist/demo/pb-clipboard.html +32 -28
  34. package/dist/demo/pb-code-editor.html +6 -6
  35. package/dist/demo/pb-code-highlight.html +63 -63
  36. package/dist/demo/pb-codepen.html +1 -1
  37. package/dist/demo/pb-collapse.html +1 -1
  38. package/dist/demo/pb-collapse2.html +2 -2
  39. package/dist/demo/pb-combo-box.html +135 -130
  40. package/dist/demo/pb-custom-form.html +64 -55
  41. package/dist/demo/pb-dialog.html +12 -6
  42. package/dist/demo/pb-document.html +1 -1
  43. package/dist/demo/pb-download.html +68 -59
  44. package/dist/demo/pb-drawer.html +67 -46
  45. package/dist/demo/pb-drawer2.html +65 -58
  46. package/dist/demo/pb-edit-app.html +2 -2
  47. package/dist/demo/pb-edit-xml.html +1 -1
  48. package/dist/demo/pb-facsimile-2.html +26 -11
  49. package/dist/demo/pb-facsimile-3.html +25 -10
  50. package/dist/demo/pb-facsimile-dedup-test-2.html +48 -0
  51. package/dist/demo/pb-facsimile-dedup-test.html +48 -0
  52. package/dist/demo/pb-facsimile.html +4 -4
  53. package/dist/demo/pb-formula.html +1 -1
  54. package/dist/demo/pb-grid.html +22 -8
  55. package/dist/demo/pb-highlight.html +2 -2
  56. package/dist/demo/pb-i18n-simple.html +1 -0
  57. package/dist/demo/pb-i18n.html +15 -5
  58. package/dist/demo/pb-image-strip-standalone.html +2 -2
  59. package/dist/demo/pb-image-strip-view.html +2 -2
  60. package/dist/demo/pb-leaflet-map.html +3 -3
  61. package/dist/demo/pb-leaflet-map2.html +2 -2
  62. package/dist/demo/pb-leaflet-map3.html +3 -3
  63. package/dist/demo/pb-link.html +1 -1
  64. package/dist/demo/pb-load.html +2 -6
  65. package/dist/demo/pb-login.html +1 -3
  66. package/dist/demo/pb-manage-odds.html +9 -4
  67. package/dist/demo/pb-markdown.html +1 -1
  68. package/dist/demo/pb-media-query.html +2 -2
  69. package/dist/demo/pb-mei.html +2 -2
  70. package/dist/demo/pb-mei2.html +2 -2
  71. package/dist/demo/pb-message.html +2 -3
  72. package/dist/demo/pb-odd-editor.html +54 -52
  73. package/dist/demo/pb-page-header.html +27 -0
  74. package/dist/demo/pb-popover.html +1 -1
  75. package/dist/demo/pb-print-preview.html +2 -2
  76. package/dist/demo/pb-progress.html +4 -4
  77. package/dist/demo/pb-repeat.html +32 -36
  78. package/dist/demo/pb-search.html +16 -5
  79. package/dist/demo/pb-search2.html +4 -4
  80. package/dist/demo/pb-search3.html +3 -3
  81. package/dist/demo/pb-search4.html +3 -3
  82. package/dist/demo/pb-select-feature.html +4 -4
  83. package/dist/demo/pb-select-feature2.html +4 -4
  84. package/dist/demo/pb-select-feature3.html +2 -2
  85. package/dist/demo/pb-select-i18n.html +58 -53
  86. package/dist/demo/pb-select-odd.html +1 -1
  87. package/dist/demo/pb-select.html +190 -75
  88. package/dist/demo/pb-select2.html +91 -37
  89. package/dist/demo/pb-select3.html +109 -41
  90. package/dist/demo/pb-svg.html +1 -1
  91. package/dist/demo/pb-table-grid.html +26 -15
  92. package/dist/demo/pb-tabs.html +15 -7
  93. package/dist/demo/pb-tify.html +7 -7
  94. package/dist/demo/pb-timeline.html +1 -1
  95. package/dist/demo/pb-timeline2.html +1 -1
  96. package/dist/demo/pb-toggle-feature.html +26 -23
  97. package/dist/demo/pb-toggle-feature2.html +4 -4
  98. package/dist/demo/pb-toggle-feature3.html +2 -2
  99. package/dist/demo/pb-toggle-feature4.html +56 -54
  100. package/dist/demo/pb-version.html +2 -2
  101. package/dist/demo/pb-view.html +78 -40
  102. package/dist/demo/pb-view2.html +69 -46
  103. package/dist/demo/pb-view3.html +53 -48
  104. package/dist/demo/pb-view4.html +70 -49
  105. package/dist/demo/pb-zoom.html +2 -2
  106. package/dist/{es-global-bridge-d8ce175d.js → es-global-bridge-D8ZcUcx_.js} +0 -4
  107. package/dist/focus-mixin-VCsFap6b.js +768 -0
  108. package/dist/images/icons.svg +217 -0
  109. package/dist/jinn-codemirror-DETLdm08.js +1 -0
  110. package/dist/lib/openseadragon.min.js +80 -0
  111. package/dist/lib/openseadragon.min.js.map +1 -0
  112. package/dist/pb-code-editor.js +25 -20
  113. package/dist/pb-component-docs.js +414 -3225
  114. package/dist/pb-components-bundle.js +3046 -4402
  115. package/dist/pb-dialog-tklYGWfc.js +121 -0
  116. package/dist/pb-edit-app.js +208 -107
  117. package/dist/pb-elements.json +716 -249
  118. package/dist/pb-facsimile.js +46 -0
  119. package/dist/pb-i18n-C0NDma4h.js +1 -0
  120. package/dist/pb-leaflet-map.js +23 -23
  121. package/dist/pb-mei.js +152 -134
  122. package/dist/pb-mixin-DHoWQheB.js +1 -0
  123. package/dist/pb-odd-editor.js +1671 -1231
  124. package/dist/pb-tify.js +1 -27
  125. package/dist/unsafe-html-D5VGo9Oq.js +1 -0
  126. package/dist/urls-BEONu_g4.js +1 -0
  127. package/eslint.config.mjs +92 -0
  128. package/gh-pages.js +5 -3
  129. package/i18n/common/en.json +6 -0
  130. package/i18n/common/pl.json +2 -2
  131. package/images/icons.svg +217 -0
  132. package/index.html +0 -5
  133. package/lib/leaflet-src.js.map +1 -0
  134. package/lib/leaflet.markercluster-src.js.map +1 -0
  135. package/lib/openseadragon.min.js +6 -6
  136. package/package.json +56 -81
  137. package/pb-elements.json +716 -249
  138. package/rollup.config.mjs +312 -0
  139. package/src/assets/components.css +5 -5
  140. package/src/assets/design-system.css +607 -0
  141. package/src/authority/airtable.js +20 -21
  142. package/src/authority/anton.js +129 -129
  143. package/src/authority/custom.js +70 -27
  144. package/src/authority/geonames.js +38 -32
  145. package/src/authority/gnd.js +50 -42
  146. package/src/authority/kbga.js +136 -134
  147. package/src/authority/metagrid.js +44 -46
  148. package/src/authority/reconciliation.js +66 -68
  149. package/src/authority/registry.js +4 -4
  150. package/src/docs/demo-utils.js +91 -0
  151. package/src/docs/pb-component-docs.js +287 -147
  152. package/src/docs/pb-component-view.js +380 -273
  153. package/src/docs/pb-components-list.js +115 -51
  154. package/src/docs/pb-demo-snippet.js +199 -174
  155. package/src/dts-client.js +306 -303
  156. package/src/dts-select-endpoint.js +125 -85
  157. package/src/parse-date-service.js +184 -135
  158. package/src/pb-ajax.js +175 -173
  159. package/src/pb-authority-lookup.js +198 -158
  160. package/src/pb-autocomplete.js +731 -313
  161. package/src/pb-blacklab-highlight.js +266 -260
  162. package/src/pb-blacklab-results.js +230 -225
  163. package/src/pb-browse-docs.js +601 -484
  164. package/src/pb-browse.js +68 -65
  165. package/src/pb-clipboard.js +97 -76
  166. package/src/pb-code-editor.js +111 -103
  167. package/src/pb-code-highlight.js +234 -204
  168. package/src/pb-codepen.js +81 -73
  169. package/src/pb-collapse.js +265 -152
  170. package/src/pb-combo-box.js +191 -191
  171. package/src/pb-components-bundle.js +1 -7
  172. package/src/pb-components.js +2 -6
  173. package/src/pb-custom-form.js +230 -141
  174. package/src/pb-dialog.js +99 -63
  175. package/src/pb-document.js +118 -91
  176. package/src/pb-download.js +214 -198
  177. package/src/pb-drawer.js +146 -149
  178. package/src/pb-edit-app.js +471 -240
  179. package/src/pb-edit-xml.js +101 -98
  180. package/src/pb-events.js +126 -107
  181. package/src/pb-facs-link.js +130 -101
  182. package/src/pb-facsimile.js +494 -410
  183. package/src/pb-fetch.js +389 -0
  184. package/src/pb-formula.js +152 -154
  185. package/src/pb-geolocation.js +130 -132
  186. package/src/pb-grid-action.js +59 -56
  187. package/src/pb-grid.js +388 -228
  188. package/src/pb-highlight.js +142 -142
  189. package/src/pb-hotkeys.js +40 -42
  190. package/src/pb-i18n.js +115 -127
  191. package/src/pb-icon-button.js +108 -0
  192. package/src/pb-icon.js +283 -0
  193. package/src/pb-image-strip.js +85 -79
  194. package/src/pb-lang.js +142 -57
  195. package/src/pb-leaflet-map.js +551 -483
  196. package/src/pb-link.js +132 -126
  197. package/src/pb-load.js +495 -428
  198. package/src/pb-login.js +303 -248
  199. package/src/pb-manage-odds.js +384 -338
  200. package/src/pb-map-icon.js +90 -90
  201. package/src/pb-map-layer.js +86 -86
  202. package/src/pb-markdown.js +107 -110
  203. package/src/pb-media-query.js +75 -73
  204. package/src/pb-mei.js +523 -303
  205. package/src/pb-message.js +144 -98
  206. package/src/pb-mixin.js +268 -265
  207. package/src/pb-navigation.js +83 -96
  208. package/src/pb-observable.js +39 -39
  209. package/src/pb-odd-editor.js +1209 -948
  210. package/src/pb-odd-elementspec-editor.js +375 -310
  211. package/src/pb-odd-model-editor.js +1189 -941
  212. package/src/pb-odd-parameter-editor.js +269 -170
  213. package/src/pb-odd-rendition-editor.js +184 -131
  214. package/src/pb-page.js +451 -422
  215. package/src/pb-paginate.js +260 -178
  216. package/src/pb-panel.js +217 -183
  217. package/src/pb-popover-themes.js +16 -9
  218. package/src/pb-popover.js +297 -288
  219. package/src/pb-print-preview.js +128 -128
  220. package/src/pb-progress.js +52 -52
  221. package/src/pb-repeat.js +141 -108
  222. package/src/pb-restricted.js +85 -78
  223. package/src/pb-search.js +258 -230
  224. package/src/pb-select-feature.js +210 -126
  225. package/src/pb-select-odd.js +184 -118
  226. package/src/pb-select-template.js +113 -78
  227. package/src/pb-select.js +330 -229
  228. package/src/pb-split-list.js +181 -176
  229. package/src/pb-svg.js +81 -80
  230. package/src/pb-table-column.js +55 -55
  231. package/src/pb-table-grid.js +334 -205
  232. package/src/pb-tabs.js +238 -61
  233. package/src/pb-tify.js +3331 -126
  234. package/src/pb-timeline.js +394 -255
  235. package/src/pb-toggle-feature.js +196 -188
  236. package/src/pb-upload.js +201 -176
  237. package/src/pb-version.js +22 -34
  238. package/src/pb-view-annotate.js +138 -102
  239. package/src/pb-view.js +1722 -1272
  240. package/src/pb-zoom.js +144 -46
  241. package/src/search-result-service.js +256 -223
  242. package/src/seed-element.js +14 -22
  243. package/src/settings.js +4 -4
  244. package/src/theming.js +98 -91
  245. package/src/urls.js +403 -289
  246. package/src/utils.js +53 -51
  247. package/vite.config.js +86 -0
  248. package/css/pb-styles.css +0 -51
  249. package/dist/iron-form-3b8dcaa7.js +0 -210
  250. package/dist/jinn-codemirror-da0e2d1f.js +0 -1
  251. package/dist/paper-checkbox-515a5284.js +0 -1597
  252. package/dist/paper-icon-button-b1d31571.js +0 -398
  253. package/dist/paper-listbox-a3b7175c.js +0 -1265
  254. package/dist/pb-i18n-0611135a.js +0 -1
  255. package/dist/pb-mixin-b1caa22e.js +0 -158
  256. package/dist/polymer-hack.js +0 -1
  257. package/dist/vaadin-element-mixin-fe4a4883.js +0 -527
  258. package/lib/Control.Geocoder.min.js +0 -2
  259. package/lib/Control.Geocoder.min.js.map +0 -1
  260. package/src/assets/pb-styles.css +0 -51
  261. package/src/pb-light-dom.js +0 -41
  262. package/src/polymer-hack.js +0 -6
@@ -1,29 +1,22 @@
1
1
  // @ts-nocheck
2
- import { LitElement, html, css } from 'lit-element';
3
- import { supported as fsSupported, fileSave } from "browser-fs-access";
2
+ import { LitElement, html, css } from 'lit';
3
+ import { supported as fsSupported, fileSave } from 'browser-fs-access';
4
+ import { repeat } from 'lit/directives/repeat.js';
4
5
  import { pbMixin, waitOnce } from './pb-mixin.js';
5
6
  import { pbHotkeys } from './pb-hotkeys.js';
6
- import { repeat } from 'lit-html/directives/repeat';
7
- import { ifDefined } from 'lit-html/directives/if-defined';
8
7
 
9
8
  import '@vaadin/vaadin-tabs/vaadin-tabs';
10
9
  import '@vaadin/vaadin-tabs/vaadin-tab';
11
10
 
12
- import '@polymer/iron-ajax/iron-ajax';
13
- import '@polymer/paper-styles/color';
14
- import '@polymer/paper-card/paper-card';
11
+ import './pb-fetch.js';
15
12
  import './pb-edit-xml.js';
16
- import '@polymer/paper-icon-button/paper-icon-button';
17
- import '@polymer/paper-input/paper-input';
18
- import '@polymer/paper-item/paper-item';
19
- import '@cwmr/paper-autocomplete/paper-autocomplete';
13
+ import './pb-icon-button.js';
14
+ import './pb-autocomplete.js';
20
15
  import './pb-collapse';
21
- import '@polymer/paper-checkbox/paper-checkbox';
22
- import './pb-odd-elementspec-editor.js';
16
+ import { PbOddElementspecEditor } from './pb-odd-elementspec-editor.js';
23
17
  import './pb-message.js';
24
18
 
25
- import { get as i18n, translate } from "./pb-i18n.js";
26
- import { PbOddElementspecEditor } from "./pb-odd-elementspec-editor";
19
+ import { get as i18n, translate } from './pb-i18n.js';
27
20
 
28
21
  /**
29
22
  * ODD editor component
@@ -35,905 +28,1171 @@ import { PbOddElementspecEditor } from "./pb-odd-elementspec-editor";
35
28
  * @cssprop --pb-heading-line-height
36
29
  */
37
30
  export class PbOddEditor extends pbHotkeys(pbMixin(LitElement)) {
31
+ static get styles() {
32
+ return css`
33
+ :host {
34
+ display: flex;
35
+ /*margin: 30px 20px;*/
36
+ margin: 0;
37
+ padding: 0;
38
+ height: auto;
39
+ }
40
+
41
+ #layout {
42
+ width: 100%;
43
+ display: grid;
44
+ grid-template-columns: auto 1fr;
45
+ grid-template-rows: auto 1fr;
46
+ }
47
+
48
+ #drawer {
49
+ grid-column: 1 / 1;
50
+ min-width: 320px;
51
+ }
52
+
53
+ #navlist {
54
+ grid-column: 1 / 1;
55
+ grid-row: 2 / 2;
56
+ overflow: auto;
57
+ height: 100%;
58
+ }
59
+
60
+ .nav-item {
61
+ display: block;
62
+ width: 100%;
63
+ padding: 8px 16px;
64
+ border: none;
65
+ background: transparent;
66
+ text-align: left;
67
+ font: inherit;
68
+ color: inherit;
69
+ cursor: pointer;
70
+ transition: background-color 120ms ease;
71
+ }
72
+
73
+ .nav-item:hover,
74
+ .nav-item:focus-visible {
75
+ background: rgba(33, 150, 243, 0.12);
76
+ outline: none;
77
+ }
78
+
79
+ .nav-item--active {
80
+ background: rgba(33, 150, 243, 0.2);
81
+ font-weight: 600;
82
+ }
83
+
84
+ .specs {
85
+ grid-column: 2 / 2;
86
+ grid-row: 1 / span 2;
87
+ overflow: auto;
88
+ }
89
+
90
+ section {
91
+ padding: 20px;
92
+ }
93
+
94
+ h3,
95
+ h4 {
96
+ font-family: var(--pb-heading-font-family);
97
+ font-weight: var(--pb-heading-font-weight);
98
+ line-height: var(--pb-heading-line-height);
99
+ }
100
+
101
+ /* ported over but not checked yet */
102
+
103
+ .specs {
104
+ padding: 6px;
105
+ }
106
+
107
+ .metadata-card {
108
+ display: block;
109
+ margin-bottom: 16px;
110
+ border: 1px solid rgba(0, 0, 0, 0.12);
111
+ border-radius: 12px;
112
+ background: #fff;
113
+ overflow: hidden;
114
+ }
115
+
116
+ .metadata-card div {
117
+ padding: 0 16px 16px;
118
+ }
119
+
120
+ .pb-field {
121
+ display: flex;
122
+ flex-direction: column;
123
+ gap: 0.35rem;
124
+ margin-bottom: 10px;
125
+ }
126
+
127
+ .pb-field__label {
128
+ font-size: 0.8rem;
129
+ font-weight: 600;
130
+ text-transform: uppercase;
131
+ letter-spacing: 0.05em;
132
+ color: rgba(0, 0, 0, 0.6);
133
+ }
134
+
135
+ .pb-input {
136
+ width: 100%;
137
+ height: var(--pb-input-height, 48px);
138
+ padding: 0.5rem 0.75rem;
139
+ border: 1px solid rgba(0, 0, 0, 0.16);
140
+ border-radius: 8px;
141
+ font: inherit;
142
+ color: inherit;
143
+ background: #fff;
144
+ transition: border-color 120ms ease, box-shadow 120ms ease;
145
+ }
146
+
147
+ .pb-input::placeholder {
148
+ color: rgba(0, 0, 0, 0.4);
149
+ }
150
+
151
+ .pb-input:focus {
152
+ outline: none;
153
+ border-color: #1976d2;
154
+ box-shadow: 0 0 0 3px rgba(25, 118, 210, 0.16);
155
+ }
156
+
157
+ .pb-checkbox {
158
+ display: flex;
159
+ align-items: center;
160
+ gap: 0.5rem;
161
+ margin: 10px 0;
162
+ font-size: 0.95rem;
163
+ }
164
+
165
+ .pb-checkbox input {
166
+ width: 16px;
167
+ height: 16px;
168
+ }
169
+
170
+ .pb-input-with-button {
171
+ display: flex;
172
+ align-items: center;
173
+ gap: 0.5rem;
174
+ }
175
+
176
+ .pb-input-with-button pb-icon-button {
177
+ margin: 0;
178
+ }
179
+
180
+ .metadata-card .extCssEdit {
181
+ display: flex;
182
+ align-items: center;
183
+ padding: 0;
184
+ }
185
+ .metadata-card .extCssEdit .pb-input {
186
+ flex: 2;
187
+ }
188
+ .metadata-card .extCssEdit pb-edit-xml {
189
+ width: 40px;
190
+ }
191
+
192
+ #jump-to {
193
+ margin-top: 1em;
194
+ }
195
+
196
+ odd-model {
197
+ border-bottom: 1px solid #e0e0e0;
198
+ }
199
+ odd-model h4 {
200
+ margin-top: 15px;
201
+ padding-top: 5px;
202
+ border-top: 1px solid #e0e0e0;
203
+ }
204
+ .renditions {
205
+ margin-top: 10px;
206
+ }
207
+ .icons {
208
+ display: inline-block;
209
+ white-space: nowrap;
210
+ }
211
+
212
+ details {
213
+ --details-transition-duration: 0.8s;
214
+ }
215
+ details[open] {
216
+ padding: 0;
217
+ }
218
+
219
+ pb-message#errorMsg {
220
+ background: var(--paper-red-500);
221
+ color: white;
222
+ }
223
+ .card-content {
224
+ height: 100%;
225
+ overflow: auto;
226
+ }
227
+
228
+ .pb-tab {
229
+ width: 100px;
230
+ }
231
+
232
+ .editingView {
233
+ width: 100%;
234
+ }
235
+
236
+ vaadin-tabs {
237
+ margin-top: 10px;
238
+ }
239
+
240
+ vaadin-tab {
241
+ background: var(--paper-grey-200);
242
+ padding: 0 6px;
243
+ border: thin solid var(--paper-grey-300);
244
+ border-bottom: none;
245
+ }
246
+ vaadin-tab[selected] {
247
+ background: white;
248
+ border-top-right-radius: 20px;
249
+ box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14), 0 1px 5px 0 rgba(0, 0, 0, 0.12),
250
+ 0 3px 1px -2px rgba(0, 0, 0, 0.2);
251
+ }
252
+ `;
253
+ }
254
+
255
+ static get properties() {
256
+ return {
257
+ ...super.properties,
258
+ ident: {
259
+ type: String,
260
+ },
261
+ /**
262
+ * ElementSpec mode. Can be ´add´, ´change´ or undefined.
263
+ */
264
+ mode: {
265
+ type: String,
266
+ },
267
+ /**
268
+ * Array of ´odd-model´ Elements
269
+ */
270
+ models: {
271
+ type: Array,
272
+ },
273
+ /**
274
+ * the odd file being edited
275
+ */
276
+ odd: {
277
+ type: String,
278
+ reflect: true,
279
+ },
280
+ /**
281
+ * array of ´element-spec´ Elements of given odd file
282
+ */
283
+ elementSpecs: {
284
+ type: Array,
285
+ },
286
+ source: {
287
+ type: String,
288
+ },
289
+ title: {
290
+ type: String,
291
+ },
292
+ titleShort: {
293
+ type: String,
294
+ reflect: true,
295
+ attribute: 'title-short',
296
+ },
297
+ description: {
298
+ type: String,
299
+ },
300
+ namespace: {
301
+ type: String,
302
+ },
303
+ rootPath: {
304
+ type: String,
305
+ attribute: 'root-path',
306
+ },
307
+ loading: {
308
+ type: Boolean,
309
+ },
310
+ indentString: {
311
+ type: String,
312
+ },
313
+ outputPrefix: {
314
+ type: String,
315
+ attribute: 'output-prefix',
316
+ },
317
+ outputRoot: {
318
+ type: String,
319
+ attribute: 'output-root',
320
+ },
321
+ currentSelection: {
322
+ type: Object,
323
+ },
324
+ useNamespace: {
325
+ type: Boolean,
326
+ },
327
+ loggedIn: {
328
+ type: Boolean,
329
+ },
330
+ tabs: {
331
+ type: Array,
332
+ },
333
+ tabIndex: {
334
+ type: Number,
335
+ reflect: true,
336
+ },
337
+ };
338
+ }
339
+
340
+ constructor() {
341
+ super();
342
+ this.ident = '';
343
+ this.mode = '';
344
+ this.models = () => [];
345
+ this.odd = '';
346
+ this.elementSpecs = [];
347
+ this.source = '';
348
+ this.title = '';
349
+ this.titleShort = '';
350
+ this.description = '';
351
+ this.namespace = '';
352
+ this.rootPath = '';
353
+ this.loading = false;
354
+ this.indentString = ' ';
355
+ this.outputPrefix = '';
356
+ this.outputRoot = '';
357
+ this.currentSelection = {};
358
+ this.useNamespace = false;
359
+ this.loggedIn = true;
360
+ this._tabs = [];
361
+ this.tabIndex = undefined;
362
+ this.selectedNavIndex = 0;
363
+ this.cssFile = '';
364
+ this.hotkeys = {
365
+ save: 'ctrl+shift+s,command+shift+s',
366
+ };
367
+ this._hasChanges = false;
368
+ }
38
369
 
39
- static get styles() {
40
- return css`
41
- :host {
42
- display: flex;
43
- /*margin: 30px 20px;*/
44
- margin:0;
45
- padding:0;
46
- height:auto;
47
- }
48
-
49
- #layout {
50
- width: 100%;
51
- display: grid;
52
- grid-template-columns: auto 1fr;
53
- grid-template-rows: auto 1fr;
54
- }
55
-
56
- #drawer {
57
- grid-column: 1 / 1;
58
- min-width: 320px;
59
- }
60
-
61
- #navlist {
62
- grid-column: 1 / 1;
63
- grid-row: 2 / 2;
64
- overflow: auto;
65
- height: 100%;
66
- }
67
-
68
- .specs {
69
- grid-column: 2 / 2;
70
- grid-row: 1 / span 2;
71
- overflow: auto;
72
- }
73
-
74
- section{
75
- padding:20px;
76
- }
77
-
78
- h3, h4 {
79
- font-family: var(--pb-heading-font-family);
80
- font-weight: var(--pb-heading-font-weight);
81
- line-height: var(--pb-heading-line-height);
82
- }
83
-
84
- /* ported over but not checked yet */
85
-
86
- .specs {
87
- padding:6px;
88
- }
89
-
90
- .metadata {
91
- display: block;
92
- }
93
-
94
- .metadata div {
95
- padding: 0 16px 16px;
96
- }
97
-
98
- .metadata paper-input {
99
- margin-bottom: 10px;
100
- }
101
-
102
- .metadata .extCssEdit {
103
- display: flex;
104
- align-items: center;
105
- padding: 0;
106
- }
107
- .metadata .extCssEdit paper-input {
108
- flex: 2;
109
- }
110
- .metadata .extCssEdit pb-edit-xml {
111
- width: 40px;
112
- }
113
-
114
- #jump-to {
115
- margin-top: 1em;
116
- }
117
-
118
- odd-model {
119
- border-bottom: 1px solid #E0E0E0;
120
- }
121
- odd-model h4 {
122
- margin-top: 15px;
123
- padding-top: 5px;
124
- border-top: 1px solid #E0E0E0;
125
- }
126
- .renditions {
127
- margin-top: 10px;
128
- }
129
- .icons{
130
- display:inline-block;
131
- white-space: nowrap;
132
- }
133
-
134
- /* todo: this doesn't work - should refactor to have the complete trigger exposed here (move out of pb-collapse) */
135
- pb-collapse#meta ::slotted(.collapse-trigger){
136
- /*height:56px;*/
137
- }
138
-
139
- iron-collapse {
140
- --iron-collapse-transition-duration:0.8s;
141
- }
142
-
143
- pb-message#errorMsg{
144
- background: var(--paper-red-500);
145
- color:white;
146
- }
147
- .card-content{
148
- height:100%;
149
- overflow:auto;
150
- }
151
-
152
- paper-tab{
153
- width:100px;
154
- }
155
-
156
- .editingView {
157
- width:100%;
158
- }
159
-
160
- vaadin-tabs{
161
- margin-top:10px;
162
- }
163
-
164
- vaadin-tab{
165
- background:var(--paper-grey-200);
166
- padding:0 6px;
167
- border:thin solid var(--paper-grey-300);
168
- border-bottom:none;
169
- }
170
- vaadin-tab[selected]{
171
- background:white;
172
- border-top-right-radius:20px;
173
- box-shadow: 0 2px 2px 0 rgba(0, 0, 0, 0.14),
174
- 0 1px 5px 0 rgba(0, 0, 0, 0.12),
175
- 0 3px 1px -2px rgba(0, 0, 0, 0.2);
176
-
177
- }
178
-
179
- `;
180
- }
181
-
182
- static get properties() {
183
- return {
184
- ...super.properties,
185
- ident: {
186
- type: String
187
- },
188
- /**
189
- * ElementSpec mode. Can be ´add´, ´change´ or undefined.
190
- */
191
- mode: {
192
- type: String
193
- },
194
- /**
195
- * Array of ´odd-model´ Elements
196
- */
197
- models: {
198
- type: Array
199
- },
200
- /**
201
- * the odd file being edited
202
- */
203
- odd: {
204
- type: String,
205
- reflect: true
206
- },
207
- /**
208
- * array of ´element-spec´ Elements of given odd file
209
- */
210
- elementSpecs: {
211
- type: Array
212
- },
213
- source: {
214
- type: String
215
- },
216
- title: {
217
- type: String
218
- },
219
- titleShort: {
220
- type: String,
221
- reflect: true,
222
- attribute: 'title-short'
223
- },
224
- description: {
225
- type: String
226
- },
227
- namespace: {
228
- type: String
229
- },
230
- rootPath: {
231
- type: String,
232
- attribute: 'root-path'
233
- },
234
- loading: {
235
- type: Boolean
236
- },
237
- indentString: {
238
- type: String
239
- },
240
- outputPrefix: {
241
- type: String,
242
- attribute: 'output-prefix'
243
- },
244
- outputRoot: {
245
- type: String,
246
- attribute: 'output-root'
247
- },
248
- currentSelection: {
249
- type: Object
250
- },
251
- useNamespace: {
252
- type: Boolean
253
- },
254
- loggedIn: {
255
- type: Boolean
256
- },
257
- tabs: {
258
- type: Array
259
- },
260
- tabIndex: {
261
- type: Number,
262
- reflect: true
263
- }
264
- };
265
- }
266
-
267
- constructor() {
268
- super();
269
- this.ident = '';
270
- this.mode = '';
271
- this.models = () => [];
272
- this.odd = '';
273
- this.elementSpecs = [];
274
- this.source = '';
275
- this.title = '';
276
- this.titleShort = '';
277
- this.description = '';
278
- this.namespace = '';
279
- this.rootPath = '';
280
- this.loading = false;
281
- this.indentString = ' ';
282
- this.outputPrefix = '';
283
- this.outputRoot = '';
284
- this.currentSelection = {};
285
- this.useNamespace = false;
286
- this.loggedIn = true;
287
- this.tabs = [];
288
- this.tabIndex = undefined;
289
- this.selectedNavIndex = 0;
290
- this.cssFile = '';
291
- this.hotkeys = {
292
- save: 'ctrl+shift+s,command+shift+s'
293
- }
294
- this._hasChanges = false;
370
+ get tabs() {
371
+ if (!this._tabs) {
372
+ this._tabs = [];
295
373
  }
296
-
297
- render() {
298
- return html`
299
- <iron-ajax id="loadContent"
300
- handle-as="json" content-type="application/x-www-form-urlencoded"
301
- with-credentials
302
- method="GET"></iron-ajax>
303
-
304
- <iron-ajax id="saveOdd"
305
- handle-as="json"
306
- with-credentials></iron-ajax>
307
-
308
- <div id="layout">
309
- <div id="drawer">
310
- <slot id="slot"></slot>
311
- <h3>
312
- <span>${this.odd}</span>
313
-
314
- <span class="icons">
315
- <pb-edit-xml id="editSource"><paper-icon-button icon="code" title="${translate('odd.editor.odd-source')}"></paper-icon-button></pb-edit-xml>
316
- <paper-icon-button @click="${() => this.save(true)}" icon="icons:cloud-download" title="${fsSupported ? translate('odd.editor.save-as'): translate('odd.editor.download')}"></paper-icon-button>
317
- <paper-icon-button @click="${this._reload}" icon="refresh" title="${translate('odd.editor.reload')}"></paper-icon-button>
318
- <paper-icon-button @click="${() => this.save(false)}" icon="save" title="${translate('odd.editor.save')} ${this.display('save')}"
319
- ?disabled="${!this.loggedIn}"></paper-icon-button>
320
- </span>
321
- </h3>
322
- <div id="new-element" class="input-group">
323
- <paper-input id="identNew" label="${translate('odd.editor.add-element')}" always-float-label="always-float-label">
324
- <paper-icon-button slot="suffix" @click="${this.addElementSpec}" icon="add" tabindex="-1"></paper-icon-button>
325
- </paper-input>
326
- </div>
327
-
328
- <div id="jump-to">
329
- <paper-autocomplete id="jumpTo" label="${translate('odd.editor.jump-to')}" always-float-label="always-float-label"></paper-autocomplete>
330
- </div>
331
-
332
- <h3>${translate('odd.editor.specs')}</h3>
333
- </div>
334
- <div id="navlist">
335
- ${repeat(this.elementSpecs, (i) => i.ident, (i, index) =>
336
- html`
337
- <paper-item id="es_${i.ident}"
338
- index="${index}"
339
- @click="${(ev) => this._openElementSpec(ev, index)}">${i.ident}</paper-item>
340
- `)}
341
- </div>
342
- <section class="specs" id="specs">
343
-
344
- <paper-card class="metadata">
345
- <pb-collapse id="meta">
346
- <h4 slot="collapse-trigger" class="panel-title">
347
- ${this._computedTitle()}
348
- </h4>
349
- <div slot="collapse-content">
350
- <paper-input id="title" name="title" value="${this.title}" label="${translate('odd.editor.title')}"
351
- placeholder="[${translate('odd.editor.title-placeholder')}]"
352
- @change="${this._inputTitle}"></paper-input>
353
- <paper-input id="titleShort" name="short-title" .value="${this.titleShort}" label="${translate('odd.editor.title-short')}"
354
- placeholder="[${translate('odd.editor.title-short-placeholder')}]"
355
- @change="${(e) => this.titleShort = e.composedPath()[0].value}"></paper-input>
356
- <paper-input id="description" name="description" .value="${ifDefined(this.description)}" label="${translate('odd.editor.description-label')}"
357
- placeholder="[${translate('odd.editor.description-placeholder')}]"
358
- @change="${(e) => this.description = e.composedPath()[0].value}"></paper-input>
359
- <paper-input id="source" name="source" ?value="${this.source}" label="${translate('odd.editor.source-label')}"
360
- placeholder="[${translate('odd.editor.source-placeholder')}]"
361
- @change="${(e) => this.source = e.composedPath()[0].value}"></paper-input>
362
- <paper-checkbox id="useNamespace" @change="${this.setUseNamespace}">${translate('odd.editor.use-namespace')}</paper-checkbox>
363
- <paper-input id="namespace" name="namespace" value="${this.namespace}" label="Namespace" ?disabled="${!this.useNamespace}"
364
- placeholder="[${translate('odd.editor.namespace-placeholder')}]"
365
- @change="${(e) => this.namespace = e.composedPath()[0].value}"></paper-input>
366
- <div class="extCssEdit">
367
- <paper-input name="cssFile" value="${this.cssFile}" label="External CSS File"
368
- placeholder="[External CSS file with additional class definitions]"
369
- @change="${this._cssFileChanged}"></paper-input>
370
- <pb-edit-xml id="editCSS"><paper-icon-button icon="create" title="${translate('odd.editor.css-source')}"></paper-icon-button></pb-edit-xml>
371
- </div>
372
- </div>
373
- </pb-collapse>
374
- </paper-card>
375
-
376
- <!-- todo: import elementspec to make it function -->
377
-
378
- <div class="editingView">
379
- <vaadin-tabs id="tabs" selected="${this.tabIndex}">
380
- ${repeat(this.tabs, (i) => i.id, (i, index) =>
381
- html`
382
- <vaadin-tab name="${i}" @click="${(e) => this._selectTab(e, i)}"><span style="padding-right:20px;">${i}</span><paper-icon-button icon="close" @click="${(ev) => this._closeTabHandler(ev, index)}"></paper-icon-button></vaadin-tab>
383
- `)}
384
- </vaadin-tabs>
385
-
386
- <div id="currentElement"></div>
387
- </div>
388
- </section>
389
-
374
+ return this._tabs;
375
+ }
376
+
377
+ set tabs(value) {
378
+ this._tabs = Array.isArray(value) ? value : [];
379
+ }
380
+
381
+ get safeTabs() {
382
+ const tabs = this._tabs || [];
383
+ return Array.isArray(tabs) ? tabs : [];
384
+ }
385
+
386
+ render() {
387
+ return html`
388
+ <pb-fetch
389
+ id="loadContent"
390
+ handle-as="json"
391
+ content-type="application/x-www-form-urlencoded"
392
+ with-credentials
393
+ method="GET"
394
+ ></pb-fetch>
395
+
396
+ <pb-fetch id="saveOdd" handle-as="json" with-credentials></pb-fetch>
397
+
398
+ <div id="layout">
399
+ <div id="drawer">
400
+ <slot id="slot"></slot>
401
+ <h3>
402
+ <span>${this.odd}</span>
403
+
404
+ <span class="icons">
405
+ <pb-edit-xml id="editSource"
406
+ ><pb-icon-button
407
+ icon="code"
408
+ title="${translate('odd.editor.odd-source')}"
409
+ ></pb-icon-button
410
+ ></pb-edit-xml>
411
+ <pb-icon-button
412
+ @click="${() => this.save(true)}"
413
+ icon="icons:cloud-download"
414
+ title="${fsSupported
415
+ ? translate('odd.editor.save-as')
416
+ : translate('odd.editor.download')}"
417
+ ></pb-icon-button>
418
+ <pb-icon-button
419
+ @click="${this._reload}"
420
+ icon="refresh"
421
+ title="${translate('odd.editor.reload')}"
422
+ ></pb-icon-button>
423
+ <pb-icon-button
424
+ @click="${() => this.save(false)}"
425
+ icon="save"
426
+ title="${translate('odd.editor.save')} ${this.display('save')}"
427
+ ?disabled="${!this.loggedIn}"
428
+ ></pb-icon-button>
429
+ </span>
430
+ </h3>
431
+ <div id="new-element" class="input-group">
432
+ <label class="pb-field">
433
+ <span class="pb-field__label">${translate('odd.editor.add-element')}</span>
434
+ <div class="pb-input-with-button">
435
+ <input
436
+ id="identNew"
437
+ class="pb-input"
438
+ name="ident-new"
439
+ placeholder="${translate('odd.editor.add-element')}"
440
+ />
441
+ <pb-icon-button
442
+ @click="${this.addElementSpec}"
443
+ icon="add"
444
+ tabindex="-1"
445
+ ></pb-icon-button>
446
+ </div>
447
+ </label>
448
+ </div>
449
+
450
+ <div id="jump-to">
451
+ <pb-autocomplete
452
+ id="jumpTo"
453
+ placeholder="${translate('odd.editor.jump-to')}"
454
+ ></pb-autocomplete>
455
+ </div>
456
+
457
+ <h3>${translate('odd.editor.specs')}</h3>
390
458
  </div>
391
-
392
-
393
- <pb-message id="dialog" hidden></pb-message>
394
- <pb-message id="errorMsg"></pb-message>
395
- `;
459
+ <div id="navlist">
460
+ ${repeat(
461
+ this.elementSpecs || [],
462
+ i => i.ident,
463
+ (i, index) =>
464
+ html`
465
+ <button
466
+ id="es_${i.ident}"
467
+ type="button"
468
+ class="nav-item ${this.selectedNavIndex === index ? 'nav-item--active' : ''}"
469
+ @click="${ev => this._openElementSpec(ev, index)}"
470
+ >
471
+ ${i.ident}
472
+ </button>
473
+ `,
474
+ )}
475
+ </div>
476
+ <section class="specs" id="specs">
477
+ <div class="metadata-card">
478
+ <pb-collapse id="meta">
479
+ <h4 slot="collapse-trigger" class="panel-title">${this._computedTitle()}</h4>
480
+ <div slot="collapse-content">
481
+ <label class="pb-field">
482
+ <span class="pb-field__label">${translate('odd.editor.title')}</span>
483
+ <input
484
+ id="title"
485
+ class="pb-input"
486
+ name="title"
487
+ .value=${this.title || ''}
488
+ placeholder="[${translate('odd.editor.title-placeholder')}]"
489
+ @change=${this._inputTitle}
490
+ />
491
+ </label>
492
+ <label class="pb-field">
493
+ <span class="pb-field__label">${translate('odd.editor.title-short')}</span>
494
+ <input
495
+ id="titleShort"
496
+ class="pb-input"
497
+ name="short-title"
498
+ .value=${this.titleShort || ''}
499
+ placeholder="[${translate('odd.editor.title-short-placeholder')}]"
500
+ @change=${e => (this.titleShort = e.target.value)}
501
+ />
502
+ </label>
503
+ <label class="pb-field">
504
+ <span class="pb-field__label">${translate('odd.editor.description-label')}</span>
505
+ <input
506
+ id="description"
507
+ class="pb-input"
508
+ name="description"
509
+ .value=${this.description || ''}
510
+ placeholder="[${translate('odd.editor.description-placeholder')}]"
511
+ @change=${e => (this.description = e.target.value)}
512
+ />
513
+ </label>
514
+ <label class="pb-field">
515
+ <span class="pb-field__label">${translate('odd.editor.source-label')}</span>
516
+ <input
517
+ id="source"
518
+ class="pb-input"
519
+ name="source"
520
+ .value=${this.source || ''}
521
+ placeholder="[${translate('odd.editor.source-placeholder')}]"
522
+ @change=${e => (this.source = e.target.value)}
523
+ />
524
+ </label>
525
+ <label class="pb-checkbox">
526
+ <input
527
+ id="useNamespace"
528
+ type="checkbox"
529
+ ?checked=${this.useNamespace}
530
+ @change=${this.setUseNamespace}
531
+ />
532
+ <span>${translate('odd.editor.use-namespace')}</span>
533
+ </label>
534
+ <label class="pb-field">
535
+ <span class="pb-field__label">Namespace</span>
536
+ <input
537
+ id="namespace"
538
+ class="pb-input"
539
+ name="namespace"
540
+ .value=${this.namespace || ''}
541
+ ?disabled=${!this.useNamespace}
542
+ placeholder="[${translate('odd.editor.namespace-placeholder')}]"
543
+ @change=${e => (this.namespace = e.target.value)}
544
+ />
545
+ </label>
546
+ <div class="extCssEdit">
547
+ <input
548
+ name="cssFile"
549
+ class="pb-input"
550
+ .value=${this.cssFile || ''}
551
+ placeholder="[External CSS file with additional class definitions]"
552
+ @change=${this._cssFileChanged}
553
+ />
554
+ <pb-edit-xml id="editCSS"
555
+ ><pb-icon-button
556
+ icon="create"
557
+ title="${translate('odd.editor.css-source')}"
558
+ ></pb-icon-button
559
+ ></pb-edit-xml>
560
+ </div>
561
+ </div>
562
+ </pb-collapse>
563
+ </div>
564
+
565
+ <!-- todo: import elementspec to make it function -->
566
+
567
+ <div class="editingView">
568
+ <vaadin-tabs id="tabs" selected="${this.tabIndex || 0}">
569
+ ${repeat(
570
+ this.tabs || [],
571
+ i => i,
572
+ (i, index) =>
573
+ html`
574
+ <vaadin-tab name="${i}" @click="${e => this._selectTab(e, i)}"
575
+ ><span style="padding-right:20px;">${i}</span
576
+ ><pb-icon-button
577
+ icon="close"
578
+ @click="${ev => this._closeTabHandler(ev, index)}"
579
+ ></pb-icon-button
580
+ ></vaadin-tab>
581
+ `,
582
+ )}
583
+ </vaadin-tabs>
584
+
585
+ <div id="currentElement"></div>
586
+ </div>
587
+ </section>
588
+ </div>
589
+
590
+ <pb-message id="dialog" hidden></pb-message>
591
+ <pb-message id="errorMsg"></pb-message>
592
+ `;
593
+ }
594
+
595
+ firstUpdated(changedProperties) {
596
+ this.shadowRoot.getElementById('useNamespace').checked = this.useNamespace;
597
+
598
+ // console.log('firstUpdated ', changedProperties);
599
+ // console.log('firstUpdated endpoint', this.getEndpoint());
600
+ // console.log('firstUpdated rootpath', this.rootPath);
601
+ this.jumpCtrl = this.shadowRoot.getElementById('jumpTo');
602
+ this.jumpCtrl.addEventListener('pb-autocomplete-selected', this.jumpTo.bind(this));
603
+
604
+ const oddSelector = this.querySelector('odd-selector');
605
+
606
+ if (this.odd && oddSelector) {
607
+ oddSelector.selected = this.odd;
608
+
609
+ oddSelector.addEventListener('odd-selected', e => {
610
+ if (confirm('Any unsaved changes will be lost. Continue?')) {
611
+ this.odd = e.detail.odd;
612
+ window.history.pushState({}, '', `?odd=${this.odd}`);
613
+ }
614
+ oddSelector.selected = this.odd;
615
+ });
396
616
  }
397
617
 
398
- firstUpdated(changedProperties) {
399
- this.shadowRoot.getElementById('useNamespace').checked = this.useNamespace;
618
+ this.addEventListener('current-changed', this._changeSelection);
619
+ this.addEventListener('odd-copy', e => this._copy(e));
620
+ this.addEventListener('odd-paste', e => this._paste(e));
621
+ this.addEventListener('element-spec-removed', this.removeElementSpec.bind(this));
400
622
 
401
- // console.log('firstUpdated ', changedProperties);
402
- // console.log('firstUpdated endpoint', this.getEndpoint());
403
- // console.log('firstUpdated rootpath', this.rootPath);
404
- this.jumpCtrl = this.shadowRoot.getElementById('jumpTo');
405
- this.jumpCtrl.addEventListener('autocomplete-selected', this.jumpTo.bind(this));
406
-
407
- const oddSelector = this.querySelector('odd-selector');
408
-
409
- if (this.odd && oddSelector) {
410
- oddSelector.selected = this.odd;
411
-
412
- oddSelector.addEventListener('odd-selected', e => {
413
- if (confirm('Any unsaved changes will be lost. Continue?')) {
414
- this.odd = e.detail.odd;
415
- window.history.pushState({}, "", '?odd=' + this.odd)
416
- }
417
- oddSelector.selected = this.odd;
418
- })
419
- }
623
+ window.addEventListener('beforeunload', () => 'Any unsaved changes will be lost. Continue?');
420
624
 
421
- this.addEventListener('current-changed', this._changeSelection);
422
- this.addEventListener('odd-copy', e => this._copy(e));
423
- this.addEventListener('odd-paste', e => this._paste(e));
424
- this.addEventListener('element-spec-removed', this.removeElementSpec.bind(this));
625
+ this.subscribeTo('pb-login', ev => {
626
+ this.loggedIn = ev.detail.user != null;
627
+ });
425
628
 
426
- window.addEventListener('beforeunload', () => 'Any unsaved changes will be lost. Continue?');
629
+ this.focus();
427
630
 
428
- this.subscribeTo('pb-login', (ev) => {
429
- this.loggedIn = ev.detail.user != null;
430
- });
631
+ this.loadContent = this.shadowRoot.getElementById('loadContent');
431
632
 
432
- this.focus();
633
+ // it is unclear to me why root-path is not read from attribute without this explicit call
634
+ this.rootPath = this.getAttribute('root-path');
433
635
 
434
- this.loadContent = this.shadowRoot.getElementById('loadContent');
636
+ waitOnce('pb-page-ready', () => {
637
+ this.load();
638
+ this.inited = true;
639
+ });
435
640
 
436
- // it is unclear to me why root-path is not read from attribute without this explicit call
437
- this.rootPath = this.getAttribute('root-path');
641
+ this.registerHotkey('save', () => this.save(false));
642
+ }
438
643
 
439
- waitOnce('pb-page-ready', () => {
440
- this.load();
441
- this.inited = true;
442
- });
644
+ setUseNamespace() {
645
+ this.useNamespace = this.shadowRoot.getElementById('useNamespace').checked;
646
+ }
443
647
 
444
- this.registerHotkey('save', () => this.save(false));
648
+ async load() {
649
+ if (this.loading) {
650
+ return;
445
651
  }
652
+ this.loading = true;
446
653
 
447
- setUseNamespace() {
448
- this.useNamespace = this.shadowRoot.getElementById('useNamespace').checked;
654
+ if (this.rootPath === '' || this.odd === '') {
655
+ return;
449
656
  }
450
657
 
451
- async load() {
452
- if (this.loading) {
453
- return;
454
- }
455
- this.loading = true;
456
-
457
- if (this.rootPath === '' || this.odd === '') {
458
- return;
459
- }
460
-
461
- // reset
462
- this.elementSpecs = [];
658
+ // reset
659
+ this.elementSpecs = [];
660
+ this._tabs = [];
661
+ this.tabIndex = 0;
463
662
 
464
- document.dispatchEvent(new CustomEvent('pb-start-update'));
663
+ document.dispatchEvent(new CustomEvent('pb-start-update'));
465
664
 
665
+ // this.$.editSource.setPath(this.rootPath + '/' + this.odd);
666
+ const editSrc = this.shadowRoot.getElementById('editSource');
667
+ editSrc.setPath(`${this.rootPath}/${this.odd}`);
668
+ // this.shadowRoot.getElementById('editSource').setPath(this.rootPath + '/' + this.odd)
466
669
 
467
- // this.$.editSource.setPath(this.rootPath + '/' + this.odd);
468
- const editSrc = this.shadowRoot.getElementById('editSource');
469
- editSrc.setPath(this.rootPath + '/' + this.odd);
470
- // this.shadowRoot.getElementById('editSource').setPath(this.rootPath + '/' + this.odd)
670
+ const params = { odd: this.odd, root: this.rootPath };
471
671
 
472
- const params = { odd: this.odd, root: this.rootPath };
473
-
474
- // this.$.loadContent.params = params;
475
- this.loadContent.params = params;
476
- this.loadContent.url = `${this.getEndpoint()}/${this.lessThanApiVersion('1.0.0') ? 'modules/editor.xql' : 'api/odd/' + this.odd}`;
477
- const request = this.loadContent.generateRequest();
672
+ // this.$.loadContent.params = params;
673
+ this.loadContent.params = params;
674
+ this.loadContent.url = `${this.getEndpoint()}/${
675
+ this.lessThanApiVersion('1.0.0') ? 'modules/editor.xql' : `api/odd/${this.odd}`
676
+ }`;
677
+
678
+ // Set Accept header to request JSON response
679
+ this.loadContent.headers = {
680
+ 'Accept': 'application/json'
681
+ };
682
+
683
+ const request = this.loadContent.generateRequest();
478
684
 
479
- this._hasChanges = false;
480
- request.completes.then(r => this.handleOdd(r));
685
+ this._hasChanges = false;
686
+
687
+ // Handle case where generateRequest returns null (invalid URL)
688
+ if (!request) {
689
+ console.warn('pb-odd-editor: Failed to generate request - invalid URL');
690
+ this.loading = false;
691
+ document.dispatchEvent(new CustomEvent('pb-end-update'));
692
+ return;
481
693
  }
482
-
483
- handleOdd(req) {
484
- const data = req.response;
485
- this.loggedIn = data.canWrite;
486
- this.source = data.source;
487
- this.title = data.title;
488
- this.titleShort = data.titleShort;
489
- this.description = data.description;
490
- this.cssFile = data.cssFile == null ? '' : data.cssFile;
491
- this.namespace = data.namespace != null ? data.namespace : '';
492
- this.useNamespace = data.namespace != null;
493
-
494
- if (this.cssFile) {
495
- const editCss = this.shadowRoot.getElementById('editCSS');
496
- editCss.setPath(this.rootPath + '/' + this.cssFile);
497
- }
498
-
499
- // update elementSpecs
500
- this.elementSpecs = data.elementSpecs.map(es => this.mapElementSpec(es));
501
-
502
- // init auto-complete list
503
- // const jumpTo = this.shadowRoot.getElementById('jumpTo');
504
- // jumpTo.source = this.elementSpecs.map(this._specMapper);
505
- this._updateAutoComplete();
506
-
507
- this.requestUpdate();
508
-
694
+
695
+ request
696
+ .then(data => this.handleOdd({ response: data }))
697
+ .catch(error => {
698
+ console.warn('pb-odd-editor: Failed to load ODD data:', error);
509
699
  this.loading = false;
510
700
  document.dispatchEvent(new CustomEvent('pb-end-update'));
701
+ });
702
+ }
511
703
 
512
- document.title = this.titleShort || this.title;
513
- }
704
+ handleOdd(req) {
705
+ const data = req.response;
514
706
 
515
- _updateAutoComplete() {
516
- const jumpTo = this.shadowRoot.getElementById('jumpTo');
517
- jumpTo.source = this.elementSpecs.map(this._specMapper);
707
+ // Handle case where data is null (request failed)
708
+ if (!data) {
709
+ console.warn('pb-odd-editor: Failed to load ODD data');
710
+ return;
518
711
  }
519
712
 
520
- _cssFileChanged(e) {
521
- this.cssFile = e.composedPath()[0].value;
522
- if (this.cssFile) {
523
- const editCss = this.shadowRoot.getElementById('editCSS');
524
- editCss.setPath(this.rootPath + '/' + this.cssFile);
525
- }
526
- }
527
-
528
- /**
529
- * handler for paper-item in navigation list in the drawer
530
- *
531
- * @param e
532
- * @param index
533
- * @private
534
- */
535
- _navlistActiveChanged(e, index) {
536
- // set the paper-item active that got the click
537
- this.selectedNavIndex = index;
538
- this.requestUpdate();
713
+ this.loggedIn = data.canWrite;
714
+ this.source = data.source;
715
+ this.title = data.title;
716
+ this.titleShort = data.titleShort;
717
+ this.description = data.description;
718
+ this.cssFile = data.cssFile == null ? '' : data.cssFile;
719
+ this.namespace = data.namespace != null ? data.namespace : '';
720
+ this.useNamespace = data.namespace != null;
721
+
722
+ if (this.cssFile) {
723
+ const editCss = this.shadowRoot.getElementById('editCSS');
724
+ editCss.setPath(`${this.rootPath}/${this.cssFile}`);
539
725
  }
540
726
 
541
- _returnTabs() {
542
- return this.tabs;
727
+ // update elementSpecs
728
+ if (data.elementSpecs && Array.isArray(data.elementSpecs)) {
729
+ this.elementSpecs = data.elementSpecs.map(es => this.mapElementSpec(es));
730
+ } else {
731
+ console.warn('pb-odd-editor: elementSpecs data is missing or invalid');
732
+ this.elementSpecs = [];
543
733
  }
544
734
 
545
- _selectTab(e, item) {
546
- const spec = this.elementSpecs.find(theSpec => theSpec.ident === item);
547
- this._updateElementspec(spec);
548
- }
735
+ // init auto-complete list
736
+ this._updateAutoComplete();
549
737
 
550
- _openElementSpec(ev, index) {
551
- console.log('_openElementSpec ', ev, index);
738
+ this.requestUpdate();
552
739
 
553
- const spec = this.elementSpecs[index]; //get target elementspec
554
- this._updateElementspec(spec);
740
+ this.loading = false;
741
+ document.dispatchEvent(new CustomEvent('pb-end-update'));
555
742
 
556
- const ident = spec.ident;
743
+ document.title = this.titleShort || this.title;
744
+ }
557
745
 
558
- // do not re-open existing tab, but select it
559
- if (this.tabs.indexOf(ident) >= 0) {
560
- this.tabIndex = this.tabs.indexOf(ident);
561
- this.requestUpdate();
562
- return;
563
- }
746
+ _updateAutoComplete() {
747
+ const jumpTo = this.shadowRoot.getElementById('jumpTo');
748
+ jumpTo.suggestions = this.elementSpecs.map(this._specMapper);
749
+ }
564
750
 
565
- this.tabs.push(ident);
566
- this.tabIndex = this.tabs.length - 1;
567
- this.requestUpdate();
751
+ _cssFileChanged(e) {
752
+ this.cssFile = e.target.value;
753
+ if (this.cssFile) {
754
+ const editCss = this.shadowRoot.getElementById('editCSS');
755
+ editCss.setPath(`${this.rootPath}/${this.cssFile}`);
568
756
  }
569
-
570
- _updateElementspec(elementSpec) {
571
- // const spec = this.elementSpecs.find(theSpec => theSpec.ident === specIdent);
572
-
573
- // reset - delete current element if there's one
574
- const currentElement = this.shadowRoot.getElementById('currentElement');
575
- currentElement.innerHTML = "";
576
-
577
- // create new elementspec
578
- const newElementSpec = new PbOddElementspecEditor();
579
- newElementSpec.addEventListener('element-spec-changed', this.handleElementSpecChanged.bind(this));
580
- newElementSpec.ident = elementSpec.ident;
581
- newElementSpec.models = elementSpec.models;
582
- newElementSpec.mode = elementSpec.mode;
583
- newElementSpec.endpoint = this._endpoint;
584
- newElementSpec.apiVersion = this._apiVersion;
585
-
586
- newElementSpec.hotkeys = this.hotkeys;
587
- currentElement.appendChild(newElementSpec);
757
+ }
758
+
759
+ /**
760
+ * Handler for navigation buttons in the drawer
761
+ *
762
+ * @param e
763
+ * @param index
764
+ * @private
765
+ */
766
+ _navlistActiveChanged(e, index) {
767
+ // mark the navigation button that received the click as active
768
+ this.selectedNavIndex = index;
769
+ this.requestUpdate();
770
+ }
771
+
772
+ _returnTabs() {
773
+ return this.tabs;
774
+ }
775
+
776
+ _selectTab(e, item) {
777
+ const spec = this.elementSpecs.find(theSpec => theSpec.ident === item);
778
+ this._updateElementspec(spec);
779
+ }
780
+
781
+ _openElementSpec(ev, index) {
782
+ console.log('_openElementSpec ', ev, index);
783
+
784
+ const spec = this.elementSpecs[index]; // get target elementspec
785
+ this._updateElementspec(spec);
786
+ this.selectedNavIndex = index;
787
+ this.requestUpdate();
788
+
789
+ const { ident } = spec;
790
+
791
+ // do not re-open existing tab, but select it
792
+ if (this.tabs.indexOf(ident) >= 0) {
793
+ this.tabIndex = this.tabs.indexOf(ident);
794
+ this.requestUpdate();
795
+ return;
588
796
  }
589
797
 
590
- _closeTabHandler(ev, index) {
591
- console.log('_closeTabHandler ', index);
592
- ev.preventDefault();
593
- ev.stopPropagation();
594
-
595
- this._closeTab(index);
596
- return false;
798
+ this.tabs.push(ident);
799
+ this.tabIndex = this.tabs.length - 1;
800
+ this.requestUpdate();
801
+ }
802
+
803
+ _updateElementspec(elementSpec) {
804
+ // const spec = this.elementSpecs.find(theSpec => theSpec.ident === specIdent);
805
+
806
+ // reset - delete current element if there's one
807
+ const currentElement = this.shadowRoot.getElementById('currentElement');
808
+ currentElement.innerHTML = '';
809
+
810
+ // create new elementspec
811
+ const newElementSpec = new PbOddElementspecEditor();
812
+ newElementSpec.addEventListener(
813
+ 'element-spec-changed',
814
+ this.handleElementSpecChanged.bind(this),
815
+ );
816
+ newElementSpec.ident = elementSpec.ident;
817
+ newElementSpec.models = elementSpec.models;
818
+ newElementSpec.mode = elementSpec.mode;
819
+ newElementSpec.endpoint = this._endpoint;
820
+ newElementSpec.apiVersion = this._apiVersion;
821
+
822
+ newElementSpec.hotkeys = this.hotkeys;
823
+ currentElement.appendChild(newElementSpec);
824
+ }
825
+
826
+ _closeTabHandler(ev, index) {
827
+ console.log('_closeTabHandler ', index);
828
+ ev.preventDefault();
829
+ ev.stopPropagation();
830
+
831
+ this._closeTab(index);
832
+ return false;
833
+ }
834
+
835
+ _closeTab(index) {
836
+ const newTabs = [...this.tabs];
837
+ newTabs.splice(index, 1);
838
+
839
+ // last tab closed
840
+ if (newTabs.length === 0) {
841
+ this.shadowRoot.getElementById('currentElement').innerHTML = '';
842
+ this.tabIndex = 0;
843
+ this.tabs = [];
597
844
  }
598
-
599
- _closeTab(index) {
600
- this.tabs.splice(index, 1);
601
- // last tab closed
602
- if (this.tabs.length === 0) {
603
- this.shadowRoot.getElementById('currentElement').innerHTML = '';
604
- this.tabIndex = 0;
605
- this.tabs = [];
606
- }
607
- // a tab left of selected tab or current tab closed
608
- else if (this.tabIndex > 0 && this.tabIndex >= index) {
609
- // decrease tabIndex by one
610
- this.tabIndex -= 1;
611
-
612
- const currentTab = this.tabs[this.tabIndex];
613
- this._selectTab(null, currentTab);
614
- }
845
+ // a tab left of selected tab or current tab closed
846
+ else if (this.tabIndex > 0 && this.tabIndex >= index) {
847
+ // decrease tabIndex by one
848
+ this.tabIndex -= 1;
849
+ this.tabs = newTabs;
850
+
851
+ const currentTab = this.tabs[this.tabIndex];
852
+ this._selectTab(null, currentTab);
853
+ } else {
854
+ this.tabs = newTabs;
615
855
  }
616
-
617
- attributeChangedCallback(name, oldVal, newVal) {
618
- // console.log('attributeChangedCallback', name, oldVal, newVal);
619
-
620
- super.attributeChangedCallback(name, oldVal, newVal);
621
- if (name == 'odd' && oldVal !== newVal) {
622
- // console.log('<pb-document> Emit update event');
623
- // this.emitTo('pb-odd-editor', this);
624
- if (this.inited) {
625
- this.load();
626
- }
627
- }
856
+ }
857
+
858
+ attributeChangedCallback(name, oldVal, newVal) {
859
+ // console.log('attributeChangedCallback', name, oldVal, newVal);
860
+
861
+ super.attributeChangedCallback(name, oldVal, newVal);
862
+ if (name == 'odd' && oldVal !== newVal) {
863
+ // console.log('<pb-document> Emit update event');
864
+ // this.emitTo('pb-odd-editor', this);
865
+ if (this.inited) {
866
+ this.load();
867
+ }
628
868
  }
629
-
630
- static get replaceCharMap() {
631
- return {
632
- '"': '&quot;',
633
- '&': '&amp;',
634
- '<': '&lt;',
635
- '>': '&gt;'
636
- }
869
+ }
870
+
871
+ static get replaceCharMap() {
872
+ return {
873
+ '"': '&quot;',
874
+ '&': '&amp;',
875
+ '<': '&lt;',
876
+ '>': '&gt;',
637
877
  };
878
+ }
638
879
 
639
- static get replaceCharRegexp() {
640
- return /"|&|<|>/g
641
- }
642
-
643
- static replaceChars(match) {
644
- return PbOddEditor.replaceCharMap[match];
645
- }
880
+ static get replaceCharRegexp() {
881
+ return /"|&|<|>/g;
882
+ }
646
883
 
647
- jumpTo(e) {
648
- const jumpCtrl = this.shadowRoot.getElementById('jumpTo');
649
- const id = '#es_' + jumpCtrl.text;
650
- const target = this.shadowRoot.querySelector(id);
651
- if (!target) {
652
- return
653
- }
884
+ static replaceChars(match) {
885
+ return PbOddEditor.replaceCharMap[match];
886
+ }
654
887
 
655
- this.jumpCtrl.clear();
656
- target.click();
888
+ jumpTo(e) {
889
+ const ident = (e?.detail?.value || e?.detail?.text || this.jumpCtrl?.value || '').trim();
890
+ if (!ident) {
891
+ return;
657
892
  }
658
-
659
- _computedTitle() {
660
- if (!this.odd) {
661
- return ''
662
- }
663
- return this.title || this.titleShort || this.odd || 'Loading ...'
893
+ const target = this.shadowRoot.querySelector(`#es_${ident}`);
894
+ if (!target) {
895
+ return;
664
896
  }
665
897
 
666
- _copy(e) {
667
- // console.log('odd-editor._copy ', e);
668
- this.clipboard = e.detail.model;
669
- const clone = JSON.parse(JSON.stringify(e.detail.model));
670
- this.clipboard = clone;
898
+ if (this.jumpCtrl) {
899
+ this.jumpCtrl.value = '';
671
900
  }
901
+ target.click();
902
+ }
672
903
 
673
- _paste(e) {
674
- console.log('_paste ', e);
675
- console.log('_paste clipboard', this.clipboard);
676
-
677
- if (this.clipboard == {} || this.clipboard == undefined) {
678
- return;
679
- }
680
- const targetElement = e.detail.target;
681
- targetElement.addModel(this.clipboard);
682
- targetElement.render();
904
+ _computedTitle() {
905
+ if (!this.odd) {
906
+ return '';
683
907
  }
684
-
685
- _specMapper(spec) {
686
- return {
687
- text: spec.ident,
688
- value: spec.ident
689
- };
690
- }
691
-
692
- _specObserver(changeRecord) {
693
- const source = this.elementSpecs.map(this._specMapper);
694
- this.jumpCtrl.source = source;
695
- }
696
-
697
- mapElementSpec(elementSpec) {
698
- return Object.assign(
699
- {},
700
- elementSpec,
701
- { models: elementSpec.models.map(m => this.addShowToModel(m)) }
702
- );
703
- }
704
-
705
- addShowToModel(model) {
706
- if (model.models) {
707
- const extendedModels = model.models.map(m => this.addShowToModel(m));
708
- return Object.assign({}, model, { models: extendedModels, show: false });
709
- }
710
- return Object.assign({}, model, { show: false });
908
+ return this.title || this.titleShort || this.odd || 'Loading ...';
909
+ }
910
+
911
+ _copy(e) {
912
+ // console.log('odd-editor._copy ', e);
913
+ this.clipboard = e.detail.model;
914
+ const clone = JSON.parse(JSON.stringify(e.detail.model));
915
+ this.clipboard = clone;
916
+ }
917
+
918
+ _paste(e) {
919
+ console.log('_paste ', e);
920
+ console.log('_paste clipboard', this.clipboard);
921
+
922
+ if (this.clipboard == {} || this.clipboard == undefined) {
923
+ return;
711
924
  }
925
+ const targetElement = e.detail.target;
926
+ targetElement.addModel(this.clipboard);
927
+ targetElement.render();
928
+ }
929
+
930
+ _specMapper(spec) {
931
+ return {
932
+ text: spec.ident,
933
+ value: spec.ident,
934
+ };
935
+ }
712
936
 
713
- addElementSpec(ev) {
714
- // const ident = this.$.identNew.value;
715
- const identNew = this.shadowRoot.getElementById('identNew');
937
+ _specObserver(changeRecord) {
938
+ const source = this.elementSpecs.map(this._specMapper);
939
+ this.jumpCtrl.suggestions = source;
940
+ }
716
941
 
717
- const ident = identNew.value;
718
- if (!ident || ident.length === 0) {
719
- return;
720
- }
721
- const existingSpec = this.elementSpecs.find((spec) => spec.ident === ident);
722
- if (existingSpec) {
723
- console.log('<pb-odd-editor> element spec to be added already exists: %s', ident);
724
- const id = `#es_${ident}`;
725
- const target = this.shadowRoot.querySelector(id);
726
- if (!target) {
727
- return
728
- }
729
- target.click();
730
- return;
731
- }
942
+ mapElementSpec(elementSpec) {
943
+ return {
944
+ ...elementSpec,
945
+ models: elementSpec.models.map(m => this.addShowToModel(m)),
946
+ };
947
+ }
732
948
 
733
- const oldApiParams = {
734
- action: "find",
735
- odd: this.odd,
736
- root: this.rootPath,
737
- ident
738
- };
739
- const newApiParams = {
740
- root: this.rootPath,
741
- ident
742
- };
743
-
744
- const params = this.lessThanApiVersion('1.0.0') ? oldApiParams : newApiParams;
745
-
746
- this.loadContent.params = params;
747
- this.loadContent.url = `${this.getEndpoint()}/${this.lessThanApiVersion('1.0.0') ? 'modules/editor.xql' : 'api/odd/' + this.odd}`;
748
- let request = this.loadContent.generateRequest();
749
- request.completes.then(this._handleElementSpecResponse.bind(this));
949
+ addShowToModel(model) {
950
+ if (model.models) {
951
+ const extendedModels = model.models.map(m => this.addShowToModel(m));
952
+ return { ...model, models: extendedModels, show: false };
750
953
  }
954
+ return { ...model, show: false };
955
+ }
751
956
 
957
+ addElementSpec(ev) {
958
+ // const ident = this.$.identNew.value;
959
+ const identNew = this.shadowRoot.getElementById('identNew');
752
960
 
753
- _handleElementSpecResponse(req) {
754
- const identNew = this.shadowRoot.getElementById('identNew');
755
-
756
- const data = req.response;
757
- const ident = identNew.value
758
- const mode = (data.status === 'not-found' ? 'add' : 'change');
759
- const models = data.models || [];
760
- const newSpec = {
761
- ident,
762
- mode,
763
- models
764
- };
765
-
766
- this.elementSpecs.unshift(newSpec);
767
- // trigger update of autocomplete list in jumpTo
768
- identNew.value = '';
769
-
770
- //open new tab with newly created element
771
- this.tabs.push(ident);
772
- this.tabIndex = this.tabs.length - 1;
773
-
774
- this.elementSpecs.sort((a, b) => a.ident.localeCompare(b.ident));
775
-
776
- this.requestUpdate().then(() => {
777
- const elem = this.shadowRoot.querySelectorAll('paper-item');
778
- const idx = this.elementSpecs.indexOf(newSpec);
779
-
780
- this._updateAutoComplete();
781
-
782
- elem[idx].click();
783
- elem[idx].focus();
784
- });
961
+ const ident = identNew.value;
962
+ if (!ident || ident.length === 0) {
963
+ return;
785
964
  }
786
-
787
- removeElementSpec(ev) {
788
- const ident = ev.detail.target.ident;
789
- this.shadowRoot.getElementById('dialog')
790
- .confirm(i18n('browse.delete'), i18n('odd.editor.delete-spec', { ident }))
791
- .then(() => {
792
- const targetIndex = this.elementSpecs.findIndex(theSpec => theSpec.ident === ident);
793
- this.elementSpecs.splice(targetIndex, 1);
794
- this.requestUpdate();
795
-
796
-
797
- const selectedTab = this.shadowRoot.querySelector('vaadin-tab[selected]');
798
- const tabName = selectedTab.getAttribute('name');
799
- const idx = this.tabs.indexOf(tabName);
800
- this._closeTab(idx);
801
- }, () => null);
965
+ const existingSpec = this.elementSpecs.find(spec => spec.ident === ident);
966
+ if (existingSpec) {
967
+ console.log('<pb-odd-editor> element spec to be added already exists: %s', ident);
968
+ const id = `#es_${ident}`;
969
+ const target = this.shadowRoot.querySelector(id);
970
+ if (!target) {
971
+ return;
972
+ }
973
+ target.click();
974
+ return;
802
975
  }
803
976
 
804
- serializeOdd() {
805
- const ns = this.useNamespace ? ` ns="${this.namespace}"` : '';
806
- const source = this.source ? ` source="${this.source}"` : '';
807
- const description = this.description ? ` <desc>${this.description}</desc>` : '';
808
- const title = `${this.indentString}<title>${this.title}${description}</title>\n`;
809
- const titleShort = this.titleShort ? `${this.indentString}<title type="short">${this.titleShort}</title>\n` : '';
810
- const cssFile = this.cssFile ? `${this.indentString}<rendition source="${this.cssFile}"/>\n` : '';
811
- const elementSpecs = this.elementSpecs
812
- .map(e => this.serializeElementSpec(this.indentString, e)).join('');
813
-
814
- return `<schemaSpec xmlns="http://www.tei-c.org/ns/1.0" xmlns:pb="http://teipublisher.com/1.0"${ns}${source}>\n${title}${titleShort}${cssFile}\n${elementSpecs}</schemaSpec>\n`
815
- }
977
+ const oldApiParams = {
978
+ action: 'find',
979
+ odd: this.odd,
980
+ root: this.rootPath,
981
+ ident,
982
+ };
983
+ const newApiParams = {
984
+ root: this.rootPath,
985
+ ident,
986
+ };
816
987
 
817
- serializeElementSpec(indent, elementSpec) {
818
- const mode = elementSpec.mode ? ` mode="${elementSpec.mode}"` : '';
819
- const indent2 = indent + this.indentString
820
- const models = elementSpec.models
821
- .map(m => this.serializeModel(indent2, m))
822
- .join('')
988
+ const params = this.lessThanApiVersion('1.0.0') ? oldApiParams : newApiParams;
989
+
990
+ this.loadContent.params = params;
991
+ this.loadContent.url = `${this.getEndpoint()}/${
992
+ this.lessThanApiVersion('1.0.0') ? 'modules/editor.xql' : `api/odd/${this.odd}`
993
+ }`;
994
+ const request = this.loadContent.generateRequest();
995
+ request.then(data => this._handleElementSpecResponse({ response: data }));
996
+ }
997
+
998
+ _handleElementSpecResponse(req) {
999
+ const identNew = this.shadowRoot.getElementById('identNew');
1000
+
1001
+ const data = req.response;
1002
+ const ident = identNew.value;
1003
+ const mode = data.status === 'not-found' ? 'add' : 'change';
1004
+ const models = data.models || [];
1005
+ const newSpec = {
1006
+ ident,
1007
+ mode,
1008
+ models,
1009
+ };
823
1010
 
824
- return `${indent}<elementSpec ident="${elementSpec.ident}"${mode}>\n${models}${indent}</elementSpec>\n`;
1011
+ this.elementSpecs.unshift(newSpec);
1012
+ // trigger update of autocomplete list in jumpTo
1013
+ identNew.value = '';
1014
+
1015
+ // open new tab with newly created element
1016
+ this.tabs.push(ident);
1017
+ this.tabIndex = this.tabs.length - 1;
1018
+
1019
+ this.elementSpecs.sort((a, b) => a.ident.localeCompare(b.ident));
1020
+
1021
+ this.requestUpdate().then(() => {
1022
+ const elem = this.shadowRoot.querySelectorAll('.nav-item');
1023
+ const idx = this.elementSpecs.indexOf(newSpec);
1024
+
1025
+ this._updateAutoComplete();
1026
+
1027
+ elem[idx].click();
1028
+ elem[idx].focus();
1029
+ });
1030
+ }
1031
+
1032
+ removeElementSpec(ev) {
1033
+ const { ident } = ev.detail.target;
1034
+ this.shadowRoot
1035
+ .getElementById('dialog')
1036
+ .confirm(i18n('browse.delete'), i18n('odd.editor.delete-spec', { ident }))
1037
+ .then(
1038
+ () => {
1039
+ const targetIndex = this.elementSpecs.findIndex(theSpec => theSpec.ident === ident);
1040
+ this.elementSpecs.splice(targetIndex, 1);
1041
+ this.requestUpdate();
1042
+
1043
+ const selectedTab = this.shadowRoot.querySelector('vaadin-tab[selected]');
1044
+ const tabName = selectedTab.getAttribute('name');
1045
+ const idx = this.tabs.indexOf(tabName);
1046
+ this._closeTab(idx);
1047
+ },
1048
+ () => null,
1049
+ );
1050
+ }
1051
+
1052
+ serializeOdd() {
1053
+ const ns = this.useNamespace ? ` ns="${this.namespace}"` : '';
1054
+ const source = this.source ? ` source="${this.source}"` : '';
1055
+ const description = this.description ? ` <desc>${this.description}</desc>` : '';
1056
+ const title = `${this.indentString}<title>${this.title}${description}</title>\n`;
1057
+ const titleShort = this.titleShort
1058
+ ? `${this.indentString}<title type="short">${this.titleShort}</title>\n`
1059
+ : '';
1060
+ const cssFile = this.cssFile
1061
+ ? `${this.indentString}<rendition source="${this.cssFile}"/>\n`
1062
+ : '';
1063
+ const elementSpecs = this.elementSpecs
1064
+ .map(e => this.serializeElementSpec(this.indentString, e))
1065
+ .join('');
1066
+
1067
+ return `<schemaSpec xmlns="http://www.tei-c.org/ns/1.0" xmlns:pb="http://teipublisher.com/1.0"${ns}${source}>\n${title}${titleShort}${cssFile}\n${elementSpecs}</schemaSpec>\n`;
1068
+ }
1069
+
1070
+ serializeElementSpec(indent, elementSpec) {
1071
+ const mode = elementSpec.mode ? ` mode="${elementSpec.mode}"` : '';
1072
+ const indent2 = indent + this.indentString;
1073
+ const models = elementSpec.models.map(m => this.serializeModel(indent2, m)).join('');
1074
+
1075
+ return `${indent}<elementSpec ident="${elementSpec.ident}"${mode}>\n${models}${indent}</elementSpec>\n`;
1076
+ }
1077
+
1078
+ serializeModel(indent, model) {
1079
+ if (model.type === 'model' && !model.behaviour) {
1080
+ return '';
825
1081
  }
826
1082
 
827
- serializeModel(indent, model) {
828
- if (model.type === 'model' && !model.behaviour) {
829
- return '';
830
- }
831
-
832
- const nestedIndent = indent + this.indentString;
833
-
834
- const attributes = [
835
- this.serializeAttribute('output', model.output),
836
- this.serializeAttribute('predicate', model.predicate),
837
- model.type === 'model' ? this.serializeAttribute('behaviour', model.behaviour) : '',
838
- this.serializeAttribute('cssClass', model.css),
839
- this.serializeAttribute('useSourceRendition', model.sourcerend),
840
- this.serializeAttribute('pb:mode', model.mode)
841
- ].join('');
842
-
843
- const desc = model.desc ? nestedIndent + '<desc>' + model.desc + '</desc>\n' : '';
844
-
845
- // innerXML += this.serializeTag('model', nestedIndent);
846
- const models = model.models.map(m => this.serializeModel(nestedIndent, m)).join('');
847
- const parameters = model.parameters.map(p => this.serializeParameter(nestedIndent, p)).join('');
848
- const renditions = model.renditions.map(r => this.serializeRendition(nestedIndent, r)).join('');
849
- const template = PbOddEditor.serializeTemplate(nestedIndent, model.template);
850
- const innerXML = `${desc}${models}${parameters}${template}${renditions}`;
851
- const end = (innerXML.length > 0) ? `>\n${innerXML}${indent}</${model.type}` : '/';
852
-
853
- return `${indent}<${model.type}${attributes}${end}>\n`
1083
+ const nestedIndent = indent + this.indentString;
1084
+
1085
+ const attributes = [
1086
+ this.serializeAttribute('output', model.output),
1087
+ this.serializeAttribute('predicate', model.predicate),
1088
+ model.type === 'model' ? this.serializeAttribute('behaviour', model.behaviour) : '',
1089
+ this.serializeAttribute('cssClass', model.css),
1090
+ this.serializeAttribute('useSourceRendition', model.sourcerend),
1091
+ this.serializeAttribute('pb:mode', model.mode),
1092
+ ].join('');
1093
+
1094
+ const desc = model.desc ? `${nestedIndent}<desc>${model.desc}</desc>\n` : '';
1095
+
1096
+ // innerXML += this.serializeTag('model', nestedIndent);
1097
+ const models = model.models.map(m => this.serializeModel(nestedIndent, m)).join('');
1098
+ const parameters = model.parameters.map(p => this.serializeParameter(nestedIndent, p)).join('');
1099
+ const renditions = model.renditions.map(r => this.serializeRendition(nestedIndent, r)).join('');
1100
+ const template = PbOddEditor.serializeTemplate(nestedIndent, model.template);
1101
+ const innerXML = `${desc}${models}${parameters}${template}${renditions}`;
1102
+ const end = innerXML.length > 0 ? `>\n${innerXML}${indent}</${model.type}` : '/';
1103
+
1104
+ return `${indent}<${model.type}${attributes}${end}>\n`;
1105
+ }
1106
+
1107
+ serializeParameter(indent, parameter) {
1108
+ if (!parameter.name) {
1109
+ return '';
854
1110
  }
855
-
856
- serializeParameter(indent, parameter) {
857
- if (!parameter.name) {
858
- return '';
859
- }
860
- const name = this.serializeAttribute('name', parameter.name);
861
- const value = this.serializeAttribute('value', parameter.value);
862
- if (parameter.set) {
863
- return `${indent}<pb:set-param xmlns=""${name}${value}/>\n`
864
- }
865
- return `${indent}<param${name}${value}/>\n`
1111
+ const name = this.serializeAttribute('name', parameter.name);
1112
+ const value = this.serializeAttribute('value', parameter.value);
1113
+ if (parameter.set) {
1114
+ return `${indent}<pb:set-param xmlns=""${name}${value}/>\n`;
866
1115
  }
867
-
868
- serializeRendition(indent, rendition) {
869
- const scope = rendition.scope && rendition.scope !== 'null' ? this.serializeAttribute('scope', rendition.scope) : '';
870
- const css = PbOddEditor.escape(rendition.css);
871
- return `${indent}<outputRendition xml:space="preserve" ${scope}>\n${indent}${css}\n${indent}</outputRendition>\n`;
1116
+ return `${indent}<param${name}${value}/>\n`;
1117
+ }
1118
+
1119
+ serializeRendition(indent, rendition) {
1120
+ const scope =
1121
+ rendition.scope && rendition.scope !== 'null'
1122
+ ? this.serializeAttribute('scope', rendition.scope)
1123
+ : '';
1124
+ const css = PbOddEditor.escape(rendition.css);
1125
+ return `${indent}<outputRendition xml:space="preserve" ${scope}>\n${indent}${css}\n${indent}</outputRendition>\n`;
1126
+ }
1127
+
1128
+ static serializeTemplate(indent, template) {
1129
+ if (!template) {
1130
+ return '';
872
1131
  }
1132
+ return `${indent}<pb:template xml:space="preserve" xmlns="">${template}</pb:template>\n`;
1133
+ }
873
1134
 
874
- static serializeTemplate(indent, template) {
875
- if (!template) {
876
- return '';
877
- }
878
- return `${indent}<pb:template xml:space="preserve" xmlns="">${template}</pb:template>\n`;
879
- }
1135
+ serializeAttribute(name, value) {
1136
+ return value ? ` ${name}="${PbOddEditor.escape(value)}"` : '';
1137
+ }
880
1138
 
881
- serializeAttribute(name, value) {
882
- return value ? ` ${name}="${PbOddEditor.escape(value)}"` : ''
1139
+ static escape(code) {
1140
+ if (!code) {
1141
+ return '';
883
1142
  }
884
-
885
- static escape(code) {
886
- if (!code) {
887
- return '';
888
- }
889
- if (typeof code === 'string') {
890
- return code.replace(PbOddEditor.replaceCharRegexp, PbOddEditor.replaceChars);
891
- }
892
- return code;
1143
+ if (typeof code === 'string') {
1144
+ return code.replace(PbOddEditor.replaceCharRegexp, PbOddEditor.replaceChars);
893
1145
  }
894
-
895
- save(download=false) {
896
- document.dispatchEvent(new CustomEvent('pb-start-update'));
897
- const data = this.serializeOdd();
898
-
899
- this.shadowRoot.getElementById('dialog').show(i18n("odd.editor.save"), i18n('odd.editor.saving'));
900
-
901
- const saveOdd = this.shadowRoot.getElementById('saveOdd');
902
- saveOdd.url = `${this.getEndpoint()}/${this.lessThanApiVersion('1.0.0') ? 'modules/editor.xql' : 'api/odd/' + this.odd}`;
903
- if (this.lessThanApiVersion('1.0.0')) {
904
- saveOdd.contentType = 'application/x-www-form-urlencoded';
905
- saveOdd.method = "POST";
906
- saveOdd.params = null;
907
- saveOdd.body = {
908
- action: "save",
909
- root: this.rootPath,
910
- "output-prefix": this.outputPrefix,
911
- "output-root": this.outputRoot,
912
- odd: this.odd,
913
- data
914
- };
915
- } else {
916
- saveOdd.contentType = 'application/xml';
917
- saveOdd.method = "PUT";
918
- saveOdd.params = {
919
- root: this.rootPath,
920
- "output-prefix": this.outputPrefix,
921
- "output-root": this.outputRoot,};
922
- saveOdd.body = data;
923
- }
924
-
925
- const request = saveOdd.generateRequest();
926
- request.completes
927
- .then((req) => {
928
- this.handleSaveComplete(req, download);
929
- })
930
- .catch(this.handleSaveError.bind(this));
1146
+ return code;
1147
+ }
1148
+
1149
+ save(download = false) {
1150
+ document.dispatchEvent(new CustomEvent('pb-start-update'));
1151
+ const data = this.serializeOdd();
1152
+
1153
+ this.shadowRoot
1154
+ .getElementById('dialog')
1155
+ .show(i18n('odd.editor.save'), i18n('odd.editor.saving'));
1156
+
1157
+ const saveOdd = this.shadowRoot.getElementById('saveOdd');
1158
+ saveOdd.url = `${this.getEndpoint()}/${
1159
+ this.lessThanApiVersion('1.0.0') ? 'modules/editor.xql' : `api/odd/${this.odd}`
1160
+ }`;
1161
+ if (this.lessThanApiVersion('1.0.0')) {
1162
+ saveOdd.contentType = 'application/x-www-form-urlencoded';
1163
+ saveOdd.method = 'POST';
1164
+ saveOdd.params = null;
1165
+ saveOdd.body = {
1166
+ action: 'save',
1167
+ root: this.rootPath,
1168
+ 'output-prefix': this.outputPrefix,
1169
+ 'output-root': this.outputRoot,
1170
+ odd: this.odd,
1171
+ data,
1172
+ };
1173
+ } else {
1174
+ saveOdd.contentType = 'application/xml';
1175
+ saveOdd.method = 'PUT';
1176
+ saveOdd.params = {
1177
+ root: this.rootPath,
1178
+ 'output-prefix': this.outputPrefix,
1179
+ 'output-root': this.outputRoot,
1180
+ };
1181
+ saveOdd.body = data;
931
1182
  }
932
1183
 
933
- //to be deprecated: only used for old api
934
- static _renderReport(report) {
935
- if (report.error) {
936
- return `
1184
+ const request = saveOdd.generateRequest();
1185
+ request
1186
+ .then(data => {
1187
+ this.handleSaveComplete({ response: data }, download);
1188
+ })
1189
+ .catch(this.handleSaveError.bind(this));
1190
+ }
1191
+
1192
+ // to be deprecated: only used for old api
1193
+ static _renderReport(report) {
1194
+ if (report.error) {
1195
+ return `
937
1196
  <div class="list-group-item-danger">
938
1197
  <h4 class="list-group-item-heading">${report.file}</h4>
939
1198
  <h5 class="list-group-item-heading">Compilation error on line ${report.line}:</h5>
@@ -941,138 +1200,140 @@ export class PbOddEditor extends pbHotkeys(pbMixin(LitElement)) {
941
1200
  <pre class="list-group-item-text">${report.message}</pre>
942
1201
  </div>
943
1202
  `;
944
- }
945
- return `
1203
+ }
1204
+ return `
946
1205
  <div class="list-group-item-success">
947
1206
  <p class="list-group-item-text">Generated ${report.file}</p>
948
1207
  </div>
949
1208
  `;
1209
+ }
1210
+
1211
+ handleSaveComplete(req, download = false) {
1212
+ const data = req.response;
1213
+ if (data.status === 'denied') {
1214
+ this.shadowRoot
1215
+ .getElementById('dialog')
1216
+ .set(i18n('odd.editor.denied'), i18n('odd.editor.denied-message', { odd: this.odd }));
1217
+ document.dispatchEvent(new CustomEvent('pb-end-update'));
1218
+ return;
950
1219
  }
951
1220
 
952
- handleSaveComplete(req, download=false) {
953
- const data = req.response;
954
- if (data.status === 'denied') {
955
- this.shadowRoot.getElementById('dialog').set(i18n("odd.editor.denied"), i18n("odd.editor.denied-message", { odd: this.odd }));
956
- document.dispatchEvent(new CustomEvent('pb-end-update'));
957
- return;
958
- }
959
-
960
- let msg;
961
-
962
- if (this.lessThanApiVersion('1.0.0')) {
963
- const report = data.report.map(PbOddEditor._renderReport);
964
- msg = `<div class="list-group">${report.join('')}</div>`;
965
- } else {
966
- const report = data.report;
967
- msg = `<div class="list-group">${report}</div>`;
968
- }
969
-
970
- this.shadowRoot.getElementById('dialog').set(i18n("odd.editor.saved"), msg);
971
-
972
- this._hasChanges = false;
973
- document.dispatchEvent(new CustomEvent('pb-end-update'));
1221
+ let msg;
974
1222
 
975
- if (download) {
976
- const blob = new Blob([data.source], { type: 'application/xml'});
977
- fileSave(blob, {
978
- fileName: this.odd,
979
- extensions: ['.odd'],
980
- })
981
- .then(
982
- () => console.log(`<pb-odd-editor> ${this.odd} exported`),
983
- () => console.log('<pb-odd-editor> export aborted')
984
- );
985
- }
1223
+ if (this.lessThanApiVersion('1.0.0')) {
1224
+ const report = data.report.map(PbOddEditor._renderReport);
1225
+ msg = `<div class="list-group">${report.join('')}</div>`;
1226
+ } else {
1227
+ const { report } = data;
1228
+ msg = `<div class="list-group">${report}</div>`;
986
1229
  }
987
1230
 
988
- handleSaveError(rejected) {
989
- this.shadowRoot.getElementById('dialog').set("Error", rejected.error);
990
- // this.$.dialog.set("Error", rejected.error);
991
- document.dispatchEvent(new CustomEvent('pb-end-update'));
992
- }
1231
+ this.shadowRoot.getElementById('dialog').set(i18n('odd.editor.saved'), msg);
993
1232
 
994
- _reload() {
995
- this.shadowRoot.getElementById('dialog')
996
- .confirm(i18n('odd.editor.reload'), i18n('odd.editor.reload-confirm'))
997
- .then(() => {
998
- this.load();
999
- this.tabs = [];
1000
- this.tabIndex = 0;
1001
- this.shadowRoot.getElementById('currentElement').innerHTML = '';
1002
- }, () => null);
1003
- }
1233
+ this._hasChanges = false;
1234
+ document.dispatchEvent(new CustomEvent('pb-end-update'));
1004
1235
 
1005
- _setCurrentSelection(e) {
1006
- if (this.currentSelection != undefined) {
1007
- this.currentSelection.removeAttribute('currentselection');
1008
- }
1009
- this.currentSelection = e.target;
1010
- this.currentSelection.setAttribute('currentselection', 'true');
1236
+ if (download) {
1237
+ const blob = new Blob([data.source], { type: 'application/xml' });
1238
+ fileSave(blob, {
1239
+ fileName: this.odd,
1240
+ extensions: ['.odd'],
1241
+ }).then(
1242
+ () => console.log(`<pb-odd-editor> ${this.odd} exported`),
1243
+ () => console.log('<pb-odd-editor> export aborted'),
1244
+ );
1011
1245
  }
1012
-
1013
-
1014
- _changeSelection(ev) {
1015
- ev.preventDefault();
1016
- ev.stopPropagation();
1017
-
1018
- if (ev.detail.target === this) return;
1019
-
1020
- if (this.currentSelection &&
1021
- this.currentSelection.tagName !== undefined) {
1022
- this.currentSelection.removeAttribute('currentselection');
1023
- }
1024
-
1025
- let newSelection;
1026
- if (ev.detail.target) {
1027
- newSelection = ev.detail.target;
1028
- } else {
1029
- newSelection = ev.target;
1030
- }
1031
- newSelection.setAttribute('currentselection', 'true');
1032
- this.currentSelection = newSelection;
1246
+ }
1247
+
1248
+ handleSaveError(rejected) {
1249
+ this.shadowRoot.getElementById('dialog').set('Error', rejected.error);
1250
+ // this.$.dialog.set("Error", rejected.error);
1251
+ document.dispatchEvent(new CustomEvent('pb-end-update'));
1252
+ }
1253
+
1254
+ _reload() {
1255
+ this.shadowRoot
1256
+ .getElementById('dialog')
1257
+ .confirm(i18n('odd.editor.reload'), i18n('odd.editor.reload-confirm'))
1258
+ .then(
1259
+ () => {
1260
+ this.load();
1261
+ this.tabs = [];
1262
+ this.tabIndex = 0;
1263
+ this.shadowRoot.getElementById('currentElement').innerHTML = '';
1264
+ },
1265
+ () => null,
1266
+ );
1267
+ }
1268
+
1269
+ _setCurrentSelection(e) {
1270
+ if (this.currentSelection != undefined) {
1271
+ this.currentSelection.removeAttribute('currentselection');
1033
1272
  }
1273
+ this.currentSelection = e.target;
1274
+ this.currentSelection.setAttribute('currentselection', 'true');
1275
+ }
1034
1276
 
1035
- _selectElementspec(e) {
1036
- if (this.currentElementSpec &&
1037
- this.currentElementSpec.tagName === 'PB-ODD-ELEMENTSPEC-EDITOR') {
1038
- this.currentElementSpec.removeAttribute('currentselection');
1039
- }
1040
- const newSelection = e.target;
1041
- newSelection.setAttribute('currentselection', 'true');
1042
- this.currentElementSpec = newSelection;
1043
- }
1277
+ _changeSelection(ev) {
1278
+ ev.preventDefault();
1279
+ ev.stopPropagation();
1044
1280
 
1045
- nsDisabled() {
1046
- return !this.useNamespace;
1047
- }
1281
+ if (ev.detail.target === this) return;
1048
1282
 
1049
- _handleLoadError(e) {
1050
- console.log('loading error occurred: ', e);
1051
- const msg = this.shadowRoot.getElementById('errorMsg');
1052
- msg.style.background = 'red';
1053
- const url = this.shadowRoot.getElementById('loadContent').url;
1054
- console.log('url ', url);
1055
- msg.show('Error: ', 'ODD file could not be loaded from ' + url);
1283
+ if (this.currentSelection && this.currentSelection.tagName !== undefined) {
1284
+ this.currentSelection.removeAttribute('currentselection');
1056
1285
  }
1057
1286
 
1058
-
1059
- handleElementSpecChanged(e) {
1060
- // console.log('handleElementSpecChanged ',e);
1061
- this._hasChanges = true;
1062
- const elementSpec = this.elementSpecs.find(es => es.ident === e.detail.ident);
1063
- const index = this.elementSpecs.indexOf(elementSpec);
1064
- const newSpec = Object.assign({}, elementSpec, { models: e.detail.models });
1065
- const allSpecs = Array.from(this.elementSpecs);
1066
- allSpecs.splice(index, 1, newSpec)
1067
- this.elementSpecs = allSpecs;
1068
- // console.log('updated elementspecs ', this.elementSpecs);
1069
-
1287
+ let newSelection;
1288
+ if (ev.detail.target) {
1289
+ newSelection = ev.detail.target;
1290
+ } else {
1291
+ newSelection = ev.target;
1070
1292
  }
1071
-
1072
- _inputTitle(ev) {
1073
- this.title = ev.composedPath()[0].value;
1293
+ newSelection.setAttribute('currentselection', 'true');
1294
+ this.currentSelection = newSelection;
1295
+ }
1296
+
1297
+ _selectElementspec(e) {
1298
+ if (
1299
+ this.currentElementSpec &&
1300
+ this.currentElementSpec.tagName === 'PB-ODD-ELEMENTSPEC-EDITOR'
1301
+ ) {
1302
+ this.currentElementSpec.removeAttribute('currentselection');
1074
1303
  }
1075
-
1304
+ const newSelection = e.target;
1305
+ newSelection.setAttribute('currentselection', 'true');
1306
+ this.currentElementSpec = newSelection;
1307
+ }
1308
+
1309
+ nsDisabled() {
1310
+ return !this.useNamespace;
1311
+ }
1312
+
1313
+ _handleLoadError(e) {
1314
+ console.log('loading error occurred: ', e);
1315
+ const msg = this.shadowRoot.getElementById('errorMsg');
1316
+ msg.style.background = 'red';
1317
+ const { url } = this.shadowRoot.getElementById('loadContent');
1318
+ console.log('url ', url);
1319
+ msg.show('Error: ', `ODD file could not be loaded from ${url}`);
1320
+ }
1321
+
1322
+ handleElementSpecChanged(e) {
1323
+ // console.log('handleElementSpecChanged ',e);
1324
+ this._hasChanges = true;
1325
+ const elementSpec = this.elementSpecs.find(es => es.ident === e.detail.ident);
1326
+ const index = this.elementSpecs.indexOf(elementSpec);
1327
+ const newSpec = { ...elementSpec, models: e.detail.models };
1328
+ const allSpecs = Array.from(this.elementSpecs);
1329
+ allSpecs.splice(index, 1, newSpec);
1330
+ this.elementSpecs = allSpecs;
1331
+ // console.log('updated elementspecs ', this.elementSpecs);
1332
+ }
1333
+
1334
+ _inputTitle(ev) {
1335
+ this.title = ev.target.value;
1336
+ }
1076
1337
  }
1077
1338
 
1078
1339
  customElements.define('pb-odd-editor', PbOddEditor);