neo.mjs 6.25.0 → 6.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='6.25.0'
23
+ * @member {String} version='6.27.0'
24
24
  */
25
- version: '6.25.0'
25
+ version: '6.27.0'
26
26
  }
27
27
 
28
28
  /**
@@ -16,7 +16,7 @@
16
16
  "@type": "Organization",
17
17
  "name": "Neo.mjs"
18
18
  },
19
- "datePublished": "2024-07-27",
19
+ "datePublished": "2024-08-01",
20
20
  "publisher": {
21
21
  "@type": "Organization",
22
22
  "name": "Neo.mjs"
@@ -31,6 +31,15 @@ class Container extends Base {
31
31
  profileLinkedIn: 'https://www.linkedin.com/in/tobiasuhlig/',
32
32
  profileX : 'https://x.com/UhligTobias',
33
33
  teamRole : 'Co-Founder & Core Team Member'
34
+ }, {
35
+ module : MemberContainer,
36
+ location : 'Florida, USA',
37
+ name : 'Rich Waters',
38
+ picture : 'rwaters.png',
39
+ profileGitHub : 'https://github.com/rwaters',
40
+ profileLinkedIn: 'https://www.linkedin.com/in/rwaters/',
41
+ profileX : 'https://x.com/rwaters',
42
+ teamRole : 'Co-Founder & Core Team Member'
34
43
  }, {
35
44
  module : MemberContainer,
36
45
  location : 'Germany',
@@ -1,8 +1,5 @@
1
1
  import BaseContainer from './BaseContainer.mjs';
2
2
  import ContentBox from '../ContentBox.mjs';
3
- import Component from '../../../../../src/component/Base.mjs';
4
- import Container from '../../../../../src/container/Base.mjs';
5
-
6
3
 
7
4
  /**
8
5
  * @class Portal.view.home.parts.Features
@@ -10,56 +7,92 @@ import Container from '../../../../../src/container/Base.mjs';
10
7
  */
