decidim-gallery 0.0.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 (65) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE-AGPLv3.txt +661 -0
  3. data/README.md +30 -0
  4. data/Rakefile +9 -0
  5. data/app/cells/decidim/gallery/main/image.erb +51 -0
  6. data/app/cells/decidim/gallery/main/image_collection.erb +13 -0
  7. data/app/cells/decidim/gallery/main/image_list.erb +9 -0
  8. data/app/cells/decidim/gallery/main/video.erb +20 -0
  9. data/app/cells/decidim/gallery/main/video_collection.erb +3 -0
  10. data/app/cells/decidim/gallery/main_cell.rb +88 -0
  11. data/app/cells/decidim/gallery/video/show.erb +6 -0
  12. data/app/cells/decidim/gallery/video_cell.rb +96 -0
  13. data/app/commands/decidim/gallery/admin/create_gallery_item.rb +50 -0
  14. data/app/commands/decidim/gallery/admin/publish_gallery_item.rb +38 -0
  15. data/app/commands/decidim/gallery/admin/unpublish_gallery_item.rb +38 -0
  16. data/app/commands/decidim/gallery/admin/update_gallery_item.rb +44 -0
  17. data/app/controllers/decidim/gallery/admin/application_controller.rb +24 -0
  18. data/app/controllers/decidim/gallery/admin/gallery_item_controller.rb +104 -0
  19. data/app/controllers/decidim/gallery/application_controller.rb +8 -0
  20. data/app/controllers/decidim/gallery/gallery_controller.rb +9 -0
  21. data/app/forms/decidim/gallery/admin/gallery_item_form.rb +17 -0
  22. data/app/forms/decidim/gallery/admin/gallery_item_image_form.rb +14 -0
  23. data/app/forms/decidim/gallery/admin/gallery_item_video_form.rb +12 -0
  24. data/app/helpers/decidim/gallery/admin/application_helper.rb +18 -0
  25. data/app/model/decidim/gallery/application_record.rb +10 -0
  26. data/app/model/decidim/gallery/gallery_item.rb +38 -0
  27. data/app/overrides/decidim/admin/static_pages/edit/add_gallery.html.erb.deface +11 -0
  28. data/app/overrides/decidim/pages/_standalone/add_content_blocks.html.erb.deface +7 -0
  29. data/app/overrides/decidim/pages/_tabbed/add_content_blocks.html.erb.deface +5 -0
  30. data/app/packs/entrypoints/decidim_gallery.js +3 -0
  31. data/app/packs/images/decidim/gallery/icon.svg +1 -0
  32. data/app/packs/src/decidim/gallery/gallery.js +42 -0
  33. data/app/packs/src/decidim/gallery/masonry/EvEmitter.js +85 -0
  34. data/app/packs/src/decidim/gallery/masonry/getSize.js +181 -0
  35. data/app/packs/src/decidim/gallery/masonry/jQueryBridget.js +109 -0
  36. data/app/packs/src/decidim/gallery/masonry/masonry.js +202 -0
  37. data/app/packs/src/decidim/gallery/masonry/matchesSelector.js +25 -0
  38. data/app/packs/src/decidim/gallery/masonry/outlayer.js +885 -0
  39. data/app/packs/src/decidim/gallery/masonry/outlayerItem.js +522 -0
  40. data/app/packs/src/decidim/gallery/masonry/utils.js +203 -0
  41. data/app/packs/stylesheets/decidim/gallery/_gallery.scss +3 -0
  42. data/app/permissions/decidim/gallery/admin/permissions.rb +18 -0
  43. data/app/permissions/decidim/gallery/permissions.rb +17 -0
  44. data/app/views/decidim/gallery/admin/gallery_item/_form_image.html.erb +16 -0
  45. data/app/views/decidim/gallery/admin/gallery_item/_form_video.html.erb +16 -0
  46. data/app/views/decidim/gallery/admin/gallery_item/edit.html.erb +9 -0
  47. data/app/views/decidim/gallery/admin/gallery_item/index.html.erb +52 -0
  48. data/app/views/decidim/gallery/admin/gallery_item/new.html.erb +9 -0
  49. data/app/views/decidim/gallery/gallery/index.html.erb +11 -0
  50. data/config/assets.rb +9 -0
  51. data/config/i18n-tasks.yml +22 -0
  52. data/config/locales/en.yml +79 -0
  53. data/config/locales/fr.yml +79 -0
  54. data/config/locales/ro.yml +79 -0
  55. data/lib/decidim/gallery/admin/static_pages/command.rb +15 -0
  56. data/lib/decidim/gallery/admin/static_pages/form.rb +21 -0
  57. data/lib/decidim/gallery/admin.rb +14 -0
  58. data/lib/decidim/gallery/admin_engine.rb +27 -0
  59. data/lib/decidim/gallery/component.rb +47 -0
  60. data/lib/decidim/gallery/engine.rb +60 -0
  61. data/lib/decidim/gallery/test/factories.rb +58 -0
  62. data/lib/decidim/gallery/version.rb +14 -0
  63. data/lib/decidim/gallery.rb +14 -0
  64. data/lib/tasks/decidim_gallery.rake +16 -0
  65. metadata +163 -0
