@silexlabs/silex 3.0.0-alpha.21

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 (106) hide show
  1. package/.dockerignore +5 -0
  2. package/.editorconfig +14 -0
  3. package/.eslintrc.json +43 -0
  4. package/.github/FUNDING.yml +13 -0
  5. package/.github/workflows/docker-hub-publish.yml +36 -0
  6. package/.github/workflows/npm-publish.yml +19 -0
  7. package/.github/workflows/test.yml +26 -0
  8. package/.nvmrc +1 -0
  9. package/.silex.js +19 -0
  10. package/CODE_OF_CONDUCT.md +128 -0
  11. package/CONTRIBUTING.md +108 -0
  12. package/Dockerfile +15 -0
  13. package/LICENSE +203 -0
  14. package/app.json +106 -0
  15. package/directus.silex.js +7 -0
  16. package/dist/client/css/admin.css +4350 -0
  17. package/dist/client/index.html +9 -0
  18. package/dist/client/js/main.js +282 -0
  19. package/dist/client/js/main.js.LICENSE.txt +52 -0
  20. package/dist/server/config.js +50 -0
  21. package/dist/server/events.js +5 -0
  22. package/dist/server/index.js +29 -0
  23. package/dist/server/start.js +18 -0
  24. package/jest.config.js +10 -0
  25. package/logos/logo_by_ceubri/silex-icon-2018@200px.png +0 -0
  26. package/logos/logo_by_ceubri/silex-icon-2018@4x.png +0 -0
  27. package/logos/logo_by_ceubri/silex-logo-2018-white-transp.pdf +0 -0
  28. package/logos/logo_by_ceubri/silex-logo-2018-white.jpg +0 -0
  29. package/logos/logo_by_ceubri/silex-logo-2018-white.pdf +0 -0
  30. package/logos/logo_by_ceubri/silex-logo-2018-white.png +0 -0
  31. package/logos/logo_by_ceubri/silex-logo-2018-white@0.5x.png +0 -0
  32. package/logos/logo_by_ceubri/silex-logo-2018-white@0.75x.png +0 -0
  33. package/logos/logo_by_ceubri/silex-logo-2018-white@4x.jpg +0 -0
  34. package/logos/logo_by_ceubri/silex-logo-2018-white@4x.png +0 -0
  35. package/logos/logo_by_ceubri/silex-logo-2018.ai +2520 -6
  36. package/logos/logo_by_ceubri/silex-logo-2018.jpg +0 -0
  37. package/logos/logo_by_ceubri/silex-logo-2018.pdf +0 -0
  38. package/logos/logo_by_ceubri/silex-logo-2018.png +0 -0
  39. package/logos/logo_by_ceubri/silex-logo-2018@0.5x.png +0 -0
  40. package/logos/logo_by_ceubri/silex-logo-2018@0.75x.png +0 -0
  41. package/logos/logo_by_ceubri/silex-logo-2018@4x.jpg +0 -0
  42. package/logos/logo_by_ceubri/silex-logo-2018@4x.png +0 -0
  43. package/logos/silexlabs.png +0 -0
  44. package/package.json +170 -0
  45. package/public/assets/container.png +0 -0
  46. package/public/assets/favicon.png +0 -0
  47. package/public/assets/logo-silex-small.png +0 -0
  48. package/public/example-config.js +31 -0
  49. package/public/robots.txt +2 -0
  50. package/readme.md +51 -0
  51. package/src/css/_mixins.scss +8 -0
  52. package/src/css/_variables.scss +33 -0
  53. package/src/css/dialog.scss +34 -0
  54. package/src/css/grapesjs.scss +44 -0
  55. package/src/css/index.scss +22 -0
  56. package/src/css/loader.scss +10 -0
  57. package/src/css/page-panel.scss +65 -0
  58. package/src/css/project-bar.scss +102 -0
  59. package/src/css/silex.scss +183 -0
  60. package/src/html/debug.pug +8 -0
  61. package/src/html/index.pug +39 -0
  62. package/src/html/templates/includes.pug +2 -0
  63. package/src/html/templates/workspace.pug +2 -0
  64. package/src/plugins/AuthPlugin.js +21 -0
  65. package/src/plugins/Cache.js +11 -0
  66. package/src/plugins/DefaultBackend.js +113 -0
  67. package/src/plugins/ExpressPlugin.js +30 -0
  68. package/src/plugins/HooksPlugin.js +58 -0
  69. package/src/plugins/PublishPlugin.js +69 -0
  70. package/src/plugins/SslPlugin.js +66 -0
  71. package/src/plugins/StaticPlugin.js +48 -0
  72. package/src/plugins/WebsitePlugin.js +149 -0
  73. package/src/ts/client/config.ts +21 -0
  74. package/src/ts/client/expose.ts +4 -0
  75. package/src/ts/client/grapesjs/blocks.ts +88 -0
  76. package/src/ts/client/grapesjs/eleventy.ts +21 -0
  77. package/src/ts/client/grapesjs/index.ts +255 -0
  78. package/src/ts/client/grapesjs/internal-links.test.ts +87 -0
  79. package/src/ts/client/grapesjs/internal-links.ts +210 -0
  80. package/src/ts/client/grapesjs/new-page-dialog.ts +73 -0
  81. package/src/ts/client/grapesjs/openImport.ts +59 -0
  82. package/src/ts/client/grapesjs/page-panel.ts +101 -0
  83. package/src/ts/client/grapesjs/project-bar.ts +92 -0
  84. package/src/ts/client/grapesjs/publish.ts +386 -0
  85. package/src/ts/client/grapesjs/rich-text.ts +16 -0
  86. package/src/ts/client/grapesjs/semantic.ts +96 -0
  87. package/src/ts/client/grapesjs/settings.ts +190 -0
  88. package/src/ts/client/grapesjs/symbolDialogs.ts +21 -0
  89. package/src/ts/client/grapesjs/template.ts +166 -0
  90. package/src/ts/client/index.ts +38 -0
  91. package/src/ts/client/utils.ts +9 -0
  92. package/src/ts/externs.ts +1 -0
  93. package/src/ts/page.ts +14 -0
  94. package/src/ts/server/cli.ts +23 -0
  95. package/src/ts/server/config.ts +62 -0
  96. package/src/ts/server/events.ts +2 -0
  97. package/src/ts/server/index.ts +35 -0
  98. package/src/ts/server/start.ts +18 -0
  99. package/src/ts/server/utils/BackwardCompat.ts +534 -0
  100. package/src/ts/types.ts +138 -0
  101. package/tsconfig-server-debug.json +20 -0
  102. package/tsconfig-server-release.json +19 -0
  103. package/tsconfig.jest.json +9 -0
  104. package/tsconfig.json +27 -0
  105. package/webpack.config.js +29 -0
  106. package/webpack.debug.config.js +8 -0