11
8
  class Features extends BaseContainer {
12
9
  static config = {
10
+ /**
11
+ * @member {String} className='Portal.view.home.parts.Features'
12
+ * @protected
13
+ */
13
14
  className: 'Portal.view.home.parts.Features',
15
+ /**
16
+ * @member {String[]} cls=['portal-home-features']
17
+ */
14
18
  cls: ['portal-home-features'],
15
- layout: {ntype: 'vbox', align: 'stretch', wrap: 'wrap'},
16
- items: [
17
- {
18
- module: Container,
19
- layout: {ntype: 'hbox'},
20
- itemDefaults: {
21
- module: Component,
22
- style: {width: '33%', margin: '1em', padding: '1em', border: 'thin solid lightgray'}
23
- },
24
- items: [{
25
- module: Component,
26
- html: `
27
- <h2>Extremely High Performance</h2>
28
- <ul>
29
- <li>Multi-threaded via web workers
30
- <li>Lightning fast rendering
31
- </ul>
32
- <p>Neo.mjs runs key processes in separate web workers, each running in a parallel thread: one thread for app logic, one for managing DOM updates, and one for communicating with the backend. And if you have specialized or processor-intensive tasks, you can easily spawn additional threads.</p>
33
- <p>Besides the benefit of running in a separate thread, the DOM update thread has highly optimized code for tracking and applying delta updates, easily handling tens of thousands of updates per second.</p>
34
- `
35
- }, {
36
- module: Component,
37
- html: `
38
- <h2>Multi-Window Applications</h2>
39
- <ul>
40
- <li>Neo.mjs uniquely allows you to create multi-window applications
41
- <li>Application logic, state, data, and component instances are seamlessly shared
42
- </ul>
43
- <p>Neo.mjs components can be rendered to the DOM for any shared web worker. Your app logic listens to events, maintains state, and shares data, without caring where the component is rendering, even if it's to another browser window.</p>
44
- `
45
- }, {
46
- module: Component,
47
- html: `
48
- <h2>Powerful Framework Features</h2>
49
- <ul>
50
- <li>Component-based, declaratively configured 
51
- <li>Standard ECMAscript, without proprietary extenions
52
- <li>Property lifecycle hooks, elegant state management
53
- </ul>
54
- <p>Neo.mjs components are abstract, and configured declaratively. Compared to other libraries, Neo.mjs has features that make it much easier to do data binding, and to detect property updates and events.</p>
55
- <p>Since Neo.mjs uses standard JavaScript, there are no special WebPack transpilations. This also makes debugging easier: any statement you write in your application logic also runs in the devtools console.</p>
56
- `
57
- }]
58
- }, {
59
- module: Component,
60
- html: 'You can read more about Neo.mjs featuers and benefits in <a href="#/learn/WhyNeo-Features">the Learning section</a>. Scroll down to see some running examples.'
19
+ /**
20
+ * @member {Object|String} layout='grid'
21
+ */
22
+ layout: 'grid',
23
+ /**
24
+ * @member {Object} itemDefaults
25
+ */
26
+ itemDefaults: {
27
+ module: ContentBox
28
+ },
29
+ /**
30
+ * @member {Object[]} items
31
+ */
32
+ items: [{
33
+ header: 'Multi-threading',
34
+ route : '#/learn/WhyNeo-Quick',
35
+
36
+ content: [
37
+ 'Following the OMT (Off the Main-Thread) paradigm',
38
+ 'Your Apps & the Framework live within an Application Worker',
39
+ 'Non-blocking, no-freeze, user interaction responses, even for heavy data i/o, processing, and intensive, complex screen updating',
40
+ 'Additional Workers for OffscreenCanvas, Data, Delta-Updates & Tasks',
41
+ 'A ServiceWorker connected to the App Worker for predictive Caching'
42
+ ]
43
+ }, {
44
+ header: 'Multi-Window Apps',
45
+ route : '#/learn/WhyNeo-Quick',
46
+
47
+ content: [
48
+ 'No need for a Native Shell (e.g. Electron)',
49
+ 'Sharing Data across Windows',
50
+ 'Sharing State across Windows',
51
+ 'Moving Components across Windows while keeping the same JS instances'
52
+ ]
53
+ }, {
54
+ header: 'Modern JavaScript directly in your Browser',
55
+ route : '#/learn/WhyNeo-Quick',
56
+
57
+ content: [
58
+ 'The Dev-Mode runs without the need for Transpilations or Compilations',
59
+ 'Using the latest ECMAScript Features, as soon as the Browser Support is there',
60
+ 'Simple and powerful Debugging',
61
+ 'Reduced Development Costs'
62
+ ]
63
+ }, {
64
+ header: 'Powerful Component-Library',
65
+ route : '#/learn/WhyNeo-Quick',
66
+
67
+ content: [
68
+ 'Declarative Component-Trees',
69
+ 'High Order Components',
70
+ 'Many out-of-the-box Components, including nested lazy-loaded forms',
71
+ 'Multiple themes, which can get nested'
72
+ ]
73
+ }, {
74
+ header: 'Elegant State Management',
75
+ route : '#/learn/WhyNeo-Speed',
76
+
77
+ content: [
78
+ 'Multiple communicating State-Providers',
79
+ 'Observable',
80
+ 'Supporting different architectures like MVVM without enforcing them'
81
+ ]
82
+ }, {
83
+ header: 'Core Features',
84
+ route : '#/learn/WhyNeo-Speed',
85
+
86
+ content: [
87
+ 'RPC Layer (cross-realm, including Backends)',
88
+ 'Extensibility',
89
+ 'Scalability',
90
+ 'Class Config System',
91
+ 'Drag & Drop',
92
+ 'Mixins, Plugins & Main-Thread Addons'
93
+ ]
61
94
  }]
62
- }
95
+ }
63
96
  }
64
97
 
65
98
  Neo.setupClass(Features);
@@ -31,6 +31,7 @@ class MainContainer extends Container {
31
31
  items: [{
32
32
  module : Container,
33
33
  cls : ['sidenav-container'],
34
+ flex : 'none',
34
35
  layout : 'fit',
35
36
  reference: 'sidenav-container',
36
37
  tag : 'aside',
@@ -40,8 +41,9 @@ class MainContainer extends Container {
40
41
  reference: 'tree'
41
42
  }, {
42
43
  ntype : 'button',
44
+ bind : {hidden: data => data.size !== 'x-small'},
43
45
  cls : ['sidenav-button'],
44
- handler: 'onSideNaveToggleButtonClick',
46
+ handler: 'onSideNavToggleButtonClick',
45
47
  iconCls: 'fas fa-bars',
46
48
  ui : 'secondary'
47
49
  }]
@@ -55,9 +57,7 @@ class MainContainer extends Container {
55
57
  module: PageContainer
56
58
  }, {
57
59
  module : PageSectionsContainer,
58
- bind : {hidden: data => data.size !== 'large'},
59
- reference: 'page-sections-container',
60
- width : 250
60
+ reference: 'page-sections-container'
61
61
  }],
62
62
  /**
63
63
  * @member {Object} layout={ntype:'hbox',align:'stretch'}
@@ -121,6 +121,13 @@ class MainContainerController extends Controller {
121
121
  this.navigateTo(this.getModel().getData('nextPageRecord').id)
122
122
  }
123
123
 
124
+ /**
125
+ * @param {Object} data
126
+ */
127
+ onPageSectionsToggleButtonClick(data) {
128
+ this.getReference('page-sections-container').toggleCls('neo-expanded')
129
+ }
130
+
124
131
  /**
125
132
  * @param {Object} data
126
133
  */
@@ -159,7 +166,7 @@ class MainContainerController extends Controller {
159
166
  /**
160
167
  * @param {Object} data
161
168
  */
162
- onSideNaveToggleButtonClick(data) {
169
+ onSideNavToggleButtonClick(data) {
163
170
  this.getReference('sidenav-container').toggleCls('neo-expanded')
164
171
  }
165
172
  }
@@ -20,21 +20,26 @@ class PageSectionsContainer extends Container {
20
20
  * @member {Object[]} items
21
21
  */
22
22
  items: [{
23
+ ntype : 'button',
24
+ bind : {hidden: data => data.size === 'large'},
25
+ cls : ['sections-container-button'],
26
+ handler: 'onPageSectionsToggleButtonClick',
27
+ iconCls: 'fas fa-bars',
28
+ ui : 'secondary'
29
+ }, {
23
30
  vdom:
24
31
  {cn: [
25
32
  {tag: 'h3', html: 'On this page'}
26
33
  ]}
27
-
28
34
  }, {
29
35
  module : PageSectionsList,
36
+ listeners: {pageListSelect: 'up.onPageListSelect'},
30
37
  reference: 'list'
31
38
  }],
32
39
  /**
33
40
  * @member {Object} layout={ntype:'vbox'}
34
41
  */
35
- layout: {
36
- ntype: 'vbox'
37
- }
42
+ layout: {ntype: 'vbox'}
38
43
  }
39
44
 
40
45
  /**
@@ -44,6 +49,13 @@ class PageSectionsContainer extends Container {
44
49
  get list() {
45
50
  return this.getReference('list')
46
51
  }
52
+
53
+ /**
54
+ * @param {Object} data
55
+ */
56
+ onPageListSelect(data) {
57
+ this.toggleCls('neo-expanded', false)
58
+ }
47
59
  }
48
60
 
49
61
  Neo.setupClass(PageSectionsContainer);
@@ -62,6 +62,8 @@ class PageSectionsList extends List {
62
62
  if (record) {
63
63
  me.isAnimating = true;
64
64
 
65
+ me.fire('pageListSelect', {record});
66
+
65
67
  await Neo.main.DomAccess.scrollIntoView({
66
68
  querySelector: `[data-record-id='${record.id}']`,
67
69
  windowId : me.windowId
@@ -20,9 +20,9 @@ class ServiceWorker extends ServiceBase {
20
20
  */
21
21
  singleton: true,
22
22
  /**
23
- * @member {String} version='6.25.0'
23
+ * @member {String} version='6.27.0'
24
24
  */
25
- version: '6.25.0'
25
+ version: '6.27.0'
26
26
  }
27
27
 
28
28
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "neo.mjs",
3
- "version": "6.25.0",
3
+ "version": "6.27.0",
4
4
  "description": "The webworkers driven UI framework",
5
5
  "type": "module",
6
6
  "repository": {
@@ -61,41 +61,6 @@
61
61
  animation-timeline: --page-scroll;
62
62
  }
63
63
 
64
- //@scroll-timeline --page-scroll {
65
- // source: auto;
66
- // orientation: vertical;
67
- // scroll-offsets: 0%, 100%;
68
- //}
69
-
70
-
71
- /* Style and position the progress bar */
72
- .portal-home-progress {
73
- position: fixed;
74
- z-index: 1000;
75
- top: 0;
76
- height: 2px;
77
- width: 100%;
78
- background: var(--button-background-color);
79
- background: var(--sem-color-surface-primary-background);
80
-
81
- scale: 0 1;
82
- transform-origin: left;
83
-
84
- /* Attach the animation using an anonymous Scroll Progress Timeline */
85
- animation: grow-progress linear;
86
- animation-timeline: --page-scroll;
87
- }
88
-
89
- @keyframes grow-progress {
90
- to {
91
- scale: 1 1;
92
- }
93
- }
94
-
95
- //.page {
96
- // scroll-timeline: --page block;
97
- //}
98
-
99
64
  .portal-home-maincontainer {
100
65
  scroll-timeline: --page-scroll block;
101
66
  }
@@ -1,18 +1,13 @@
1
1
  .portal-content-box {
2
- border : 1px solid darkgray;
2
+ border : 1px solid var(--sem-color-border-default);
3
3
  cursor : pointer;
4
4
  color : #000;
5
- margin-top : 2em;
5
+ min-width : 300px;
6
6
  padding : 0 1em 1em 1em;
7
7
  text-decoration: none;
8
- width : 300px;
9
8
 
10
9
  &:hover {
11
- background-color: lightblue;
12
- }
13
-
14
- &:not(:last-child) {
15
- margin-right: 3em;
10
+ background-color: var(--sem-color-surface-primary-background);
16
11
  }
17
12
 
18
13
  .portal-content-box-content {
@@ -22,8 +17,4 @@
22
17
  .portal-content-box-header {
23
18
 
24
19
  }
25
-
26
- @media (max-width: 600px) {
27
- margin-right: 0;
28
- }
29
20
  }
@@ -2,4 +2,27 @@
2
2
  overflow-x : hidden!important;
3
3
  scroll-behavior : smooth;
4
4
  scroll-snap-type: y mandatory;
5
+
6
+ /* Style and position the progress bar */
7
+ .portal-home-progress {
8
+ background : var(--sem-color-surface-primary-background);
9
+ height : 4px;
10
+ position : fixed;
11
+ scale : 0 1;
12
+ top : 0;
13
+ transform-origin: left;
14
+ width : 100%;
15
+ z-index : 1000;
16
+
17
+
18
+ /* Attach the animation using an anonymous Scroll Progress Timeline */
19
+ animation : grow-progress linear;
20
+ animation-timeline: --page-scroll;
21
+ }
22
+
23
+ @keyframes grow-progress {
24
+ to {
25
+ scale: 1 1;
26
+ }
27
+ }
5
28
  }
@@ -2,52 +2,23 @@
2
2
  min-height : 100%;
3
3
  scroll-snap-align: center;
4
4
 
5
- @keyframes appear-left {
6
- 0% {opacity: 0; transform: translateX(-400%);}
7
- 90% {opacity: .3;}
8
- 100% {opacity: 1; transform: translateX(0);}
9
- }
10
-
11
- @keyframes appear-right {
12
- 0% {opacity: 0; transform: translateX(400%);}
13
- 90% {opacity: .3;}
14
- 100% {opacity: 1; transform: translateX(0);}
15
- }
16
-
17
- // &:not(:first-child) {
18
- // &:not(:last-child) {
19
- // animation-timeline : view(block 100% -500%);
20
- // animation-fill-mode : both;
21
- // animation-duration : 1ms; /* Firefox requires this to apply the animation */
22
- // animation-timing-function: linear;
23
-
24
- // &:nth-child(even) {
25
- // animation-name: appear-right;
26
- // }
27
-
28
- // &:nth-child(odd) {
29
- // animation-name: appear-left;
30
- // }
31
- // }
32
- // }
33
-
34
5
  .neo-content {
35
6
  font-size: min(max(2.3vw, 16px), 30px);
36
7
  }
37
8
 
38
9
  .neo-h1 {
39
10
  font-size : min(max(5.5vw, 30px), 64px);
40
- text-align : center;
41
- margin : 0;
42
11
  line-height: 1em;
12
+ margin : 0;
13
+ text-align : center;
43
14
  }
44
15
 
45
16
  .neo-h2 {
46
17
  font-size : min(max(3.5vw, 24px), 44px);
47
18
  font-weight: 600;
48
- text-align : center;
49
- margin : 0;
50
19
  line-height: 1em;
20
+ margin : 0;
21
+ text-align : center;
51
22
  }
52
23
 
53
24
  .page-live-preview {
@@ -1,5 +1,15 @@
1
1
  .portal-home-features {
2
- padding : 3em;
3
- overflow-y : auto;
4
- place-content: start;
2
+ gap : 2em;
3
+ grid-template-columns: repeat(3, 1fr);
4
+ overflow-x : auto;
5
+ padding : 2em;
6
+ place-content : start;
7
+
8
+ @media (max-width: 1000px) {
9
+ grid-template-columns: repeat(2, 1fr);
10
+ }
11
+
12
+ @media (max-width: 700px) {
13
+ grid-template-columns: repeat(6, 1fr);
14
+ }
5
15
  }
@@ -3,7 +3,7 @@
3
3
  border-radius : 8px;
4
4
  letter-spacing : 1px;
5
5
  margin : 0 auto;
6
- max-width : 900px;
6
+ max-width : 1200px;
7
7
  overflow : unset;
8
8
  padding : 0 3rem;
9
9
  width : -webkit-fill-available;
@@ -11,7 +11,7 @@
11
11
  pre[data-javascript] {
12
12
  border : thin solid lightgray;
13
13
  border-radius: 4px;
14
- min-width : fit-content;
14
+ overflow-x : scroll;
15
15
  padding : 12px;
16
16
  }
17
17
 
@@ -5,6 +5,10 @@
5
5
  margin: 0;
6
6
  }
7
7
 
8
+ .sidenav-button {
9
+ display: none;
10
+ }
11
+
8
12
  .sidenav-container {
9
13
  min-width: 350px;
10
14
  width : 350px;
@@ -17,6 +21,7 @@
17
21
  overflow : visible;
18
22
  position : fixed;
19
23
  transition: left 250ms ease-out;
24
+ z-index : 101;
20
25
 
21
26
  &.neo-expanded {
22
27
  left: 0;
@@ -24,11 +29,13 @@
24
29
 
25
30
  .sidenav-button {
26
31
  box-shadow: 0 5px 10px rgba(0,0,0,.3);
32
+ display : flex;
27
33
  height : 35px;
28
34
  min-width : 35px;
29
35
  position : absolute;
30
36
  right : -42px;
31
37
  width : 35px;
38
+ z-index : 101;
32
39
  }
33
40
  }
34
41
  }
@@ -3,12 +3,42 @@
3
3
  border : none; // reset the default 1px
4
4
  display : block;
5
5
  flex-shrink : 0;
6
+ min-width : 250px;
6
7
  position : fixed;
7
8
  right : 0;
8
9
  width : 250px;
9
10
  z-index : 1;
10
11
 
11
12
  h3 {
12
- margin-left: 16px;
13
+ margin-bottom: .4em;
14
+ margin-left : 16px;
15
+ }
16
+
17
+ .sections-container-button {
18
+ display: none;
19
+ }
20
+
21
+ @media (max-width: 1296px) {
22
+ background-color: #fff;
23
+ box-shadow : 0 5px 10px rgba(0,0,0,.3);
24
+ overflow : visible;
25
+ right : -250px;
26
+ transition : right 250ms ease-out;
27
+ z-index : 100;
28
+
29
+ &.neo-expanded {
30
+ right: 0;
31
+ }
32
+
33
+ .sections-container-button {
34
+ background-color: #fff;
35
+ box-shadow : 0 5px 10px rgba(0,0,0,.3);
36
+ display : flex;
37
+ height : 35px;
38
+ min-width : 35px;
39
+ position : absolute;
40
+ left : -42px;
41
+ width : 35px;
42
+ }
13
43
  }
14
44
  }
@@ -260,12 +260,12 @@ const DefaultConfig = {
260
260
  useVdomWorker: true,
261
261
  /**
262
262
  * buildScripts/injectPackageVersion.mjs will update this value
263
- * @default '6.25.0'
263
+ * @default '6.27.0'
264
264
  * @memberOf! module:Neo
265
265
  * @name config.version
266
266
  * @type String
267
267
  */
268
- version: '6.25.0'
268
+ version: '6.27.0'
269
269
  };
270
270
 
271
271
  Object.assign(DefaultConfig, {
@@ -178,7 +178,7 @@ class LivePreview extends Container {
178
178
  position: 'fixed',
179
179
  top : rect.y + 'px',
180
180
  width : rect.width + 'px',
181
- zIndex : 100
181
+ zIndex : 103
182
182
  });
183
183
 
184
184
  me.update();
@@ -1,13 +1,18 @@
1
1
  import BaseComponent from '../component/Base.mjs';
2
- import VDomUtil from '../util/VDom.mjs';
2
+ import VDomUtil from '../util/VDom.mjs';
3
3
 
4
4
  /**
5
5
  * @class Neo.component.Video
6
6
  * @extends Neo.component.Base
7
7
  *
8
8
  * @example
9
- * ntype: 'video',
10
- * url: 'https://video-ssl.itunes.apple.com/itunes-assets/Video125/v4/a0/57/54/a0575426-dd8e-2d25-bdf3-139702870b50/mzvf_786190431362224858.640x464.h264lc.U.p.m4v'
9
+ * ntype : 'video',
10
+ * url : 'https://video-ssl.itunes.apple.com/itunes-assets/Video125/v4/a0/57/54/a0575426-dd8e-2d25-bdf3-139702870b50/mzvf_786190431362224858.640x464.h264lc.U.p.m4v'
11
+ * autoplay: true
12
+ *
13
+ * @methods
14
+ * play
15
+ * pause
11
16
  */
12
17
  class Video extends BaseComponent {
13
18
  static config = {
@@ -25,6 +30,19 @@ class Video extends BaseComponent {
25
30
  * @member {String[]} baseCls=['neo-video']
26
31
  */
27
32
  baseCls: ['neo-video'],
33
+ /**
34
+ * Automatically start the video
35
+ * Initial setting, which does not make sense to change later
36
+ * !!Most browsers only support muted autostart
37
+ * @member {Boolean} autoplay=false
38
+ */
39
+ autoplay: false,
40
+ /**
41
+ * In case the browser does not support the video source
42
+ * the component should show an error.
43
+ * @member {Boolean} errorMsg='The browser does not support the video'
44
+ */
45
+ errorMsg: 'Your browser does not support the video tag.',
28
46
  /**
29
47
  * Current state of the video
30
48
  * @member {Boolean} playing_=false
@@ -45,18 +63,17 @@ class Video extends BaseComponent {
45
63
  _vdom: {
46
64
  cn: [{
47
65
  flag: 'ghost',
48
- cls : ['neo-video-ghost'],
49
- cn : [{
50
- // The <i> tag defines a part of text in an alternate voice or mood. The content inside is typically displayed in italic.
66
+ cls: ['neo-video-ghost'],
67
+ cn: [{
51
68
  tag: 'i',
52
69
  cls: ['fa-solid', 'fa-circle-play']
53
70
  }]
54
71
  }, {
55
72
  // Neo specific configs
56
- tag : 'video',
57
- flag : 'media',
73
+ tag: 'video',
74
+ flag: 'media',
75
+ cls: ['neo-video-media'],
58
76
  removeDom: true,
59
- cls : ['neo-video-media'],
60
77
  // dom attributes
61
78
  autoplay: true,
62
79
  controls: true
@@ -72,8 +89,9 @@ class Video extends BaseComponent {
72
89
 
73
90
  let me = this;
74
91
 
92
+ me.handleAutoplay();
75
93
  me.addDomListeners(
76
- {click: me.play, delegate: '.neo-video-ghost'},
94
+ {click: me.play, delegate: '.neo-video-ghost'},
77
95
  {click: me.pause, delegate: '.neo-video-media'}
78
96
  )
79
97
  }
@@ -100,8 +118,8 @@ class Video extends BaseComponent {
100
118
  */
101
119
  afterSetPlaying(value, oldValue) {
102
120
  let {vdom} = this,
103
- media = VDomUtil.getFlags(vdom, 'media')[0],
104
- ghost = VDomUtil.getFlags(vdom, 'ghost')[0];
121
+ media = VDomUtil.getFlags(vdom, 'media')[0],
122
+ ghost = VDomUtil.getFlags(vdom, 'ghost')[0];
105
123
 
106
124
  ghost.removeDom = value;
107
125
  media.removeDom = !value;
@@ -117,20 +135,39 @@ class Video extends BaseComponent {
117
135
  * @param {String|null} oldValue
118
136
  */
119
137
  afterSetUrl(value, oldValue) {
120
- if (!value) {
121
- return
122
- }
138
+ if (!value) return;
123
139
 
124
- let me = this,
125
- media = me.vdom.cn[1];
140
+ let {vdom} = this,
141
+ media = VDomUtil.getFlags(vdom, 'media')[0];
126
142
 
127
143
  media.cn = [{
128
- tag : 'source',
129
- src : value,
130
- type: me.type
144
+ tag: 'source',
145
+ src: value,
146
+ type: this.type
147
+ }, {
148
+ tag: 'span',
149
+ html: this.errorMsg,
131
150
  }];
132
151
 
133
- me.update()
152
+ this.update()
153
+ }
154
+
155
+ /**
156
+ * autoplay - run the event listeners
157
+ */
158
+ handleAutoplay() {
159
+ if (!this.autoplay) return;
160
+
161
+ let {vdom} = this,
162
+ media = VDomUtil.getFlags(vdom, 'media')[0];
163
+
164
+ // Most browsers require videos to be muted for autoplay to work.
165
+ media.muted = true;
166
+ // Allows inline playback on iOS devices
167
+ media.playsInline = true;
168
+
169
+ this.update();
170
+ this.playing = true;
134
171
  }
135
172
 
136
173
  /**