@@ -0,0 +1,522 @@
1
+ import EvEmitter from "./EvEmitter"
2
+ import getSize from "./getSize";
3
+
4
+ // ----- helpers ----- //
5
+
6
+ function isEmptyObj( obj ) {
7
+ for ( var prop in obj ) {
8
+ return false;
9
+ }
10
+ prop = null;
11
+ return true;
12
+ }
13
+
14
+ // -------------------------- CSS3 support -------------------------- //
15
+
16
+
17
+ var docElemStyle = document.documentElement.style;
18
+
19
+ var transitionProperty = typeof docElemStyle.transition == 'string' ?
20
+ 'transition' : 'WebkitTransition';
21
+ var transformProperty = typeof docElemStyle.transform == 'string' ?
22
+ 'transform' : 'WebkitTransform';
23
+
24
+ var transitionEndEvent = {
25
+ WebkitTransition: 'webkitTransitionEnd',
26
+ transition: 'transitionend'
27
+ }[ transitionProperty ];
28
+
29
+ // cache all vendor properties that could have vendor prefix
30
+ var vendorProperties = {
31
+ transform: transformProperty,
32
+ transition: transitionProperty,
33
+ transitionDuration: transitionProperty + 'Duration',
34
+ transitionProperty: transitionProperty + 'Property',
35
+ transitionDelay: transitionProperty + 'Delay'
36
+ };
37
+
38
+ // -------------------------- Item -------------------------- //
39
+
40
+ function Item( element, layout ) {
41
+ if ( !element ) {
42
+ return;
43
+ }
44
+
45
+ this.element = element;
46
+ // parent layout class, i.e. Masonry, Isotope, or Packery
47
+ this.layout = layout;
48
+ this.position = {
49
+ x: 0,
50
+ y: 0
51
+ };
52
+
53
+ this._create();
54
+ }
55
+
56
+ // inherit EvEmitter
57
+ var proto = Item.prototype = Object.create( EvEmitter.prototype );
58
+ proto.constructor = Item;
59
+
60
+ proto._create = function() {
61
+ // transition objects
62
+ this._transn = {
63
+ ingProperties: {},
64
+ clean: {},
65
+ onEnd: {}
66
+ };
67
+
68
+ this.css({
69
+ position: 'absolute'
70
+ });
71
+ };
72
+
73
+ // trigger specified handler for event type
74
+ proto.handleEvent = function( event ) {
75
+ var method = 'on' + event.type;
76
+ if ( this[ method ] ) {
77
+ this[ method ]( event );
78
+ }
79
+ };
80
+
81
+ proto.getSize = function() {
82
+ this.size = getSize( this.element );
83
+ };
84
+
85
+ /**
86
+ * apply CSS styles to element
87
+ * @param {Object} style
88
+ */
89
+ proto.css = function( style ) {
90
+ var elemStyle = this.element.style;
91
+
92
+ for ( var prop in style ) {
93
+ // use vendor property if available
94
+ var supportedProp = vendorProperties[ prop ] || prop;
95
+ elemStyle[ supportedProp ] = style[ prop ];
96
+ }
97
+ };
98
+
99
+ // measure position, and sets it
100
+ proto.getPosition = function() {
101
+ var style = getComputedStyle( this.element );
102
+ var isOriginLeft = this.layout._getOption('originLeft');
103
+ var isOriginTop = this.layout._getOption('originTop');
104
+ var xValue = style[ isOriginLeft ? 'left' : 'right' ];
105
+ var yValue = style[ isOriginTop ? 'top' : 'bottom' ];
106
+ var x = parseFloat( xValue );
107
+ var y = parseFloat( yValue );
108
+ // convert percent to pixels
109
+ var layoutSize = this.layout.size;
110
+ if ( xValue.indexOf('%') != -1 ) {
111
+ x = ( x / 100 ) * layoutSize.width;
112
+ }
113
+ if ( yValue.indexOf('%') != -1 ) {
114
+ y = ( y / 100 ) * layoutSize.height;
115
+ }
116
+ // clean up 'auto' or other non-integer values
117
+ x = isNaN( x ) ? 0 : x;
118
+ y = isNaN( y ) ? 0 : y;
119
+ // remove padding from measurement
120
+ x -= isOriginLeft ? layoutSize.paddingLeft : layoutSize.paddingRight;
121
+ y -= isOriginTop ? layoutSize.paddingTop : layoutSize.paddingBottom;
122
+
123
+ this.position.x = x;
124
+ this.position.y = y;
125
+ };
126
+
127
+ // set settled position, apply padding
128
+ proto.layoutPosition = function() {
129
+ var layoutSize = this.layout.size;
130
+ var style = {};
131
+ var isOriginLeft = this.layout._getOption('originLeft');
132
+ var isOriginTop = this.layout._getOption('originTop');
133
+
134
+ // x
135
+ var xPadding = isOriginLeft ? 'paddingLeft' : 'paddingRight';
136
+ var xProperty = isOriginLeft ? 'left' : 'right';
137
+ var xResetProperty = isOriginLeft ? 'right' : 'left';
138
+
139
+ var x = this.position.x + layoutSize[ xPadding ];
140
+ // set in percentage or pixels
141
+ style[ xProperty ] = this.getXValue( x );
142
+ // reset other property
143
+ style[ xResetProperty ] = '';
144
+
145
+ // y
146
+ var yPadding = isOriginTop ? 'paddingTop' : 'paddingBottom';
147
+ var yProperty = isOriginTop ? 'top' : 'bottom';
148
+ var yResetProperty = isOriginTop ? 'bottom' : 'top';
149
+
150
+ var y = this.position.y + layoutSize[ yPadding ];
151
+ // set in percentage or pixels
152
+ style[ yProperty ] = this.getYValue( y );
153
+ // reset other property
154
+ style[ yResetProperty ] = '';
155
+
156
+ this.css( style );
157
+ this.emitEvent( 'layout', [ this ] );
158
+ };
159
+
160
+ proto.getXValue = function( x ) {
161
+ var isHorizontal = this.layout._getOption('horizontal');
162
+ return this.layout.options.percentPosition && !isHorizontal ?
163
+ ( ( x / this.layout.size.width ) * 100 ) + '%' : x + 'px';
164
+ };
165
+
166
+ proto.getYValue = function( y ) {
167
+ var isHorizontal = this.layout._getOption('horizontal');
168
+ return this.layout.options.percentPosition && isHorizontal ?
169
+ ( ( y / this.layout.size.height ) * 100 ) + '%' : y + 'px';
170
+ };
171
+
172
+ proto._transitionTo = function( x, y ) {
173
+ this.getPosition();
174
+ // get current x & y from top/left
175
+ var curX = this.position.x;
176
+ var curY = this.position.y;
177
+
178
+ var didNotMove = x == this.position.x && y == this.position.y;
179
+
180
+ // save end position
181
+ this.setPosition( x, y );
182
+
183
+ // if did not move and not transitioning, just go to layout
184
+ if ( didNotMove && !this.isTransitioning ) {
185
+ this.layoutPosition();
186
+ return;
187
+ }
188
+
189
+ var transX = x - curX;
190
+ var transY = y - curY;
191
+ var transitionStyle = {};
192
+ transitionStyle.transform = this.getTranslate( transX, transY );
193
+
194
+ this.transition({
195
+ to: transitionStyle,
196
+ onTransitionEnd: {
197
+ transform: this.layoutPosition
198
+ },
199
+ isCleaning: true
200
+ });
201
+ };
202
+
203
+ proto.getTranslate = function( x, y ) {
204
+ // flip cooridinates if origin on right or bottom
205
+ var isOriginLeft = this.layout._getOption('originLeft');
206
+ var isOriginTop = this.layout._getOption('originTop');
207
+ x = isOriginLeft ? x : -x;
208
+ y = isOriginTop ? y : -y;
209
+ return 'translate3d(' + x + 'px, ' + y + 'px, 0)';
210
+ };
211
+
212
+ // non transition + transform support
213
+ proto.goTo = function( x, y ) {
214
+ this.setPosition( x, y );
215
+ this.layoutPosition();
216
+ };
217
+
218
+ proto.moveTo = proto._transitionTo;
219
+
220
+ proto.setPosition = function( x, y ) {
221
+ this.position.x = parseFloat( x );
222
+ this.position.y = parseFloat( y );
223
+ };
224
+
225
+ // ----- transition ----- //
226
+
227
+ /**
228
+ * @param {Object} style - CSS
229
+ * @param {Function} onTransitionEnd
230
+ */
231
+
232
+ // non transition, just trigger callback
233
+ proto._nonTransition = function( args ) {
234
+ this.css( args.to );
235
+ if ( args.isCleaning ) {
236
+ this._removeStyles( args.to );
237
+ }
238
+ for ( var prop in args.onTransitionEnd ) {
239
+ args.onTransitionEnd[ prop ].call( this );
240
+ }
241
+ };
242
+
243
+ /**
244
+ * proper transition
245
+ * @param {Object} args - arguments
246
+ * @param {Object} to - style to transition to
247
+ * @param {Object} from - style to start transition from
248
+ * @param {Boolean} isCleaning - removes transition styles after transition
249
+ * @param {Function} onTransitionEnd - callback
250
+ */
251
+ proto.transition = function( args ) {
252
+ // redirect to nonTransition if no transition duration
253
+ if ( !parseFloat( this.layout.options.transitionDuration ) ) {
254
+ this._nonTransition( args );
255
+ return;
256
+ }
257
+
258
+ var _transition = this._transn;
259
+ // keep track of onTransitionEnd callback by css property
260
+ for ( var prop in args.onTransitionEnd ) {
261
+ _transition.onEnd[ prop ] = args.onTransitionEnd[ prop ];
262
+ }
263
+ // keep track of properties that are transitioning
264
+ for ( prop in args.to ) {
265
+ _transition.ingProperties[ prop ] = true;
266
+ // keep track of properties to clean up when transition is done
267
+ if ( args.isCleaning ) {
268
+ _transition.clean[ prop ] = true;
269
+ }
270
+ }
271
+
272
+ // set from styles
273
+ if ( args.from ) {
274
+ this.css( args.from );
275
+ // force redraw. http://blog.alexmaccaw.com/css-transitions
276
+ var h = this.element.offsetHeight;
277
+ // hack for JSHint to hush about unused var
278
+ h = null;
279
+ }
280
+ // enable transition
281
+ this.enableTransition( args.to );
282
+ // set styles that are transitioning
283
+ this.css( args.to );
284
+
285
+ this.isTransitioning = true;
286
+
287
+ };
288
+
289
+ // dash before all cap letters, including first for
290
+ // WebkitTransform => -webkit-transform
291
+ function toDashedAll( str ) {
292
+ return str.replace( /([A-Z])/g, function( $1 ) {
293
+ return '-' + $1.toLowerCase();
294
+ });
295
+ }
296
+
297
+ var transitionProps = 'opacity,' + toDashedAll( transformProperty );
298
+
299
+ proto.enableTransition = function(/* style */) {
300
+ // HACK changing transitionProperty during a transition
301
+ // will cause transition to jump
302
+ if ( this.isTransitioning ) {
303
+ return;
304
+ }
305
+
306
+ // make `transition: foo, bar, baz` from style object
307
+ // HACK un-comment this when enableTransition can work
308
+ // while a transition is happening
309
+ // var transitionValues = [];
310
+ // for ( var prop in style ) {
311
+ // // dash-ify camelCased properties like WebkitTransition
312
+ // prop = vendorProperties[ prop ] || prop;
313
+ // transitionValues.push( toDashedAll( prop ) );
314
+ // }
315
+ // munge number to millisecond, to match stagger
316
+ var duration = this.layout.options.transitionDuration;
317
+ duration = typeof duration == 'number' ? duration + 'ms' : duration;
318
+ // enable transition styles
319
+ this.css({
320
+ transitionProperty: transitionProps,
321
+ transitionDuration: duration,
322
+ transitionDelay: this.staggerDelay || 0
323
+ });
324
+ // listen for transition end event
325
+ this.element.addEventListener( transitionEndEvent, this, false );
326
+ };
327
+
328
+ // ----- events ----- //
329
+
330
+ proto.onwebkitTransitionEnd = function( event ) {
331
+ this.ontransitionend( event );
332
+ };
333
+
334
+ proto.onotransitionend = function( event ) {
335
+ this.ontransitionend( event );
336
+ };
337
+
338
+ // properties that I munge to make my life easier
339
+ var dashedVendorProperties = {
340
+ '-webkit-transform': 'transform'
341
+ };
342
+
343
+ proto.ontransitionend = function( event ) {
344
+ // disregard bubbled events from children
345
+ if ( event.target !== this.element ) {
346
+ return;
347
+ }
348
+ var _transition = this._transn;
349
+ // get property name of transitioned property, convert to prefix-free
350
+ var propertyName = dashedVendorProperties[ event.propertyName ] || event.propertyName;
351
+
352
+ // remove property that has completed transitioning
353
+ delete _transition.ingProperties[ propertyName ];
354
+ // check if any properties are still transitioning
355
+ if ( isEmptyObj( _transition.ingProperties ) ) {
356
+ // all properties have completed transitioning
357
+ this.disableTransition();
358
+ }
359
+ // clean style
360
+ if ( propertyName in _transition.clean ) {
361
+ // clean up style
362
+ this.element.style[ event.propertyName ] = '';
363
+ delete _transition.clean[ propertyName ];
364
+ }
365
+ // trigger onTransitionEnd callback
366
+ if ( propertyName in _transition.onEnd ) {
367
+ var onTransitionEnd = _transition.onEnd[ propertyName ];
368
+ onTransitionEnd.call( this );
369
+ delete _transition.onEnd[ propertyName ];
370
+ }
371
+
372
+ this.emitEvent( 'transitionEnd', [ this ] );
373
+ };
374
+
375
+ proto.disableTransition = function() {
376
+ this.removeTransitionStyles();
377
+ this.element.removeEventListener( transitionEndEvent, this, false );
378
+ this.isTransitioning = false;
379
+ };
380
+
381
+ /**
382
+ * removes style property from element
383
+ * @param {Object} style
384
+ **/
385
+ proto._removeStyles = function( style ) {
386
+ // clean up transition styles
387
+ var cleanStyle = {};
388
+ for ( var prop in style ) {
389
+ cleanStyle[ prop ] = '';
390
+ }
391
+ this.css( cleanStyle );
392
+ };
393
+
394
+ var cleanTransitionStyle = {
395
+ transitionProperty: '',
396
+ transitionDuration: '',
397
+ transitionDelay: ''
398
+ };
399
+
400
+ proto.removeTransitionStyles = function() {
401
+ // remove transition
402
+ this.css( cleanTransitionStyle );
403
+ };
404
+
405
+ // ----- stagger ----- //
406
+
407
+ proto.stagger = function( delay ) {
408
+ delay = isNaN( delay ) ? 0 : delay;
409
+ this.staggerDelay = delay + 'ms';
410
+ };
411
+
412
+ // ----- show/hide/remove ----- //
413
+
414
+ // remove element from DOM
415
+ proto.removeElem = function() {
416
+ this.element.parentNode.removeChild( this.element );
417
+ // remove display: none
418
+ this.css({ display: '' });
419
+ this.emitEvent( 'remove', [ this ] );
420
+ };
421
+
422
+ proto.remove = function() {
423
+ // just remove element if no transition support or no transition
424
+ if ( !transitionProperty || !parseFloat( this.layout.options.transitionDuration ) ) {
425
+ this.removeElem();
426
+ return;
427
+ }
428
+
429
+ // start transition
430
+ this.once( 'transitionEnd', function() {
431
+ this.removeElem();
432
+ });
433
+ this.hide();
434
+ };
435
+
436
+ proto.reveal = function() {
437
+ delete this.isHidden;
438
+ // remove display: none
439
+ this.css({ display: '' });
440
+
441
+ var options = this.layout.options;
442
+
443
+ var onTransitionEnd = {};
444
+ var transitionEndProperty = this.getHideRevealTransitionEndProperty('visibleStyle');
445
+ onTransitionEnd[ transitionEndProperty ] = this.onRevealTransitionEnd;
446
+
447
+ this.transition({
448
+ from: options.hiddenStyle,
449
+ to: options.visibleStyle,
450
+ isCleaning: true,
451
+ onTransitionEnd: onTransitionEnd
452
+ });
453
+ };
454
+
455
+ proto.onRevealTransitionEnd = function() {
456
+ // check if still visible
457
+ // during transition, item may have been hidden
458
+ if ( !this.isHidden ) {
459
+ this.emitEvent('reveal');
460
+ }
461
+ };
462
+
463
+ /**
464
+ * get style property use for hide/reveal transition end
465
+ * @param {String} styleProperty - hiddenStyle/visibleStyle
466
+ * @returns {String}
467
+ */
468
+ proto.getHideRevealTransitionEndProperty = function( styleProperty ) {
469
+ var optionStyle = this.layout.options[ styleProperty ];
470
+ // use opacity
471
+ if ( optionStyle.opacity ) {
472
+ return 'opacity';
473
+ }
474
+ // get first property
475
+ for ( var prop in optionStyle ) {
476
+ return prop;
477
+ }
478
+ };
479
+
480
+ proto.hide = function() {
481
+ // set flag
482
+ this.isHidden = true;
483
+ // remove display: none
484
+ this.css({ display: '' });
485
+
486
+ var options = this.layout.options;
487
+
488
+ var onTransitionEnd = {};
489
+ var transitionEndProperty = this.getHideRevealTransitionEndProperty('hiddenStyle');
490
+ onTransitionEnd[ transitionEndProperty ] = this.onHideTransitionEnd;
491
+
492
+ this.transition({
493
+ from: options.visibleStyle,
494
+ to: options.hiddenStyle,
495
+ // keep hidden stuff hidden
496
+ isCleaning: true,
497
+ onTransitionEnd: onTransitionEnd
498
+ });
499
+ };
500
+
501
+ proto.onHideTransitionEnd = function() {
502
+ // check if still hidden
503
+ // during transition, item may have been un-hidden
504
+ if ( this.isHidden ) {
505
+ this.css({ display: 'none' });
506
+ this.emitEvent('hide');
507
+ }
508
+ };
509
+
510
+ proto.destroy = function() {
511
+ this.css({
512
+ position: '',
513
+ left: '',
514
+ right: '',
515
+ top: '',
516
+ bottom: '',
517
+ transition: '',
518
+ transform: ''
519
+ });
520
+ };
521
+
522
+ export default Item;
@@ -0,0 +1,203 @@
1
+ import matchesSelector from "./matchesSelector";
2
+
3
+ var utils = {};
4
+
5
+ // ----- extend ----- //
6
+
7
+ // extends objects
8
+ utils.extend = function( a, b ) {
9
+ for ( var prop in b ) {
10
+ a[ prop ] = b[ prop ];
11
+ }
12
+ return a;
13
+ };
14
+
15
+ // ----- modulo ----- //
16
+
17
+ utils.modulo = function( num, div ) {
18
+ return ( ( num % div ) + div ) % div;
19
+ };
20
+
21
+ // ----- makeArray ----- //
22
+
23
+ var arraySlice = Array.prototype.slice;
24
+
25
+ // turn element or nodeList into an array
26
+ utils.makeArray = function( obj ) {
27
+ if ( Array.isArray( obj ) ) {
28
+ // use object if already an array
29
+ return obj;
30
+ }
31
+ // return empty array if undefined or null. #6
32
+ if ( obj === null || obj === undefined ) {
33
+ return [];
34
+ }
35
+
36
+ var isArrayLike = typeof obj == 'object' && typeof obj.length == 'number';
37
+ if ( isArrayLike ) {
38
+ // convert nodeList to array
39
+ return arraySlice.call( obj );
40
+ }
41
+
42
+ // array of single index
43
+ return [ obj ];
44
+ };
45
+
46
+ // ----- removeFrom ----- //
47
+
48
+ utils.removeFrom = function( ary, obj ) {
49
+ var index = ary.indexOf( obj );
50
+ if ( index != -1 ) {
51
+ ary.splice( index, 1 );
52
+ }
53
+ };
54
+
55
+ // ----- getParent ----- //
56
+
57
+ utils.getParent = function( elem, selector ) {
58
+ while ( elem.parentNode && elem != document.body ) {
59
+ elem = elem.parentNode;
60
+ if ( matchesSelector( elem, selector ) ) {
61
+ return elem;
62
+ }
63
+ }
64
+ };
65
+
66
+ // ----- getQueryElement ----- //
67
+
68
+ // use element as selector string
69
+ utils.getQueryElement = function( elem ) {
70
+ if ( typeof elem == 'string' ) {
71
+ return document.querySelector( elem );
72
+ }
73
+ return elem;
74
+ };
75
+
76
+ // ----- handleEvent ----- //
77
+
78
+ // enable .ontype to trigger from .addEventListener( elem, 'type' )
79
+ utils.handleEvent = function( event ) {
80
+ var method = 'on' + event.type;
81
+ if ( this[ method ] ) {
82
+ this[ method ]( event );
83
+ }
84
+ };
85
+
86
+ // ----- filterFindElements ----- //
87
+
88
+ utils.filterFindElements = function( elems, selector ) {
89
+ // make array of elems
90
+ elems = utils.makeArray( elems );
91
+ var ffElems = [];
92
+
93
+ elems.forEach( function( elem ) {
94
+ // check that elem is an actual element
95
+ if ( !( elem instanceof HTMLElement ) ) {
96
+ return;
97
+ }
98
+ // add elem if no selector
99
+ if ( !selector ) {
100
+ ffElems.push( elem );
101
+ return;
102
+ }
103
+ // filter & find items if we have a selector
104
+ // filter
105
+ if ( matchesSelector( elem, selector ) ) {
106
+ ffElems.push( elem );
107
+ }
108
+ // find children
109
+ var childElems = elem.querySelectorAll( selector );
110
+ // concat childElems to filterFound array
111
+ for ( var i=0; i < childElems.length; i++ ) {
112
+ ffElems.push( childElems[i] );
113
+ }
114
+ });
115
+
116
+ return ffElems;
117
+ };
118
+
119
+ // ----- debounceMethod ----- //
120
+
121
+ utils.debounceMethod = function( _class, methodName, threshold ) {
122
+ threshold = threshold || 100;
123
+ // original method
124
+ var method = _class.prototype[ methodName ];
125
+ var timeoutName = methodName + 'Timeout';
126
+
127
+ _class.prototype[ methodName ] = function() {
128
+ var timeout = this[ timeoutName ];
129
+ clearTimeout( timeout );
130
+
131
+ var args = arguments;
132
+ var _this = this;
133
+ this[ timeoutName ] = setTimeout( function() {
134
+ method.apply( _this, args );
135
+ delete _this[ timeoutName ];
136
+ }, threshold );
137
+ };
138
+ };
139
+
140
+ // ----- docReady ----- //
141
+
142
+ utils.docReady = function( callback ) {
143
+ var readyState = document.readyState;
144
+ if ( readyState == 'complete' || readyState == 'interactive' ) {
145
+ // do async to allow for other scripts to run. metafizzy/flickity#441
146
+ setTimeout( callback );
147
+ } else {
148
+ document.addEventListener( 'DOMContentLoaded', callback );
149
+ }
150
+ };
151
+
152
+ // ----- htmlInit ----- //
153
+
154
+ // http://jamesroberts.name/blog/2010/02/22/string-functions-for-javascript-trim-to-camel-case-to-dashed-and-to-underscore/
155
+ utils.toDashed = function( str ) {
156
+ return str.replace( /(.)([A-Z])/g, function( match, $1, $2 ) {
157
+ return $1 + '-' + $2;
158
+ }).toLowerCase();
159
+ };
160
+
161
+ var console = window.console;
162
+ /**
163
+ * allow user to initialize classes via [data-namespace] or .js-namespace class
164
+ * htmlInit( Widget, 'widgetName' )
165
+ * options are parsed from data-namespace-options
166
+ */
167
+ utils.htmlInit = function( WidgetClass, namespace ) {
168
+ utils.docReady( function() {
169
+ var dashedNamespace = utils.toDashed( namespace );
170
+ var dataAttr = 'data-' + dashedNamespace;
171
+ var dataAttrElems = document.querySelectorAll( '[' + dataAttr + ']' );
172
+ var jsDashElems = document.querySelectorAll( '.js-' + dashedNamespace );
173
+ var elems = utils.makeArray( dataAttrElems )
174
+ .concat( utils.makeArray( jsDashElems ) );
175
+ var dataOptionsAttr = dataAttr + '-options';
176
+ var jQuery = window.jQuery;
177
+
178
+ elems.forEach( function( elem ) {
179
+ var attr = elem.getAttribute( dataAttr ) ||
180
+ elem.getAttribute( dataOptionsAttr );
181
+ var options;
182
+ try {
183
+ options = attr && JSON.parse( attr );
184
+ } catch ( error ) {
185
+ // log error, do not initialize
186
+ if ( console ) {
187
+ console.error( 'Error parsing ' + dataAttr + ' on ' + elem.className +
188
+ ': ' + error );
189
+ }
190
+ return;
191
+ }
192
+ // initialize
193
+ var instance = new WidgetClass( elem, options );
194
+ // make available via $().data('namespace')
195
+ if ( jQuery ) {
196
+ jQuery.data( elem, namespace, instance );
197
+ }
198
+ });
199
+
200
+ });
201
+ };
202
+
203
+ export default utils;