@@ -0,0 +1,534 @@
1
+ /**
2
+ * @fileoverview Handle backward compatibility when a user opens a site for edition
3
+ *
4
+ */
5
+
6
+ import * as Path from 'path'
7
+ import * as fs from 'fs'
8
+
9
+ import { Constants } from '../../constants'
10
+ import {
11
+ ElementData,
12
+ ElementState,
13
+ ElementType,
14
+ Link,
15
+ LinkType
16
+ } from '../../client/element-store/types'
17
+ import { PageData } from '../../client/page-store/types'
18
+ import { PersistantData } from '../../client/store/types'
19
+ import {
20
+ cleanupBefore,
21
+ getElementsFromDomBC,
22
+ getPagesFromDom,
23
+ getSiteFromDom,
24
+ writeSiteStyles,
25
+ writeStyles
26
+ } from './BackwardCompatV2.5.60'
27
+ import { getDomElement } from '../../client/element-store/dom'
28
+ import { setTagName } from '../../client/utils/dom'
29
+ import { writeDataToDom } from '../../client/store/dom'
30
+
31
+ /**
32
+ * util function for the "fixes", @see fixes
33
+ */
34
+ function updateLinks<T extends { link?: Link }>(items: T[]): T[] {
35
+ return items
36
+ .map((item: T) => {
37
+ if(!!item.link && !item.link.linkType) {
38
+ // add new props
39
+ const link: any = {
40
+ ...item.link,
41
+ linkType: item.link.type as LinkType,
42
+ href: (item.link as any).value,
43
+ }
44
+ // remove old props
45
+ delete link.type
46
+ delete link.value
47
+ // return the updated element
48
+ return {
49
+ ...item,
50
+ link,
51
+ }
52
+ } else {
53
+ return item
54
+ }
55
+ })
56
+ }
57
+
58
+ /**
59
+ * class name for containers which are created with sections
60
+ */
61
+ const SECTION_CONTAINER = 'silex-container-content'
62
+
63
+ export default class BackwardCompat {
64
+ private data: PersistantData = null
65
+ private frontEndVersion: string[]
66
+ private silexVersion: number[]
67
+
68
+ constructor(private rootUrl: string, rootPath = __dirname + '/../../../..') {
69
+ /**
70
+ * the version of the website is stored in the generator tag as "Silex v-X-Y-Z"
71
+ * we get it from package.json
72
+ * used for backward compat and for the static files URLs taken from //{{host}}/static/{{Y-Z}}
73
+ */
74
+ const packageJson = JSON.parse(fs.readFileSync(Path.resolve(rootPath, 'package.json')).toString())
75
+ this.frontEndVersion = packageJson['version:frontend'].split('.').map((s) => parseInt(s))
76
+ this.silexVersion = packageJson['version:backwardcompat'].split('.').map((s) => parseInt(s))
77
+
78
+ // const components = require('../../../dist/client/libs/prodotype/components/components.json')
79
+ console.log(`\nStarting. Version is ${this.silexVersion} for the websites and ${this.frontEndVersion} for Silex\n`)
80
+ }
81
+
82
+ // remove all tags
83
+ // export for tests
84
+ removeIfExist(doc: HTMLDocument, selector: string) {
85
+ Array.from(doc.querySelectorAll(selector))
86
+ .forEach((tag) => tag.remove())
87
+ }
88
+
89
+ // remove all useless css class
90
+ // export for tests
91
+ removeUselessCSSClass(doc: HTMLDocument, className: string) {
92
+ Array.from(doc.querySelectorAll('.' + className))
93
+ .forEach((el) => el.classList.remove(className))
94
+ }
95
+
96
+ getVersion(doc): number[] {
97
+ // if no generator tag, create one
98
+ let metaNode = doc.querySelector('meta[name="generator"]')
99
+ if (!metaNode) {
100
+ metaNode = doc.createElement('meta')
101
+ metaNode.setAttribute('name', 'generator')
102
+ doc.head.appendChild(metaNode)
103
+ }
104
+ // retrieve the website version from generator tag
105
+ return (metaNode.getAttribute('content') || '')
106
+ .replace('Silex v', '')
107
+ .split('.')
108
+ .map((str) => parseInt(str, 10) || 0)
109
+ }
110
+
111
+ hasDataFile(doc): boolean {
112
+ return !this.hasToUpdate(this.getVersion(doc), [2, 2, 11])
113
+ }
114
+
115
+ /**
116
+ * handle backward compatibility issues
117
+ * Backwardcompatibility process takes place after opening a file
118
+ * @param {Document} doc
119
+ * @return {Promise} a Promise, resolve can be called with a warning message
120
+ */
121
+ async update(doc: HTMLDocument, data: PersistantData): Promise<[string, PersistantData]> {
122
+ // // fix an issue when the style tag has no type, then json is "broken"
123
+ // const styleTag = doc.querySelector('.' + Constants.JSON_STYLE_TAG_CLASS_NAME);
124
+ // if (styleTag) { styleTag.type = 'text/json'; } // old versions of silex have no json at all so do nothing in that case
125
+ // TODO: move this to the data model (e.g. data.site.silexVersion)
126
+
127
+ // we need this.data as to2_2_11 will extract it and set it from the dom
128
+ this.data = data
129
+
130
+ const version = this.getVersion(doc)
131
+ const hasToUpdate = this.hasToUpdate(version, this.silexVersion)
132
+
133
+ // warn the user
134
+ if (this.amIObsolete(version, this.silexVersion)) {
135
+ return ['This website has been saved with a newer version of Silex. Continue at your own risks.', this.data]
136
+ } else if (this.hasToUpdate(version, [2, 2, 7])) {
137
+ return Promise.reject({
138
+ message: 'This website has been saved with an older version of Silex, which is not supported anymore as of March 2018. In order to convert it to a newer version, please go to <a href="https://old.silex.me">old.silex.me</a> to open and then save your website. <a href="https://github.com/silexlabs/Silex/wiki/Website-saved-with-older-version-of-Silex">More about this here</a>',
139
+ })
140
+ } else if (hasToUpdate) {
141
+ // convert to the latest version
142
+ const allActions = {
143
+ '2.2.8': await this.to2_2_8(version, doc),
144
+ '2.2.9': await this.to2_2_9(version, doc),
145
+ '2.2.10': await this.to2_2_10(version, doc),
146
+ '2.2.11': await this.to2_2_11(version, doc), // this will set this.data
147
+ '2.2.12': await this.to2_2_12(version, doc),
148
+ '2.2.13': await this.to2_2_13(version, doc),
149
+ '2.2.14': await this.to2_2_14(version, doc),
150
+ }
151
+ // update the static scripts to match the current server and latest version
152
+ this.updateStatic(doc)
153
+ // store the latest version
154
+ const metaNode = doc.querySelector('meta[name="generator"]')
155
+ metaNode.setAttribute('content', 'Silex v' + this.silexVersion.join('.'))
156
+ // apply all-time fixes
157
+ this.fixes(doc)
158
+ // build the report for the user
159
+ const report = Object.keys(allActions)
160
+ .filter((_version) => allActions[_version].length > 0)
161
+ .map((_version) => {
162
+ return `<p>Update to version ${ _version }:
163
+ <ul>${ allActions[_version].map((_action) => `<li class="no-list">${ _action }</li>`).join('') }</ul>
164
+ </p>`
165
+ }).join('')
166
+ // save data to dom for front-end.js and other scripts
167
+ // in case data has been changed
168
+ // FIXME: should not have this.data mutated but returned by update scripts
169
+ writeDataToDom(doc, this.data)
170
+ // needs to reload if silex scripts and stylesheets have been updated
171
+ return [`
172
+ <p>This website has been updated to Silex latest version.</p>
173
+ <p>Before you save it, please check that everything is fine. Saving it with another name could be a good idea too (menu file > save as).</p>
174
+ <details>
175
+ <summary>Details</summary>
176
+ <small>
177
+ ${ report }
178
+ </small>
179
+ </details>
180
+ `,
181
+ this.data,
182
+ ]
183
+ } else {
184
+ // update the static scripts to match the current server URL
185
+ this.updateStatic(doc)
186
+ // apply all-time fixes
187
+ this.fixes(doc)
188
+ // resolve immediately
189
+ return ['', this.data]
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Check for common errors in editable html files
195
+ */
196
+ fixes(doc: HTMLDocument) {
197
+ // const pages: HTMLElement[] = Array.from(doc.querySelectorAll(`.${Constants.PAGES_CONTAINER_CLASS_NAME} a[${Constants.TYPE_ATTR}="page"]`));
198
+ // if (pages.length > 0) {
199
+ // console.log('Fix error of wrong silex type for', pages.length, 'pages');
200
+ // pages.forEach((page) => page.setAttribute(Constants.TYPE_ATTR, Constants.TYPE_PAGE));
201
+ // }
202
+
203
+ // the following is a fix following the beta version of 07-2020
204
+ // for elements and pages:
205
+ // link.value becomes href
206
+ // link.type becomes linkType
207
+ this.data = {
208
+ site: this.data.site,
209
+ pages: updateLinks<PageData>(this.data.pages),
210
+ elements: updateLinks<ElementData>(this.data.elements),
211
+ }
212
+ // remove juery-ui at publication, in case the website has been updated before the fix of 2.6.2
213
+ Array.from(doc.querySelectorAll('script[src$="pageable.js"], script[src$="jquery-ui.js"]'))
214
+ .forEach((tag) => tag.setAttribute(Constants.ATTR_REMOVE_PUBLISH, ''))
215
+ // this does not work because sections can not be smaller than their content:
216
+ // // resizable sections
217
+ // this.data = {
218
+ // ...this.data,
219
+ // elements: this.data.elements
220
+ // .map((el) => el.type === ElementType.SECTION ? {
221
+ // ...el,
222
+ // enableResize: {
223
+ // bottom: true,
224
+ // top: true,
225
+ // left: false,
226
+ // right: false,
227
+ // }
228
+ // } : el),
229
+ // }
230
+
231
+ // remove pages which do not exist (was caused by a bug in deletePages, fix 2021-03)
232
+ // also remove css classes which are pages (was caused by a bug??)
233
+ this.data.elements = this.data.elements.map(el => ({
234
+ ...el,
235
+ pageNames: el.pageNames.filter(name => this.data.pages.some(p => p.id === name)),
236
+ classList: el.classList.filter(name => !this.data.pages.some(p => p.id === name)),
237
+ }))
238
+ }
239
+
240
+ /**
241
+ * update the static scripts to match the current server and latest version
242
+ */
243
+ updateStatic(doc: HTMLDocument) {
244
+ // update //{{host}}/2.x/... to latest version
245
+ Array.from(doc.querySelectorAll('[' + Constants.STATIC_ASSET_ATTR + ']'))
246
+ .forEach((element: HTMLElement) => {
247
+ const propName = element.hasAttribute('src') ? 'src' : 'href'
248
+ if (element.hasAttribute(propName)) {
249
+ const newUrl = this.getStaticResourceUrl(element[propName])
250
+ const oldUrl = element.getAttribute(propName)
251
+ if (oldUrl !== newUrl) {
252
+ element.setAttribute(propName, newUrl)
253
+ }
254
+ }
255
+ })
256
+ }
257
+
258
+ /**
259
+ * get the complete URL for the static file,
260
+ * * on the current Silex server
261
+ * * with the latest Silex version
262
+ *
263
+ * this will result in a URL on the current server, in the `/static/` folder
264
+ *
265
+ * @example `//localhost:6805/static/2.1/example/example.js` returns `//editor.silex.me/static/2.7/example/unslider.js`
266
+ * @example `/static/2.1/example/example.js` returns `//editor.silex.me/static/2.7/example/example.js`
267
+ *
268
+ * with the current version
269
+ * @param {string} url
270
+ * @return {string}
271
+ */
272
+ getStaticResourceUrl(url: string): string {
273
+ const pathRelativeToStaticMatch = url.match(/static\/[0-9]*\.[0-9]*\/(.*)/)
274
+ if (pathRelativeToStaticMatch == null) {
275
+ console.warn('Error: could not extract the path and file name of static asset', url)
276
+ return url
277
+ }
278
+ const pathRelativeToStatic = pathRelativeToStaticMatch[1]
279
+ return `${ this.rootUrl }/static/${ this.frontEndVersion[0] }.${ this.frontEndVersion[1] }/${ pathRelativeToStatic }`
280
+ }
281
+
282
+ /**
283
+ * check if the website has been edited with a newer version of Silex
284
+ * @param {Array.<number>} initialVersion the website version
285
+ * @param {Array.<number>} targetVersion a given Silex version
286
+ * @return {boolean}
287
+ */
288
+ amIObsolete(initialVersion: number[], targetVersion: number[]): boolean {
289
+ return !!initialVersion[2] && initialVersion[0] > targetVersion[0] ||
290
+ initialVersion[1] > targetVersion[1] ||
291
+ initialVersion[2] > targetVersion[2]
292
+ }
293
+
294
+ /**
295
+ * check if the website has to be updated for the given version of Silex
296
+ * @param {Array.<number>} initialVersion the website version
297
+ * @param {Array.<number>} targetVersion a given Silex version
298
+ * @return {boolean}
299
+ */
300
+ hasToUpdate(initialVersion: number[], targetVersion: number[]): boolean {
301
+ return initialVersion[0] < targetVersion[0] ||
302
+ initialVersion[1] < targetVersion[1] ||
303
+ initialVersion[2] < targetVersion[2]
304
+ }
305
+
306
+ to2_2_8(version: number[], doc: HTMLDocument): Promise<string[]> {
307
+ return new Promise((resolve, reject) => {
308
+ const actions = []
309
+ if (this.hasToUpdate(version, [2, 2, 8])) {
310
+ // cleanup the hamburger menu icon
311
+ const menuButton = doc.querySelector('.menu-button')
312
+ if (menuButton) {
313
+ menuButton.classList.remove('paged-element', 'paged-element-hidden', 'page-page-1', 'prevent-resizable')
314
+ menuButton.classList.add('hide-on-desktop')
315
+ }
316
+ // give the hamburger menu a size (TODO: add to the json model too)
317
+ doc.querySelector('.silex-inline-styles').innerHTML += '.silex-id-hamburger-menu {width: 50px;min-height: 40px;}'
318
+ // pages need to have href set
319
+ Array.from(doc.querySelectorAll('.page-element'))
320
+ .forEach((el: HTMLLinkElement) => {
321
+ el.setAttribute('href', '#!' + el.getAttribute('id'))
322
+ })
323
+ actions.push('I fixed the mobile menu so that it is compatible with the new publication (now multiple pages are generated instead of 1 single page for the whole website).')
324
+ }
325
+ resolve(actions)
326
+ })
327
+ }
328
+
329
+ to2_2_9(version: number[], doc: HTMLDocument): Promise<string[]> {
330
+ return new Promise((resolve, reject) => {
331
+ const actions = []
332
+ if (this.hasToUpdate(version, [2, 2, 9])) {
333
+ // remove the hamburger menu icon
334
+ const menuButton = doc.querySelector('.menu-button')
335
+ if (menuButton) {
336
+ menuButton.parentElement.removeChild(menuButton)
337
+ actions.push(
338
+ 'I removed the mobile menu as there is now a component for that. <a target="_blank" href="https://github.com/silexlabs/Silex/wiki/Hamburger-menu">Read more about the Hamburger Menu component here</a>.',
339
+ )
340
+ }
341
+ }
342
+ resolve(actions)
343
+ })
344
+ }
345
+
346
+ to2_2_10(version: number[], doc: HTMLDocument): Promise<string[]> {
347
+ return new Promise((resolve, reject) => {
348
+ const actions = []
349
+ if (this.hasToUpdate(version, [2, 2, 10])) {
350
+ // the body is a drop zone, not selectable, not draggable, resizeable
351
+ doc.body.classList.add(
352
+ Constants.PREVENT_DRAGGABLE_CLASS_NAME,
353
+ Constants.PREVENT_RESIZABLE_CLASS_NAME,
354
+ Constants.PREVENT_SELECTABLE_CLASS_NAME)
355
+
356
+ // each section background and foreground is a drop zone, not selectable, not draggable, resizeable
357
+ const changedSections = Array.from(doc.querySelectorAll(`.${ElementType.SECTION}`)) as HTMLElement[]
358
+ changedSections.forEach((el: HTMLElement) => el.classList.add(
359
+ Constants.PREVENT_DRAGGABLE_CLASS_NAME,
360
+ Constants.PREVENT_RESIZABLE_CLASS_NAME,
361
+ ))
362
+
363
+ // we add classes to the elements so that we can tell the stage component if an element is draggable, resizeable, selectable...
364
+ const changedSectionsContent = Array.from(doc.querySelectorAll(`.${ElementType.SECTION}, .${ElementType.SECTION} .${SECTION_CONTAINER}`))
365
+ changedSectionsContent.forEach((el: HTMLElement) => el.classList.add(
366
+ Constants.PREVENT_DRAGGABLE_CLASS_NAME,
367
+ // Constants.PREVENT_RESIZABLE_LEFT_CLASS_NAME,
368
+ // Constants.PREVENT_RESIZABLE_RIGHT_CLASS_NAME
369
+ ))
370
+ actions.push(`Changed the body and ${changedSections.length} sections with new CSS classes to <a href="https://github.com/silexlabs/stage/" target="_blank">the new stage component.</a>`)
371
+
372
+ // types are now with a "-element" suffix
373
+ const changedElements = Array.from(doc.querySelectorAll(`[${Constants.TYPE_ATTR}]`))
374
+ changedElements.forEach((el: HTMLElement) => el.setAttribute(Constants.TYPE_ATTR, el.getAttribute(Constants.TYPE_ATTR) + '-element'))
375
+
376
+ actions.push(`Updated ${ changedElements.length } elements, changed their types to match the new version of Silex.`)
377
+ }
378
+ resolve(actions)
379
+ })
380
+ }
381
+
382
+ to2_2_11(version: number[], doc: HTMLDocument): Promise<string[]> {
383
+ return new Promise((resolve, reject) => {
384
+ const actions = []
385
+ if (this.hasToUpdate(version, [2, 2, 11])) {
386
+ // the body is supposed to be an element too
387
+ doc.body.classList.add(Constants.EDITABLE_CLASS_NAME)
388
+ actions.push('I made the body editable.')
389
+
390
+ const oldBodyId = doc.body.getAttribute('data-silex-id')
391
+ if (oldBodyId !== 'body-initial') {
392
+ actions.push('I udpated the body class name from "' + oldBodyId + '" to "body-initial".')
393
+ doc.body.setAttribute('data-silex-id', 'body-initial')
394
+ doc.body.classList.remove(oldBodyId)
395
+ doc.body.classList.add('body-initial')
396
+ }
397
+
398
+ // prepare the dom
399
+ cleanupBefore(doc)
400
+
401
+ // import elements
402
+ const elements = getElementsFromDomBC(doc)
403
+ writeStyles(doc, elements)
404
+
405
+ // site
406
+ const site = getSiteFromDom(doc)
407
+ writeSiteStyles(doc, site)
408
+
409
+ // pages
410
+ const pages = getPagesFromDom(doc)
411
+
412
+ if (elements.length && pages.length && site) {
413
+ this.data = {
414
+ site,
415
+ pages,
416
+ elements,
417
+ }
418
+ this.removeIfExist(doc, 'meta[name="website-width"]')
419
+ this.removeIfExist(doc, 'meta[name="hostingProvider"]')
420
+ this.removeIfExist(doc, 'meta[name="publicationPath"]')
421
+
422
+ // remove juery-ui at publication
423
+ Array.from(doc.querySelectorAll('script[src$="pageable.js"], script[src$="jquery-ui.js"]'))
424
+ .forEach((tag) => tag.setAttribute(Constants.ATTR_REMOVE_PUBLISH, ''))
425
+
426
+ ;['prevent-draggable', SECTION_CONTAINER].forEach((className) => this.removeUselessCSSClass(doc, className))
427
+
428
+ actions.push('I updated the model to the latest version of Silex.')
429
+ // pages
430
+ this.removeIfExist(doc, `.${Constants.PAGES_CONTAINER_CLASS_NAME}`)
431
+ actions.push('I removed the old pages system.')
432
+ } else {
433
+ console.error('Could not import site from v2.2.11', {elements, pages, site})
434
+ }
435
+ }
436
+ resolve(actions)
437
+ })
438
+ }
439
+
440
+ to2_2_12(version: number[], doc: HTMLDocument): Promise<string[]> {
441
+ return new Promise((resolve, reject) => {
442
+ const actions = []
443
+ if (this.hasToUpdate(version, [2, 2, 12])) {
444
+ // add empty metadata to the website object
445
+ this.data = {
446
+ ...this.data,
447
+ site: {
448
+ ...this.data.site,
449
+ data: {},
450
+ }
451
+ }
452
+ actions.push('add empty metadata to the website object')
453
+ // sections are now section tag
454
+ const changedSections = Array.from(doc.querySelectorAll(`.${ElementType.SECTION}`)) as HTMLElement[]
455
+ changedSections.forEach((el: HTMLElement) => setTagName(el, 'section'))
456
+ this.data = {
457
+ ...this.data,
458
+ elements: this.data.elements
459
+ .map((el) => ({
460
+ ...el,
461
+ tagName: getDomElement(doc, el as ElementState) ? getDomElement(doc, el as ElementState).tagName : 'DIV',
462
+ }))
463
+ }
464
+ actions.push('All sections have a &lt;SECTION&gt; tag name')
465
+ }
466
+ resolve(actions)
467
+ })
468
+ }
469
+
470
+ to2_2_13(version: number[], doc: HTMLDocument): Promise<string[]> {
471
+ return new Promise((resolve, reject) => {
472
+ const actions = []
473
+ if (this.hasToUpdate(version, [2, 2, 13])) {
474
+ Array.from(doc.querySelectorAll('[data-silex-href]'))
475
+ .forEach((tag: HTMLElement) => {
476
+ const newEl = setTagName(tag, 'A') as HTMLLinkElement
477
+ newEl.href = tag.getAttribute('data-silex-href')
478
+ newEl.removeAttribute('data-silex-href')
479
+ })
480
+ actions.push('Replaced old Silex links with standard HTML links.')
481
+ }
482
+ resolve(actions)
483
+ })
484
+ }
485
+
486
+ to2_2_14(version: number[], doc: HTMLDocument): Promise<string[]> {
487
+ return new Promise((resolve, reject) => {
488
+ const actions = []
489
+ if (this.hasToUpdate(version, [2, 2, 14])) {
490
+ // do not fix this: remove w3c warning "The type attribute is unnecessary for JavaScript resources"
491
+ // because silex uses script type attr in WebsiteRouter
492
+ // instead we remove the type at publication time in DomPublisher
493
+ // remove w3c warning "The type attribute for the style element is not needed and should be omitted."
494
+ const styles = Array.from(doc.querySelectorAll('style[type="text/css"]'))
495
+ if(styles.length) {
496
+ styles.forEach(el => el.removeAttribute('type'))
497
+ actions.push(`Fixed ${styles.length} w3c warning 'The type attribute for the style element is not needed and should be omitted.'`)
498
+ }
499
+ // This should not be useful (previous BC bug?)
500
+ // Remove empty attributes
501
+ // Sometimes we have style="", title=""...
502
+ let numEmpty = 0
503
+ const attrs = [
504
+ 'href',
505
+ 'style',
506
+ 'rel',
507
+ 'target',
508
+ 'type',
509
+ 'title',
510
+ ]
511
+ attrs.forEach((attr: string) => {
512
+ Array.from(doc.querySelectorAll(`[${attr}]`))
513
+ .forEach(el => {
514
+ if(el.hasAttribute(attr) && el.getAttribute(attr) === '') {
515
+ el.removeAttribute(attr)
516
+ numEmpty++
517
+ }
518
+ })
519
+ })
520
+ if(numEmpty) actions.push(`Removed ${numEmpty} empty attributes (${attrs.join(', ')})`)
521
+
522
+ // This should not be useful (previous BC bug?)
523
+ // Remove the href attribute from non <a> tags
524
+ // Sometimes we have href="null" on non a tags
525
+ const tagsWithWrongHref = Array.from(doc.querySelectorAll('[href]:not(a, link, base)'))
526
+ tagsWithWrongHref.forEach(el => {
527
+ el.removeAttribute('href')
528
+ })
529
+ if(tagsWithWrongHref.length) actions.push(`Removed ${tagsWithWrongHref.length} href attributes on non links tags`)
530
+ }
531
+ resolve(actions)
532
+ })
533
+ }
534
+ }
@@ -0,0 +1,138 @@
1
+ /**
2
+ * @fileoverview define types for Silex client and server
3
+ */
4
+
5
+ export const WEBSITE_CONTEXT_RUNTIME_CLASS_NAME = 'silex-runtime'
6
+ export const WEBSITE_CONTEXT_EDITOR_CLASS_NAME = 'silex-editor'
7
+
8
+ // Note: paths begin and end **without** slash
9
+ export const defaultSettings: PublicationSettings = {
10
+ path: 'publication',
11
+ url: '',
12
+ autoHomePage: true,
13
+ assets: { path: 'assets', url: '/assets' },
14
+ html: { path: '' },
15
+ css: { path: 'css', url: '/css' },
16
+ }
17
+
18
+ export const defaultSite: WebsiteData = {
19
+ pages: [],
20
+ assets: [],
21
+ styles: [],
22
+ name: 'New website',
23
+ settings: {
24
+ description: '',
25
+ title: '',
26
+ head: '',
27
+ lang: 'en',
28
+ favicon: 'https://editor.silex.me/assets/favicon.png',
29
+ 'og:title': '',
30
+ 'og:description': '',
31
+ 'og:image': '',
32
+ },
33
+ fonts: [
34
+ { name: 'Arial', value: 'Arial, Helvetica, sans-serif', variants: [] },
35
+ { name: 'Times New Roman', value: '"Times New Roman", Times, serif', variants: [] },
36
+ ],
37
+ symbols: [],
38
+ publication: defaultSettings,
39
+ }
40
+
41
+ export interface WebsiteSettings {
42
+ description: string,
43
+ title: string,
44
+ lang: string,
45
+ head: string,
46
+ favicon: string,
47
+ 'og:title': string,
48
+ 'og:description': string,
49
+ 'og:image': string,
50
+ }
51
+
52
+ export interface WebsiteData {
53
+ pages: Page[],
54
+ assets: Asset[],
55
+ styles: Style[],
56
+ name: string,
57
+ settings: WebsiteSettings,
58
+ fonts: Font[],
59
+ symbols: symbol[],
60
+ publication: PublicationSettings,
61
+ }
62
+
63
+ export interface Font {
64
+ name: string,
65
+ value: string,
66
+ variants: string[],
67
+ }
68
+ export interface PublicationSettings {
69
+ path?: string, // Folder to publish to
70
+ url?: string, // URL to display where the website is published to
71
+ autoHomePage?: boolean, // Name the first page `index` instead of its name
72
+ assets?: {
73
+ path?: string, // Folder to copy assets to
74
+ url?: string, // URL where assets are accessed
75
+ },
76
+ html?: {
77
+ path?: string, // Folder where to generate the HTML pages
78
+ ext?: string, // File extension for HTML pages
79
+ },
80
+ css?: {
81
+ path?: string, // Folder where to generate the CSS files
82
+ url?: string, // URL of the Folder where the CSS files are accessed
83
+ ext?: string, // File extension for CSS files
84
+ },
85
+ }
86
+
87
+ export interface File {
88
+ html: string,
89
+ css: string,
90
+ htmlPath: string,
91
+ cssPath: string,
92
+ }
93
+
94
+ export interface Page {
95
+ name?: string,
96
+ id: string,
97
+ type?: string,
98
+ frames: Frame[],
99
+ settings?: WebsiteSettings,
100
+ cssExt?: string,
101
+ htmlExt?: string,
102
+ }
103
+
104
+ export interface Frame {
105
+ component: { type: string, stylable: string[] },
106
+ components: Component[],
107
+ }
108
+
109
+ export interface Component {
110
+ type: string,
111
+ content?: string,
112
+ attributes: { [key: string]: string },
113
+ conponents: Component[],
114
+ }
115
+
116
+ export enum Unit {
117
+ PX = 'px',
118
+ }
119
+
120
+ export interface Asset {
121
+ type: string,
122
+ src: string,
123
+ unitDim: Unit,
124
+ height: number,
125
+ width: number,
126
+ name: string,
127
+ }
128
+
129
+ export interface Style {
130
+ selectors: Selector[],
131
+ style: { [key: string]: string },
132
+ }
133
+
134
+ export type Selector = string | {
135
+ name: string,
136
+ type: number,
137
+ }
138
+
@@ -0,0 +1,20 @@
1
+ {
2
+ "compileOnSave": true,
3
+ "compilerOptions": {
4
+ "outDir": "dist/server/",
5
+ "target": "esnext",
6
+ "watch": false,
7
+ "inlineSourceMap": true,
8
+ "module": "commonjs",
9
+ "removeComments": false,
10
+ "esModuleInterop": true, /* for import 'Events' in silex-plugins */
11
+ "typeRoots": [
12
+ "src/ts/types",
13
+ "node_modules/@types"
14
+ ]
15
+ },
16
+ "include": [
17
+ "src/ts/server/index.ts",
18
+ "src/ts/server/cli.ts"
19
+ ]
20
+ }