uikit 3.11.2-dev.03e47c2ff → 3.11.2-dev.06fe7f1d0

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 (72) hide show
  1. package/CHANGELOG.md +5 -1
  2. package/dist/css/uikit-core-rtl.css +12 -6
  3. package/dist/css/uikit-core-rtl.min.css +1 -1
  4. package/dist/css/uikit-core.css +12 -6
  5. package/dist/css/uikit-core.min.css +1 -1
  6. package/dist/css/uikit-rtl.css +12 -6
  7. package/dist/css/uikit-rtl.min.css +1 -1
  8. package/dist/css/uikit.css +12 -6
  9. package/dist/css/uikit.min.css +1 -1
  10. package/dist/js/components/countdown.js +3 -3
  11. package/dist/js/components/countdown.min.js +1 -1
  12. package/dist/js/components/filter.js +1 -1
  13. package/dist/js/components/filter.min.js +1 -1
  14. package/dist/js/components/lightbox-panel.js +4 -14
  15. package/dist/js/components/lightbox-panel.min.js +1 -1
  16. package/dist/js/components/lightbox.js +4 -14
  17. package/dist/js/components/lightbox.min.js +1 -1
  18. package/dist/js/components/notification.js +1 -1
  19. package/dist/js/components/notification.min.js +1 -1
  20. package/dist/js/components/parallax.js +8 -9
  21. package/dist/js/components/parallax.min.js +1 -1
  22. package/dist/js/components/slider-parallax.js +8 -9
  23. package/dist/js/components/slider-parallax.min.js +1 -1
  24. package/dist/js/components/slider.js +59 -78
  25. package/dist/js/components/slider.min.js +1 -1
  26. package/dist/js/components/slideshow-parallax.js +8 -9
  27. package/dist/js/components/slideshow-parallax.min.js +1 -1
  28. package/dist/js/components/slideshow.js +23 -45
  29. package/dist/js/components/slideshow.min.js +1 -1
  30. package/dist/js/components/sortable.js +1 -1
  31. package/dist/js/components/sortable.min.js +1 -1
  32. package/dist/js/components/tooltip.js +1 -1
  33. package/dist/js/components/tooltip.min.js +1 -1
  34. package/dist/js/components/upload.js +2 -1
  35. package/dist/js/components/upload.min.js +1 -1
  36. package/dist/js/uikit-core.js +357 -264
  37. package/dist/js/uikit-core.min.js +1 -1
  38. package/dist/js/uikit-icons.js +1 -1
  39. package/dist/js/uikit-icons.min.js +1 -1
  40. package/dist/js/uikit.js +414 -361
  41. package/dist/js/uikit.min.js +1 -1
  42. package/package.json +1 -1
  43. package/src/js/api/hooks.js +2 -2
  44. package/src/js/api/state.js +173 -140
  45. package/src/js/components/countdown.js +2 -2
  46. package/src/js/components/internal/slider-preload.js +4 -32
  47. package/src/js/components/slider.js +39 -42
  48. package/src/js/components/upload.js +2 -1
  49. package/src/js/core/accordion.js +6 -1
  50. package/src/js/core/core.js +2 -75
  51. package/src/js/core/cover.js +5 -1
  52. package/src/js/core/drop.js +3 -1
  53. package/src/js/core/form-custom.js +2 -2
  54. package/src/js/core/height-viewport.js +3 -0
  55. package/src/js/core/img.js +16 -24
  56. package/src/js/core/offcanvas.js +2 -1
  57. package/src/js/core/sticky.js +27 -33
  58. package/src/js/core/switcher.js +11 -3
  59. package/src/js/core/toggle.js +3 -1
  60. package/src/js/core/video.js +13 -1
  61. package/src/js/mixin/lazyload.js +20 -0
  62. package/src/js/mixin/parallax.js +8 -9
  63. package/src/js/mixin/slider.js +0 -6
  64. package/src/js/mixin/slideshow.js +0 -4
  65. package/src/js/mixin/swipe.js +72 -0
  66. package/src/js/util/index.js +1 -0
  67. package/src/js/util/observer.js +36 -0
  68. package/src/less/components/utility.less +6 -3
  69. package/src/scss/components/utility.scss +6 -3
  70. package/tests/parallax.html +2 -2
  71. package/tests/sticky-parallax.html +1 -1
  72. package/tests/utility.html +17 -0
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "uikit",
3
3
  "title": "UIkit",
4
4
  "description": "UIkit is a lightweight and modular front-end framework for developing fast and powerful web interfaces.",
5
- "version": "3.11.2-dev.03e47c2ff",
5
+ "version": "3.11.2-dev.06fe7f1d0",
6
6
  "main": "dist/js/uikit.js",
7
7
  "style": "dist/css/uikit.css",
8
8
  "sideEffects": [
@@ -81,8 +81,8 @@ export default function (UIkit) {
81
81
  };
82
82
 
83
83
  function runUpdates(types) {
84
- for (const { read, write, events } of this.$options.update) {
85
- if (!types.has('update') && (!events || !events.some((type) => types.has(type)))) {
84
+ for (const { read, write, events = [] } of this.$options.update) {
85
+ if (!types.has('update') && !events.some((type) => types.has(type))) {
86
86
  continue;
87
87
  }
88
88
 
@@ -4,6 +4,7 @@ import {
4
4
  data as getData,
5
5
  hasOwn,
6
6
  hyphenate,
7
+ includes,
7
8
  isArray,
8
9
  isFunction,
9
10
  isNumeric,
@@ -100,203 +101,235 @@ export default function (UIkit) {
100
101
  }
101
102
  }
102
103
  }
104
+ for (const { events = [] } of this.$options.update || []) {
105
+ if (includes(events, 'scroll')) {
106
+ registerScrollListener(this._uid, () => this.$emit('scroll'));
107
+ break;
108
+ }
109
+ }
103
110
  };
104
111
 
105
112
  UIkit.prototype._unbindEvents = function () {
106
113
  this._events.forEach((unbind) => unbind());
107
114
  delete this._events;
115
+ unregisterScrollListener(this._uid);
108
116
  };
109
117
 
110
118
  UIkit.prototype._initObservers = function () {
111
119
  this._observers = [initChildListObserver(this), initPropsObserver(this)];
112
120
  };
113
121
 
122
+ UIkit.prototype.registerObserver = function (observer) {
123
+ this._observers.push(observer);
124
+ };
125
+
114
126
  UIkit.prototype._disconnectObservers = function () {
115
127
  this._observers.forEach((observer) => observer?.disconnect());
116
128
  };
129
+ }
117
130
 
118
- function getProps(opts, name) {
119
- const data = {};
120
- const { args = [], props = {}, el } = opts;
121
-
122
- if (!props) {
123
- return data;
124
- }
125
-
126
- for (const key in props) {
127
- const prop = hyphenate(key);
128
- let value = getData(el, prop);
129
-
130
- if (isUndefined(value)) {
131
- continue;
132
- }
131
+ function getProps(opts, name) {
132
+ const data = {};
133
+ const { args = [], props = {}, el } = opts;
133
134
 
134
- value = props[key] === Boolean && value === '' ? true : coerce(props[key], value);
135
+ if (!props) {
136
+ return data;
137
+ }
135
138
 
136
- if (prop === 'target' && (!value || startsWith(value, '_'))) {
137
- continue;
138
- }
139
+ for (const key in props) {
140
+ const prop = hyphenate(key);
141
+ let value = getData(el, prop);
139
142
 
140
- data[key] = value;
143
+ if (isUndefined(value)) {
144
+ continue;
141
145
  }
142
146
 
143
- const options = parseOptions(getData(el, name), args);
147
+ value = props[key] === Boolean && value === '' ? true : coerce(props[key], value);
144
148
 
145
- for (const key in options) {
146
- const prop = camelize(key);
147
- if (props[prop] !== undefined) {
148
- data[prop] = coerce(props[prop], options[key]);
149
- }
149
+ if (prop === 'target' && (!value || startsWith(value, '_'))) {
150
+ continue;
150
151
  }
151
152
 
152
- return data;
153
+ data[key] = value;
153
154
  }
154
155
 
155
- function registerComputed(component, key, cb) {
156
- Object.defineProperty(component, key, {
157
- enumerable: true,
156
+ const options = parseOptions(getData(el, name), args);
158
157
 
159
- get() {
160
- const { _computeds, $props, $el } = component;
158
+ for (const key in options) {
159
+ const prop = camelize(key);
160
+ if (props[prop] !== undefined) {
161
+ data[prop] = coerce(props[prop], options[key]);
162
+ }
163
+ }
161
164
 
162
- if (!hasOwn(_computeds, key)) {
163
- _computeds[key] = (cb.get || cb).call(component, $props, $el);
164
- }
165
+ return data;
166
+ }
165
167
 
166
- return _computeds[key];
167
- },
168
+ function registerComputed(component, key, cb) {
169
+ Object.defineProperty(component, key, {
170
+ enumerable: true,
168
171
 
169
- set(value) {
170
- const { _computeds } = component;
172
+ get() {
173
+ const { _computeds, $props, $el } = component;
171
174
 
172
- _computeds[key] = cb.set ? cb.set.call(component, value) : value;
175
+ if (!hasOwn(_computeds, key)) {
176
+ _computeds[key] = (cb.get || cb).call(component, $props, $el);
177
+ }
173
178
 
174
- if (isUndefined(_computeds[key])) {
175
- delete _computeds[key];
176
- }
177
- },
178
- });
179
- }
179
+ return _computeds[key];
180
+ },
180
181
 
181
- function registerEvent(component, event, key) {
182
- if (!isPlainObject(event)) {
183
- event = { name: key, handler: event };
184
- }
182
+ set(value) {
183
+ const { _computeds } = component;
185
184
 
186
- let { name, el, handler, capture, passive, delegate, filter, self } = event;
187
- el = isFunction(el) ? el.call(component) : el || component.$el;
185
+ _computeds[key] = cb.set ? cb.set.call(component, value) : value;
188
186
 
189
- if (isArray(el)) {
190
- el.forEach((el) => registerEvent(component, { ...event, el }, key));
191
- return;
192
- }
187
+ if (isUndefined(_computeds[key])) {
188
+ delete _computeds[key];
189
+ }
190
+ },
191
+ });
192
+ }
193
193
 
194
- if (!el || (filter && !filter.call(component))) {
195
- return;
196
- }
194
+ export function registerEvent(component, event, key) {
195
+ if (!isPlainObject(event)) {
196
+ event = { name: key, handler: event };
197
+ }
197
198
 
198
- component._events.push(
199
- on(
200
- el,
201
- name,
202
- delegate ? (isString(delegate) ? delegate : delegate.call(component)) : null,
203
- isString(handler) ? component[handler] : handler.bind(component),
204
- { passive, capture, self }
205
- )
206
- );
199
+ let { name, el, handler, capture, passive, delegate, filter, self } = event;
200
+ el = isFunction(el) ? el.call(component) : el || component.$el;
201
+
202
+ if (isArray(el)) {
203
+ el.forEach((el) => registerEvent(component, { ...event, el }, key));
204
+ return;
207
205
  }
208
206
 
209
- function notIn(options, key) {
210
- return options.every((arr) => !arr || !hasOwn(arr, key));
207
+ if (!el || (filter && !filter.call(component))) {
208
+ return;
211
209
  }
212
210
 
213
- function coerce(type, value) {
214
- if (type === Boolean) {
215
- return toBoolean(value);
216
- } else if (type === Number) {
217
- return toNumber(value);
218
- } else if (type === 'list') {
219
- return toList(value);
220
- }
211
+ component._events.push(
212
+ on(
213
+ el,
214
+ name,
215
+ delegate ? (isString(delegate) ? delegate : delegate.call(component)) : null,
216
+ isString(handler) ? component[handler] : handler.bind(component),
217
+ { passive, capture, self }
218
+ )
219
+ );
220
+ }
221
221
 
222
- return type ? type(value) : value;
223
- }
222
+ function notIn(options, key) {
223
+ return options.every((arr) => !arr || !hasOwn(arr, key));
224
+ }
224
225
 
225
- function toList(value) {
226
- return isArray(value)
227
- ? value
228
- : isString(value)
229
- ? value
230
- .split(/,(?![^(]*\))/)
231
- .map((value) => (isNumeric(value) ? toNumber(value) : toBoolean(value.trim())))
232
- : [value];
226
+ function coerce(type, value) {
227
+ if (type === Boolean) {
228
+ return toBoolean(value);
229
+ } else if (type === Number) {
230
+ return toNumber(value);
231
+ } else if (type === 'list') {
232
+ return toList(value);
233
233
  }
234
234
 
235
- function normalizeData({ data = {} }, { args = [], props = {} }) {
236
- if (isArray(data)) {
237
- data = data.slice(0, args.length).reduce((data, value, index) => {
238
- if (isPlainObject(value)) {
239
- assign(data, value);
240
- } else {
241
- data[args[index]] = value;
242
- }
243
- return data;
244
- }, {});
245
- }
235
+ return type ? type(value) : value;
236
+ }
246
237
 
247
- for (const key in data) {
248
- if (isUndefined(data[key])) {
249
- delete data[key];
250
- } else if (props[key]) {
251
- data[key] = coerce(props[key], data[key]);
238
+ function toList(value) {
239
+ return isArray(value)
240
+ ? value
241
+ : isString(value)
242
+ ? value
243
+ .split(/,(?![^(]*\))/)
244
+ .map((value) => (isNumeric(value) ? toNumber(value) : toBoolean(value.trim())))
245
+ : [value];
246
+ }
247
+
248
+ function normalizeData({ data = {} }, { args = [], props = {} }) {
249
+ if (isArray(data)) {
250
+ data = data.slice(0, args.length).reduce((data, value, index) => {
251
+ if (isPlainObject(value)) {
252
+ assign(data, value);
253
+ } else {
254
+ data[args[index]] = value;
252
255
  }
253
- }
256
+ return data;
257
+ }, {});
258
+ }
254
259
 
255
- return data;
260
+ for (const key in data) {
261
+ if (isUndefined(data[key])) {
262
+ delete data[key];
263
+ } else if (props[key]) {
264
+ data[key] = coerce(props[key], data[key]);
265
+ }
256
266
  }
257
267
 
258
- function initChildListObserver(component) {
259
- const { el } = component.$options;
268
+ return data;
269
+ }
260
270
 
261
- const observer = new MutationObserver(() => component.$emit());
262
- observer.observe(el, {
263
- childList: true,
264
- subtree: true,
265
- });
271
+ function initChildListObserver(component) {
272
+ const { el } = component.$options;
266
273
 
267
- return observer;
268
- }
274
+ const observer = new MutationObserver(() => component.$emit());
275
+ observer.observe(el, {
276
+ childList: true,
277
+ subtree: true,
278
+ });
279
+
280
+ return observer;
281
+ }
269
282
 
270
- function initPropsObserver(component) {
271
- const { $name, $options, $props } = component;
272
- const { attrs, props, el } = $options;
283
+ function initPropsObserver(component) {
284
+ const { $name, $options, $props } = component;
285
+ const { attrs, props, el } = $options;
273
286
 
274
- if (!props || attrs === false) {
275
- return;
287
+ if (!props || attrs === false) {
288
+ return;
289
+ }
290
+
291
+ const attributes = isArray(attrs) ? attrs : Object.keys(props);
292
+ const filter = attributes.map((key) => hyphenate(key)).concat($name);
293
+
294
+ const observer = new MutationObserver((records) => {
295
+ const data = getProps($options, $name);
296
+ if (
297
+ records.some(({ attributeName }) => {
298
+ const prop = attributeName.replace('data-', '');
299
+ return (
300
+ prop === $name ? attributes : [camelize(prop), camelize(attributeName)]
301
+ ).some((prop) => !isUndefined(data[prop]) && data[prop] !== $props[prop]);
302
+ })
303
+ ) {
304
+ component.$reset();
276
305
  }
306
+ });
277
307
 
278
- const attributes = isArray(attrs) ? attrs : Object.keys(props);
279
- const filter = attributes.map((key) => hyphenate(key)).concat($name);
280
-
281
- const observer = new MutationObserver((records) => {
282
- const data = getProps($options, $name);
283
- if (
284
- records.some(({ attributeName }) => {
285
- const prop = attributeName.replace('data-', '');
286
- return (
287
- prop === $name ? attributes : [camelize(prop), camelize(attributeName)]
288
- ).some((prop) => !isUndefined(data[prop]) && data[prop] !== $props[prop]);
289
- })
290
- ) {
291
- component.$reset();
292
- }
293
- });
308
+ observer.observe(el, {
309
+ attributes: true,
310
+ attributeFilter: filter.concat(filter.map((key) => `data-${key}`)),
311
+ });
294
312
 
295
- observer.observe(el, {
296
- attributes: true,
297
- attributeFilter: filter.concat(filter.map((key) => `data-${key}`)),
313
+ return observer;
314
+ }
315
+
316
+ const scrollListeners = new Map();
317
+ let unbindScrollListener;
318
+ function registerScrollListener(id, listener) {
319
+ unbindScrollListener =
320
+ unbindScrollListener ||
321
+ on(window, 'scroll', () => scrollListeners.forEach((listener) => listener()), {
322
+ passive: true,
323
+ capture: true,
298
324
  });
299
325
 
300
- return observer;
326
+ scrollListeners.set(id, listener);
327
+ }
328
+
329
+ function unregisterScrollListener(id) {
330
+ scrollListeners.delete(id);
331
+ if (unbindScrollListener && !scrollListeners.size) {
332
+ unbindScrollListener();
333
+ unbindScrollListener = null;
301
334
  }
302
335
  }
@@ -101,8 +101,8 @@ export default {
101
101
  this.stop();
102
102
 
103
103
  if (this.date && this.units.length) {
104
- this.$update();
105
- this.timer = setInterval(this.$update, 1000);
104
+ this.$emit();
105
+ this.timer = setInterval(() => this.$emit(), 1000);
106
106
  }
107
107
  },
108
108
 
@@ -1,37 +1,9 @@
1
- import { $$, isVisible, removeAttr } from 'uikit-util';
1
+ import Lazyload from '../../mixin/lazyload';
2
2
 
3
3
  export default {
4
- connected() {
5
- if (window.IntersectionObserver) {
6
- this.observer = new IntersectionObserver(
7
- (entries) => {
8
- if (entries.some((entry) => entry.isIntersecting)) {
9
- removeLazyLoad(this.getAdjacentSlides());
10
- }
11
- },
12
- { rootMargin: '50% 50%' }
13
- );
14
- this.observer.observe(this.$el);
15
- }
16
- },
17
-
18
- disconnected() {
19
- this.observer && this.observer.disconnect();
20
- },
4
+ mixins: [Lazyload],
21
5
 
22
- update: {
23
- read() {
24
- if (isVisible(this.$el)) {
25
- removeLazyLoad(this.getAdjacentSlides());
26
- }
27
- },
28
-
29
- events: ['resize'],
6
+ connected() {
7
+ this.lazyload(this.slides, this.getAdjacentSlides);
30
8
  },
31
9
  };
32
-
33
- export function removeLazyLoad(elements = []) {
34
- for (const el of elements) {
35
- el && $$('img[loading="lazy"]', el).forEach((el) => removeAttr(el, 'loading'));
36
- }
37
- }
@@ -12,9 +12,7 @@ import {
12
12
  dimensions,
13
13
  findIndex,
14
14
  includes,
15
- isEmpty,
16
15
  last,
17
- sortBy,
18
16
  toFloat,
19
17
  toggleClass,
20
18
  toNumber,
@@ -73,48 +71,39 @@ export default {
73
71
  return ~index ? index : this.length - 1;
74
72
  },
75
73
 
76
- sets({ sets }) {
77
- if (!sets) {
74
+ sets({ sets: enabled }) {
75
+ if (!enabled) {
78
76
  return;
79
77
  }
80
78
 
81
- const width = dimensions(this.list).width / (this.center ? 2 : 1);
82
-
83
79
  let left = 0;
84
- let leftCenter = width;
85
- let slideLeft = 0;
86
-
87
- sets = sortBy(this.slides, 'offsetLeft').reduce((sets, slide, i) => {
88
- const slideWidth = dimensions(slide).width;
89
- const slideRight = slideLeft + slideWidth;
80
+ const sets = [];
81
+ const width = dimensions(this.list).width;
82
+ for (let i in this.slides) {
83
+ const slideWidth = dimensions(this.slides[i]).width;
90
84
 
91
- if (slideRight > left) {
92
- if (!this.center && i > this.maxIndex) {
93
- i = this.maxIndex;
94
- }
85
+ if (left + slideWidth > width) {
86
+ left = 0;
87
+ }
95
88
 
96
- if (!includes(sets, i)) {
97
- const cmp = this.slides[i + 1];
98
- if (
99
- this.center &&
100
- cmp &&
101
- slideWidth < leftCenter - dimensions(cmp).width / 2
102
- ) {
103
- leftCenter -= slideWidth;
104
- } else {
105
- leftCenter = width;
106
- sets.push(i);
107
- left = slideLeft + width + (this.center ? slideWidth / 2 : 0);
108
- }
89
+ if (this.center) {
90
+ if (
91
+ left < width / 2 &&
92
+ left + slideWidth + dimensions(this.slides[+i + 1]).width / 2 > width / 2
93
+ ) {
94
+ sets.push(+i);
95
+ left = width / 2 - slideWidth / 2;
109
96
  }
97
+ } else if (left === 0) {
98
+ sets.push(Math.min(+i, this.maxIndex));
110
99
  }
111
100
 
112
- slideLeft += slideWidth;
101
+ left += slideWidth;
102
+ }
113
103
 
104
+ if (sets.length) {
114
105
  return sets;
115
- }, []);
116
-
117
- return !isEmpty(sets) && sets;
106
+ }
118
107
  },
119
108
 
120
109
  transitionOptions() {
@@ -146,15 +135,7 @@ export default {
146
135
  this._translate(1);
147
136
  }
148
137
 
149
- const actives = this._getTransitioner(this.index).getActives();
150
- const activeClasses = [
151
- this.clsActive,
152
- ((!this.sets || includes(this.sets, toFloat(this.index))) && this.clsActivated) ||
153
- '',
154
- ];
155
- for (const slide of this.slides) {
156
- toggleClass(slide, activeClasses, includes(actives, slide));
157
- }
138
+ this.updateActiveClasses();
158
139
  },
159
140
 
160
141
  events: ['resize'],
@@ -203,6 +184,10 @@ export default {
203
184
  addClass(this._getTransitioner().getItemIn(), this.clsActive);
204
185
  }
205
186
  },
187
+
188
+ itemshown() {
189
+ this.updateActiveClasses();
190
+ },
206
191
  },
207
192
 
208
193
  methods: {
@@ -239,6 +224,18 @@ export default {
239
224
  }
240
225
  },
241
226
 
227
+ updateActiveClasses() {
228
+ const actives = this._getTransitioner(this.index).getActives();
229
+ const activeClasses = [
230
+ this.clsActive,
231
+ ((!this.sets || includes(this.sets, toFloat(this.index))) && this.clsActivated) ||
232
+ '',
233
+ ];
234
+ for (const slide of this.slides) {
235
+ toggleClass(slide, activeClasses, includes(actives, slide));
236
+ }
237
+ },
238
+
242
239
  getValidIndex(index = this.index, prevIndex = this.prevIndex) {
243
240
  index = this.getIndex(index, prevIndex);
244
241
 
@@ -1,4 +1,4 @@
1
- import { addClass, ajax, matches, noop, on, removeClass, trigger } from 'uikit-util';
1
+ import { addClass, ajax, matches, noop, on, removeClass, toArray, trigger } from 'uikit-util';
2
2
 
3
3
  export default {
4
4
  props: {
@@ -178,6 +178,7 @@ function match(pattern, path) {
178
178
  }
179
179
 
180
180
  function chunk(files, size) {
181
+ files = toArray(files);
181
182
  const chunks = [];
182
183
  for (let i = 0; i < files.length; i += size) {
183
184
  chunks.push(files.slice(i, i + size));
@@ -1,4 +1,5 @@
1
1
  import Class from '../mixin/class';
2
+ import Lazyload from '../mixin/lazyload';
2
3
  import { default as Togglable, toggleHeight } from '../mixin/togglable';
3
4
  import {
4
5
  $,
@@ -17,7 +18,7 @@ import {
17
18
  } from 'uikit-util';
18
19
 
19
20
  export default {
20
- mixins: [Class, Togglable],
21
+ mixins: [Class, Lazyload, Togglable],
21
22
 
22
23
  props: {
23
24
  targets: String,
@@ -73,6 +74,10 @@ export default {
73
74
  },
74
75
  },
75
76
 
77
+ connected() {
78
+ this.lazyload();
79
+ },
80
+
76
81
  events: [
77
82
  {
78
83
  name: 'click',