Shapely Companion - Version 1.2.4

Version Description

  • Implemented milestone https://github.com/puikinsh/shapely/milestone/8?closed=1
Download this release

Release Info

Developer machothemes
Plugin Icon wp plugin Shapely Companion
Version 1.2.4
Comparing to
See all releases

Version 1.2.4

Files changed (40) hide show
  1. assets/css/admin.css +51 -0
  2. assets/img/placeholder-image.jpg +0 -0
  3. assets/img/placeholder.jpg +0 -0
  4. assets/img/placeholder_wide.jpg +0 -0
  5. assets/js/admin.js +113 -0
  6. assets/js/nav-menu.js +46 -0
  7. assets/js/previewer.js +74 -0
  8. assets/js/vendor/jquery-cloneya.js +518 -0
  9. assets/js/vendor/jquery-cloneya.min.js +1 -0
  10. assets/js/vendor/jquery.vide.js +501 -0
  11. assets/js/vendor/jquery.vide.min.js +9 -0
  12. assets/js/vendor/jquery.youtubebackground.js +334 -0
  13. assets/js/vendor/player.js +2119 -0
  14. assets/js/vendor/player.min.js +2 -0
  15. assets/js/widget.js +113 -0
  16. inc/class-shapely-walker-nav-menu-edit.php +208 -0
  17. inc/epsilon-dashboard/class-epsilon-dashboard.php +224 -0
  18. inc/shapely-demo-content.php +152 -0
  19. inc/shapely-enqueues.php +33 -0
  20. inc/shapely-helper.php +64 -0
  21. inc/shapely-metabox.php +84 -0
  22. inc/shapely-navmenu.php +95 -0
  23. inc/shapely-widgets.php +53 -0
  24. inc/views/shapely-demo-content.php +33 -0
  25. inc/widgets/class-shapely-categories.php +148 -0
  26. inc/widgets/class-shapely-home-call-for-action.php +122 -0
  27. inc/widgets/class-shapely-home-clients.php +177 -0
  28. inc/widgets/class-shapely-home-contact.php +267 -0
  29. inc/widgets/class-shapely-home-features.php +752 -0
  30. inc/widgets/class-shapely-home-parallax.php +284 -0
  31. inc/widgets/class-shapely-home-portfolio.php +254 -0
  32. inc/widgets/class-shapely-home-testimonials.php +170 -0
  33. inc/widgets/class-shapely-page-content.php +70 -0
  34. inc/widgets/class-shapely-page-title.php +48 -0
  35. inc/widgets/class-shapely-recent-posts.php +164 -0
  36. inc/widgets/class-shapely-social.php +115 -0
  37. inc/widgets/class-shapely-video.php +250 -0
  38. languages/shapely.pot +426 -0
  39. readme.txt +90 -0
  40. shapely-companion.php +92 -0
assets/css/admin.css ADDED
@@ -0,0 +1,51 @@
1
+ .shapely-media-control img {
2
+ width: 100%;
3
+ height: auto;
4
+ }
5
+
6
+ .logo_heading {
7
+ display: block;
8
+ width: 100%;
9
+ }
10
+
11
+ #setting-error-tgmpa.notice {
12
+ display: block;
13
+ }
14
+
15
+ .client-sortable .logo_heading {
16
+ background: #f3f3f3;
17
+ border: 1px dotted;
18
+ cursor: move;
19
+ display: block;
20
+ font-size: 14px;
21
+ padding: 8px 0;
22
+ text-align: center;
23
+ width: 100%;
24
+ }
25
+
26
+ .client-sortable .logo_heading:hover {
27
+ border: 1px solid;
28
+ }
29
+
30
+ .client-sortable .cloneya a.clone,
31
+ .client-sortable .cloneya a.delete {
32
+ display: none;
33
+ }
34
+
35
+ .client-sortable .cloneya:last-child a.clone,
36
+ .client-sortable .cloneya:last-child a.delete {
37
+ display: inline-block;
38
+ }
39
+
40
+ .shapely-media-control img {
41
+ width: 100%;
42
+ }
43
+
44
+ .shapely-editor-container label {
45
+ position: relative;
46
+ top: 30px;
47
+ }
48
+
49
+ .checkbox_switch {
50
+ margin: 10px auto;
51
+ }
assets/img/placeholder-image.jpg ADDED
Binary file
assets/img/placeholder.jpg ADDED
Binary file
assets/img/placeholder_wide.jpg ADDED
Binary file
assets/js/admin.js ADDED
@@ -0,0 +1,113 @@
1
+ jQuery( document ).ready(function() {// jscs:ignore validateLineBreaks
2
+
3
+ jQuery( '#demo_content .button' ).click(function( evt ) {
4
+ var currentButton = jQuery( this );
5
+ var ajaxData = { 'action': 'shapely_companion_import_content', 'import': jQuery( this ).data( 'action' ) };
6
+ evt.preventDefault();
7
+ jQuery( this ).addClass( 'disabled' );
8
+ jQuery( this ).next( '.spinner' ).addClass( 'is-active' );
9
+ jQuery.ajax({
10
+ type: 'POST',
11
+ data: ajaxData,
12
+ url: shapelyCompanion.ajaxurl,
13
+ success: function( data ) {
14
+ if ( 'succes' === data ) {
15
+ currentButton.removeClass( 'disabled' );
16
+ currentButton.next( '.spinner' ).removeClass( 'is-active' );
17
+ currentButton.parent().parent().find( '.updated-message' ).show();
18
+ location.reload();
19
+ }
20
+
21
+ }
22
+
23
+ });
24
+
25
+ });
26
+
27
+ });
28
+
29
+ jQuery(function( $ ) {
30
+ var mediaControl = {
31
+
32
+ // Initializes a new media manager or returns an existing frame.
33
+ // @see wp.media.featuredImage.frame()
34
+ selector: null,
35
+ size: null,
36
+ container: null,
37
+ frame: function() {
38
+ if ( this._frame ) {
39
+ return this._frame;
40
+
41
+ }
42
+
43
+ this._frame = wp.media({
44
+ title: 'Media',
45
+ button: {
46
+ text: 'Update'
47
+ },
48
+ multiple: false
49
+ });
50
+
51
+ this._frame.on( 'open', this.updateFrame ).state( 'library' ).on( 'select', this.select );
52
+
53
+ return this._frame;
54
+
55
+ },
56
+
57
+ select: function() {
58
+
59
+ // Do something when the "update" button is clicked after a selection is made.
60
+ var id = $( '.attachments' ).find( '.selected' ).attr( 'data-id' );
61
+ var selector = $( '.shapely-media-control' ).find( mediaControl.selector );
62
+ var data = {
63
+ action: 'shapely_get_attachment_media',
64
+ attachment_id: id
65
+ };
66
+
67
+ if ( ! selector.length ) {
68
+ return false;
69
+
70
+ }
71
+
72
+ jQuery.post( shapelyCompanion.ajaxurl, data, function( response ) {
73
+ var ext = response.substr( ( response.lastIndexOf( '.' ) + 1 ) );
74
+ if ( 'mp4' !== ext ) {
75
+ $( mediaControl.container ).find( 'img' ).attr( 'src', response );
76
+ }
77
+
78
+ selector.val( response ).change();
79
+
80
+ });
81
+
82
+ },
83
+
84
+ init: function() {
85
+ var context = $( '#wpbody, .wp-customizer' );
86
+ context.on( 'click', '.shapely-media-control > .upload-button', function( e ) {
87
+ var container = $( this ).parent(),
88
+ sibling = container.find( '.image-id' ),
89
+ id = sibling.attr( 'id' );
90
+ e.preventDefault();
91
+ mediaControl.size = $( '[data-delegate="' + id + '"]' ).val();
92
+ mediaControl.container = container;
93
+ mediaControl.selector = '#' + id;
94
+ mediaControl.frame().open();
95
+
96
+ });
97
+
98
+ context.on( 'click', '.shapely-media-control > .remove-button', function( e ) {
99
+ var container = $( this ).parent(),
100
+ sibling = container.find( '.image-id' ),
101
+ img = container.find( 'img' );
102
+ e.preventDefault();
103
+ img.attr( 'src', img.attr( 'data-default' ) );
104
+ sibling.val( '' ).trigger( 'change' );
105
+
106
+ });
107
+
108
+ }
109
+
110
+ };
111
+
112
+ mediaControl.init();
113
+ });
assets/js/nav-menu.js ADDED
@@ -0,0 +1,46 @@
1
+ (function( $ ) {// jscs:ignore validateLineBreaks
2
+
3
+ 'use strict';
4
+ var api;
5
+
6
+ api = wpNavMenu;
7
+
8
+ $( '#submit-shapelysection' ).click( function( evt ) {
9
+ var section = $( '#shapelysectionsdiv' ).find( '#shapely-section-item-widget' ).val(),
10
+ label = $( '#shapelysectionsdiv' ).find( '#shapely-section-item-name' ).val(),
11
+ url = $( '#shapelysectionsdiv' ).find( '#shapely-section-item-url' ).val();
12
+
13
+ evt.preventDefault();
14
+
15
+ if ( '0' === section || '' === label || '' === url ) {
16
+ $( '#shapelysectionsdiv' ).addClass( 'form-invalid' );
17
+ return false;
18
+ }
19
+
20
+ $( '.customlinkdiv .spinner' ).addClass( 'is-active' );
21
+
22
+ api.addItemToMenu( {
23
+ '-1': {
24
+ 'menu-item-type': 'custom',
25
+ 'menu-item-extra': 'shapely-section',
26
+ 'menu-item-url': url,
27
+ 'menu-item-widget': section,
28
+ 'menu-item-title': label
29
+ }
30
+ }, api.addMenuItemToBottom, shapelyMenuAdded );
31
+
32
+ } );
33
+
34
+ function shapelyMenuAdded() {
35
+
36
+ // Remove the ajax spinner
37
+ $( '#shapelysectionsdiv .spinner' ).removeClass( 'is-active' );
38
+
39
+ // Set custom link form back to defaults
40
+ $( '#shapelysectionsdiv #shapely-section-item-widget' ).val( '0' ).blur();
41
+ $( '#shapelysectionsdiv #shapely-section-item-url' ).val( '' ).blur();
42
+ $( '#shapelysectionsdiv #shapely-section-item-name' ).val( '' ).blur();
43
+
44
+ }
45
+
46
+ })( jQuery );
assets/js/previewer.js ADDED
@@ -0,0 +1,74 @@
1
+ (function( $ ) {// jscs:ignore validateLineBreaks
2
+
3
+ 'use strict';
4
+
5
+ $( document ).ready( function() {
6
+ if ( 'undefined' === typeof wp || ! wp.customize || ! wp.customize.selectiveRefresh ) {
7
+ return;
8
+ }
9
+
10
+ wp.customize.selectiveRefresh.bind( 'widget-updated', function( placement ) {
11
+ var maxHeight, container, msnry, element;
12
+ $( '.logo-carousel' ).flexslider( {
13
+ minItems: 1,
14
+ maxItems: 4,
15
+ move: 1,
16
+ itemWidth: 200,
17
+ itemMargin: 0,
18
+ animation: 'slide',
19
+ slideshow: true,
20
+ slideshowSpeed: 3000,
21
+ directionNav: false,
22
+ controlNav: false
23
+ } );
24
+
25
+ if ( 'function' === typeof $( '.parallax-window' ).parallax ) {
26
+ $( '.parallax-window' ).parallax();
27
+ }
28
+
29
+ if ( $( '.masonry' ).length && 'undefined' !== typeof Masonry ) {
30
+ container = document.querySelector( '.masonry' );
31
+ msnry = new Masonry( container, {
32
+ itemSelector: '.masonry-item'
33
+ } );
34
+ maxHeight = -1;
35
+ msnry.on( 'layoutComplete', function() {
36
+ var element = jQuery( msnry.element ),
37
+ cols = element.find( '.masonry-item img' );
38
+ jQuery.each( cols, function() {
39
+ if ( parseInt( jQuery( this ).attr( 'height' ), 10 ) > maxHeight ) {
40
+ maxHeight = parseInt( jQuery( this ).attr( 'height' ), 10 );
41
+ }
42
+ } );
43
+
44
+ } );
45
+
46
+ msnry.layout();
47
+ element = jQuery( msnry.element );
48
+
49
+ jQuery( element ).css( 'height', maxHeight + 'px' );
50
+ }
51
+
52
+ if ( 0 !== jQuery( '.testimonial-section' ).length ) {
53
+ testimonialHeight();
54
+ setTimeout( function() {
55
+ testimonialHeight();
56
+ }, 3000 );
57
+ }
58
+
59
+ $( '.slider-arrow-controls' ).flexslider( {
60
+ controlNav: false
61
+ } );
62
+
63
+ /*
64
+ * Resetting testimonial parallax height
65
+ */
66
+ function testimonialHeight() {
67
+ var testimonialHeight = jQuery( '.testimonial-section .parallax-window .container' ).outerHeight() + 150;
68
+ jQuery( '.testimonial-section .parallax-window' ).css( 'height', testimonialHeight );
69
+ jQuery( window ).trigger( 'resize' ).trigger( 'scroll' );
70
+ }
71
+ } );
72
+ } );
73
+
74
+ })( jQuery );
assets/js/vendor/jquery-cloneya.js ADDED
@@ -0,0 +1,518 @@
1
+ /**
2
+ * CloneYa!: Plugin to clone form elements in a nested manner
3
+ * @author Saurabh Shukla <saurabh@yapapaya.com>
4
+ * http://hookrefineandtinker.com
5
+ * License GNU/GPL & MIT
6
+ */
7
+
8
+ (function( $ ) {
9
+
10
+ "use strict";
11
+
12
+ var name = "cloneya", defaults = {
13
+ cloneThis: '.toclone',
14
+ cloneButton: '.clone',
15
+ deleteButton: '.delete',
16
+ clonePosition: 'after',
17
+ minimum: 1,
18
+ // renaming limit
19
+ maximum: 999, //setting it to a high number, by default
20
+
21
+ //limit: 999,
22
+
23
+ valueClone: false,
24
+ dataClone: false,
25
+ deepClone: false,
26
+ serializeID: true,
27
+ ignore: 'label.error',
28
+ preserveChildCount: false
29
+ };
30
+
31
+ /**
32
+ * Create the class CloneYa
33
+ *
34
+ * @class CloneYa
35
+ * @classdesc Adds cloning functionality to element
36
+ *
37
+ * @param {String | Object} element - the clone wrapper
38
+ *
39
+ * @param {Object} options - options to initialise with
40
+ *
41
+ * @param {String} options.cloneThis - Selector for the clone element
42
+ * @param {String} options.cloneButton - Selector for the clone button
43
+ * @param {String} options.deleteButton - Selector for the delete button
44
+ *
45
+ * @param {String} options.clonePosition - Where should the clone be added 'before' or 'after'
46
+ *
47
+ * @param {Number} options.limit - The maximum number of clones
48
+ *
49
+ * @param {Boolean} options.valueClone - Clone the input values as well?
50
+ * @param {Boolean} options.dataClone - Clone the data attributes?
51
+ * @param {Boolean} options.deepClone - Clone other data added to the jQuery object
52
+ *
53
+ * @param {Boolean} options.serializeID - Whether to serialize the IDs, automatically
54
+ * @param {String} options.ignore - Selectors for clonables' elements that should not be cloned
55
+ * @param {Boolean} options.defaultRender - Start with this number of clones, by default
56
+ * @param {Boolean} options.preserveChildCount - whether to preserve the initial number of clone's child clones, works with nesting as well.
57
+ *
58
+ * @returns {_L13.CloneYa}
59
+ */
60
+ function CloneYa( element, options ) {
61
+ /**
62
+ * regex for recalculating the ids
63
+ *
64
+ * @type RegExp
65
+ */
66
+ this.regex = /^(.*)(\d)+#x2F;i;
67
+
68
+ this.elem = element;
69
+
70
+ this.$elem = $( element );
71
+
72
+ this.elemClass = name + '-wrap';
73
+
74
+ /**
75
+ * creating a jQuery object, just in case
76
+ *
77
+ * @type @call;$
78
+ */
79
+ //var elem = $(element);
80
+
81
+ /**
82
+ * Support deprecated parameters
83
+ */
84
+ if ( typeof options !== 'undefined' ) {
85
+ if ( typeof options.limit !== 'undefined' && options.limit > 0 ) {
86
+ options.maximum = options.limit;
87
+ }
88
+ }
89
+
90
+ /**
91
+ * merge the passed options object with defaults
92
+ *
93
+ * @type @exp;$@call;extend
94
+ */
95
+ this.config = $.extend( {}, defaults, options );
96
+
97
+ /**
98
+ *
99
+ * @type @exp;elem@call;closestChild
100
+ */
101
+ this.clones = this.$elem.closestChild( this.config.cloneThis );
102
+
103
+ this.init();
104
+
105
+ }
106
+
107
+ CloneYa.prototype = {
108
+ init: function() {
109
+
110
+ var $this = this;
111
+
112
+ // add our classes
113
+ $this.$elem.addClass( $this.elemClass );
114
+ $this.clones.addClass( name );
115
+
116
+ // save the sibling count into data attr
117
+ $this.clones.data( 'initialCount', $this.clones.length );
118
+
119
+ //Now, what if the clone button and delete button are not contained in
120
+ //the clonable?
121
+ // add a click handler for the clone buttons
122
+ $this.$elem.on( 'click.' + name, $this.config.cloneThis + '>' + $this.config.cloneButton, function( event ) {
123
+ event.preventDefault();
124
+ event.stopPropagation();
125
+
126
+ var toClone = $( this ).closest( $this.config.cloneThis );
127
+
128
+ // this is just a wrapper for the custom clone event
129
+ $this.$elem.triggerAll( 'clone_clone clone.' + name, [ toClone ] );
130
+ } );
131
+
132
+ // the custom clone event
133
+ $this.$elem.on( 'clone.' + name, function( event, toClone ) {
134
+ if ( event.namespace === name ) {
135
+ $this._cloneAndAppend( toClone );
136
+ }
137
+ } );
138
+
139
+ // click handler for delete button
140
+ $this.$elem.on( 'click.' + name, $this.config.cloneThis + '>' + $this.config.deleteButton, function( event ) {
141
+ event.preventDefault();
142
+ event.stopPropagation();
143
+
144
+ var toDelete = $( this ).closest( $this.config.cloneThis );
145
+ // just a wrapper for delclone event
146
+ $this.$elem.triggerAll( 'clone_delete delete.' + name, [ toDelete ] );
147
+ } );
148
+
149
+ // the delete clone event
150
+ $this.$elem.on( 'delete.' + name, function( event, toDelete ) {
151
+
152
+ // get the count of all the sibling clones
153
+ /**
154
+ *
155
+ * @type @exp;$todelete@call;closest@call;closestChild@pro;length
156
+ */
157
+ var cloneCount = toDelete.closest( '.' + $this.elemClass ).closestChild( $this.config.cloneThis ).length;
158
+
159
+ if ( cloneCount > $this.config.minimum ) {
160
+ // trigger hook
161
+ $this.$elem.triggerAll( 'clone_before_delete before_delete.' + name, [ toDelete, cloneCount ] );
162
+ $this.$elem.triggerHandler( 'remove.' + name, [ toDelete ] );
163
+ $this.$elem.triggerAll( 'clone_after_delete after_delete.' + name );
164
+
165
+ }
166
+ else {
167
+
168
+ $this.$elem.triggerHandler( 'minimum.' + name, $this.config.minimum, [ toDelete ] );
169
+
170
+ // First clone form can't be deleted, but the values should be removed from first form
171
+ // is this expected behaviour? especially since we use minimum?
172
+ toDelete.find( 'input, textarea, select' ).each( function() {
173
+ $this._clearForm( $( this ) );
174
+ } );
175
+
176
+ }
177
+ } );
178
+
179
+ $this.$elem.on( 'remove.' + name, function( event, toDelete ) {
180
+ $( toDelete ).remove();
181
+
182
+ } );
183
+
184
+ },
185
+ _clean: function() {
186
+ var $this = this;
187
+ $this.$elem.removeClass( name + '-wrap' );
188
+ $this.clones.removeClass( name );
189
+ $this.$elem.off( 'click.' + name, $this.config.cloneThis + '>' + $this.config.cloneButton );
190
+ $this.$elem.off( 'click.' + name, $this.config.cloneThis + '>' + $this.config.deleteButton );
191
+ $this.$elem.off( 'clone_clone clone_delete clone_before_delete clone.' + name + ' delete.' + name + ' before_delete.' + name );
192
+
193
+ },
194
+ destroy: function() {
195
+ this._clean();
196
+ this.$elem.removeData( name );
197
+ },
198
+ getOption: function() {
199
+ return this.config;
200
+ },
201
+ setOption: function( lateOptions ) {
202
+ $.extend( this.config, lateOptions || {} );
203
+ this._clean();
204
+ this.init();
205
+
206
+ },
207
+ _cloneAndAppend: function( toClone ) {
208
+
209
+
210
+ // get the count of all the sibling clones
211
+ /**
212
+ *
213
+ * @type @exp;$toclone@call;closest@call;closestChild@pro;length
214
+ */
215
+ var cloneCount = toClone.closest( '.' + this.elemClass ).closestChild( this.config.cloneThis ).length;
216
+
217
+ // check if we've reached the maximum limit
218
+ if ( cloneCount < this.config.maximum ) {
219
+
220
+ // trigger a custom event for hooking in
221
+ this.$elem.triggerAll( 'clone_before_clone before_clone.' + name, [ toClone ] );
222
+
223
+ var newClone = this._cloneItem( toClone );
224
+
225
+ // trigger custom event on the original element
226
+ this.$elem.triggerAll( 'clone_after_clone after_clone.' + name, [ toClone, newClone ] );
227
+
228
+ // add to our clones object
229
+ this.clones.add( newClone );
230
+
231
+ // trigger custom event on the new clone
232
+ this.$elem.triggerAll( 'clone_before_append before_append.' + name, [ toClone, newClone ] );
233
+
234
+ // get the position where the clone has to be added
235
+ // and add the newclone
236
+ if ( this.config.clonePosition !== 'after' ) {
237
+ toClone.before( newClone );
238
+ } else {
239
+ toClone.after( newClone );
240
+
241
+ }
242
+
243
+ if ( this.config.ignore ) {
244
+ newClone.find( this.config.ignore ).remove();
245
+ }
246
+
247
+ // reformat the id attributes
248
+ this._redoIDs();
249
+
250
+ // trigger custom event for hooking
251
+ this.$elem.triggerAll( 'clone_after_append after_append.' + name, [ toClone, newClone ] );
252
+ } else {
253
+ // trigger a custom event for hooking
254
+ this.$elem.triggerAll( 'clone_limit maximum.' + name, this.config.maximum, [ toClone ] );
255
+ }
256
+
257
+ },
258
+ _cloneItem: function( toClone ) {
259
+ var $this = this;
260
+
261
+ // clone it
262
+ /**
263
+ *
264
+ * @type @exp;$toclone@call;clone
265
+ */
266
+ var newClone = toClone.clone( $this.config.dataClone, $this.config.deepClone );
267
+
268
+ // we want to preserve the initial child count
269
+ if ( $this.config.preserveChildCount !== false ) {
270
+ // the child count only needs preservation if they are clonable.
271
+
272
+ var originalChildren = toClone.find( '.' + name + '-wrap' );
273
+
274
+ // for each wrapper
275
+ newClone.find( '.' + name + '-wrap' ).each( function( index ) {
276
+
277
+ /**
278
+ *
279
+ * @type @call;jquery-cloneya_L8.$@call;closestChild
280
+ */
281
+ var inNewClone = $( this ).closestChild( '.' + name );
282
+
283
+ var inOriginal = $( originalChildren[ index ] ).closestChild( '.' + name );
284
+
285
+ /**
286
+ *
287
+ * @type @exp;inOriginal@call;data
288
+ */
289
+ var originalCount = inOriginal.data( 'initialCount' );
290
+
291
+ /**
292
+ *
293
+ * @type @exp;inNewClone@call;slice
294
+ */
295
+ var $extra = inNewClone.slice( originalCount, inNewClone.length );
296
+
297
+ $extra.remove();
298
+
299
+ inNewClone.data( 'initial-count', originalCount );
300
+ } );
301
+
302
+ }
303
+
304
+ // get the form input
305
+ newClone.find( 'input, textarea, select' ).each( function() {
306
+
307
+ // check if the values need to be copied, if not empty them
308
+ $this._clearForm( $( this ) );
309
+
310
+ // removed the portion taking care of the index
311
+ // each case is specific and I'd rather leave it to the developer
312
+
313
+ // custom event hook for index handling
314
+ $this.$elem.triggerAll( 'clone_form_input form_input.' + name, [ $( this ), toClone, newClone ] );
315
+ } );
316
+
317
+ return newClone;
318
+
319
+ },
320
+ /*
321
+ * Clear Form will used to clear the values of the form
322
+ */
323
+ /**
324
+ *
325
+ * @param {type} $el
326
+ * @returns {undefined}
327
+ */
328
+ _clearForm: function( $el ) {
329
+
330
+ if ( !this.config.valueClone && !$el.hasClass( 'noEmpty' ) ) {
331
+
332
+ if ( $el.is( ':checkbox' ) || $el.is( ':radio' ) ) {
333
+
334
+ $el.prop( 'checked', false );
335
+ }
336
+ else {
337
+ $el.val( '' );
338
+ }
339
+
340
+ }
341
+
342
+ },
343
+ /**
344
+ * Redo the id attribute, serially
345
+ */
346
+ /**
347
+ *
348
+ * @returns {undefined}
349
+ */
350
+ _redoIDs: function() {
351
+
352
+ var $this = this;
353
+
354
+ // check if this even needs to be done
355
+ if ( $this.config.serializeID !== true ) {
356
+ return;
357
+ }
358
+
359
+ // get the id of the first clone (hoping to increment the ids)
360
+ /**
361
+ *
362
+ * @type @exp;elem@call;find@call;first@call;attr
363
+ */
364
+ var mainid = $this.$elem.find( $this.config.cloneThis ).first().attr( 'id' );
365
+
366
+ $this.$elem.find( $this.config.cloneThis ).each( function( i ) {
367
+
368
+ var j;
369
+ // assign the index to a string var for appending to the ids
370
+ // 0 index will have no number at the end
371
+ if ( i !== 0 ) {
372
+ j = i;
373
+ } else {
374
+ j = '';
375
+ }
376
+
377
+ // first modify the clone id
378
+ if ( $( this ).attr( 'id' ) ) {
379
+ $( this ).attr( 'id', mainid + j );
380
+ }
381
+
382
+ var id, nId;
383
+ // take all the elements inside the clone
384
+ $( this ).find( '*' ).each( function() {
385
+
386
+ id = $( this ).attr( 'id' );
387
+ if ( id ) {
388
+ // match the id with the regex to get the string part
389
+ // separate from the number part
390
+ var match = id.match( $this.regex );
391
+
392
+ // if there was a number
393
+ if ( match && match.length === 3 ) {
394
+ // just take the string part
395
+ // add the new number to it
396
+ nId = id.replace( /\d+#x2F;, "" ) + j;
397
+
398
+ $( this ).attr( 'id', nId );
399
+ } else {
400
+ // else there was no number,
401
+ // this was earlier the first element
402
+ // just add the number to its id
403
+ nId = id + j;
404
+ $( this ).attr( 'id', nId );
405
+ }
406
+ }
407
+
408
+ //update label
409
+ $( this ).closest( $this.config.cloneThis ).find( "label[for='" + id + "']" ).attr( 'for', nId );
410
+
411
+ if ( $this.config.serializeIndex ) {
412
+ var name = $( this ).attr( 'name' );
413
+ // This will increment the numeric array index for cloned field names
414
+ if ( name ) {
415
+ var matches = name.match( /\[([0-9}]+)\]/ );
416
+
417
+ if ( matches && matches.length >= 1 ) {
418
+
419
+ var st = name;
420
+ var name = st.replace( matches[ 0 ], "[" + i + "]" );
421
+
422
+ $( this ).attr( 'name', name );
423
+ }
424
+ }
425
+ }
426
+
427
+ } );
428
+ } );
429
+
430
+ }
431
+
432
+ };
433
+
434
+ // add the cloneya to the global object
435
+ /**
436
+ *
437
+ * @param {type} options
438
+ * @returns {jquery-cloneya_L8.$.fn@call;each}
439
+ */
440
+ $.fn[ name ] = function( options ) {
441
+ var args = arguments;
442
+
443
+ if ( options === undefined || typeof options === 'object' ) {
444
+ // Creates a new plugin instance, for each selected element, and
445
+ // stores a reference withint the element's data
446
+ return this.each( function() {
447
+ if ( !$.data( this, name ) ) {
448
+ $.data( this, name, new CloneYa( this, options ) );
449
+ }
450
+ } );
451
+ } else if ( typeof options === 'string' && options[ 0 ] !== '_' && options !== 'init' ) {
452
+ // Call a public pluguin method (not starting with an underscore) for each
453
+ // selected element.
454
+ if ( Array.prototype.slice.call( args, 1 ).length === 0 && $.inArray( options, $.fn[ name ].getters ) !== -1 ) {
455
+ // If the user does not pass any arguments and the method allows to
456
+ // work as a getter then break the chainability so we can return a value
457
+ // instead the element reference.
458
+ var instance = $.data( this[ 0 ], name );
459
+ return instance[ options ].apply( instance, Array.prototype.slice.call( args, 1 ) );
460
+ } else {
461
+ // Invoke the speficied method on each selected element
462
+ return this.each( function() {
463
+ var instance = $.data( this, name );
464
+ if ( instance instanceof CloneYa && typeof instance[ options ] === 'function' ) {
465
+ instance[ options ].apply( instance, Array.prototype.slice.call( args, 1 ) );
466
+ }
467
+ } );
468
+ }
469
+ }
470
+ };
471
+
472
+ $.fn[ name ].getters = [ 'getOption' ];
473
+
474
+ /*
475
+ * jquery.closestchild 0.1.1
476
+ *
477
+ * Author: Andrey Mikhaylov aka lolmaus
478
+ * Email: lolmaus@gmail.com
479
+ *
480
+ */
481
+ /**
482
+ *
483
+ * @param {type} selector
484
+ * @returns {$}
485
+ */
486
+ $.fn.closestChild = function( selector ) {
487
+ var $children, $results;
488
+
489
+ $children = this.children();
490
+
491
+ if ( $children.length === 0 ) {
492
+ return $();
493
+ }
494
+
495
+ $results = $children.filter( selector );
496
+
497
+ if ( $results.length > 0 ) {
498
+ return $results;
499
+ } else {
500
+ return $children.closestChild( selector );
501
+ }
502
+ };
503
+
504
+ /*
505
+ * TriggerAll, modified from stackoverflow
506
+ * http://stackoverflow.com/questions/11850625/jquery-trigger-multiple-events
507
+ */
508
+ $.fn.extend( {
509
+ triggerAll: function( events, params ) {
510
+ var el = this, i, evts = events.split( ' ' );
511
+ for ( i = 0; i < evts.length; i += 1 ) {
512
+ el.triggerHandler( evts[ i ], params );
513
+ }
514
+ return el;
515
+ }
516
+ } );
517
+
518
+ })( jQuery );
assets/js/vendor/jquery-cloneya.min.js ADDED
@@ -0,0 +1 @@
1
+ !function(a){"use strict";function b(b,e){this.regex=/^(.*)(\d)+#x2F;i,this.elem=b,this.$elem=a(b),this.elemClass=c+"-wrap","undefined"!=typeof e&&"undefined"!=typeof e.limit&&e.limit>0&&(e.maximum=e.limit),this.config=a.extend({},d,e),this.clones=this.$elem.closestChild(this.config.cloneThis),this.init()}var c="cloneya",d={cloneThis:".toclone",cloneButton:".clone",deleteButton:".delete",clonePosition:"after",minimum:1,maximum:999,valueClone:!1,dataClone:!1,deepClone:!1,serializeID:!0,ignore:"label.error",preserveChildCount:!1};b.prototype={init:function(){var b=this;b.$elem.addClass(b.elemClass),b.clones.addClass(c),b.clones.data("initialCount",b.clones.length),b.$elem.on("click."+c,b.config.cloneThis+">"+b.config.cloneButton,function(d){d.preventDefault(),d.stopPropagation();var e=a(this).closest(b.config.cloneThis);b.$elem.triggerAll("clone_clone clone."+c,[e])}),b.$elem.on("clone."+c,function(a,c){b._cloneAndAppend(c)}),b.$elem.on("click."+c,b.config.cloneThis+">"+b.config.deleteButton,function(d){d.preventDefault(),d.stopPropagation();var e=a(this).closest(b.config.cloneThis);b.$elem.triggerAll("clone_delete delete."+c,[e])}),b.$elem.on("delete."+c,function(d,e){var f=e.closest("."+b.elemClass).closestChild(b.config.cloneThis).length;f>b.config.minimum?(b.$elem.triggerAll("clone_before_delete before_delete."+c,[e,f]),b.$elem.triggerHandler("remove."+c,[e]),b.$elem.triggerAll("clone_after_delete after_delete."+c)):(b.$elem.triggerHandler("minimum."+c,b.config.minimum,[e]),e.find("input, textarea, select").each(function(){b._clearForm(a(this))}))}),b.$elem.on("remove."+c,function(b,c){a(c).remove()})},_clean:function(){var a=this;a.$elem.removeClass(c+"-wrap"),a.clones.removeClass(c),a.$elem.off("click."+c,a.config.cloneThis+">"+a.config.cloneButton),a.$elem.off("click."+c,a.config.cloneThis+">"+a.config.deleteButton),a.$elem.off("clone_clone clone_delete clone_before_delete clone."+c+" delete."+c+" before_delete."+c)},destroy:function(){this._clean(),this.$elem.removeData(c)},getOption:function(){return this.config},setOption:function(b){a.extend(this.config,b||{}),this._clean(),this.init()},_cloneAndAppend:function(a){var b=a.closest("."+this.elemClass).closestChild(this.config.cloneThis).length;if(b<this.config.maximum){this.$elem.triggerAll("clone_before_clone before_clone."+c,[a]);var d=this._cloneItem(a);this.$elem.triggerAll("clone_after_clone after_clone."+c,[a,d]),this.clones.add(d),this.$elem.triggerAll("clone_before_append before_append."+c,[a,d]),"after"!==this.config.clonePosition?a.before(d):a.after(d),this.config.ignore&&d.find(this.config.ignore).remove(),this._redoIDs(),this.$elem.triggerAll("clone_after_append after_append."+c,[a,d])}else this.$elem.triggerAll("clone_limit maximum."+c,this.config.maximum,[a])},_cloneItem:function(b){var d=this,e=b.clone(d.config.dataClone,d.config.deepClone);if(d.config.preserveChildCount!==!1){var f=b.find("."+c+"-wrap");e.find("."+c+"-wrap").each(function(b){var d=a(this).closestChild("."+c),e=a(f[b]).closestChild("."+c),g=e.data("initialCount"),h=d.slice(g,d.length);h.remove(),d.data("initial-count",g)})}return e.find("input, textarea, select").each(function(){d._clearForm(a(this)),d.$elem.triggerAll("clone_form_input form_input."+c,[a(this),b,e])}),e},_clearForm:function(a){this.config.valueClone||a.hasClass("noEmpty")||(a.is(":checkbox")||a.is(":radio")?a.prop("checked",!1):a.val(""))},_redoIDs:function(){var b=this;if(b.config.serializeID===!0){var c=b.$elem.find(b.config.cloneThis).first().attr("id");b.$elem.find(b.config.cloneThis).each(function(d){var e;e=0!==d?d:"",a(this).attr("id")&&a(this).attr("id",c+e);var f,g;a(this).find("*").each(function(){if(f=a(this).attr("id")){var c=f.match(b.regex);c&&3===c.length?(g=f.replace(/\d+#x2F;,"")+e,a(this).attr("id",g)):(g=f+e,a(this).attr("id",g))}if(a(this).closest(b.config.cloneThis).find("label[for='"+f+"']").attr("for",g),b.config.serializeIndex){var h=a(this).attr("name");if(h){var i=h.match(/\[([^}]+)\]/);if(i&&i.length>=1){var j=h;h=[].map.call(j,function(a,b){return isNaN(+a)||"["!==j[b-1]||"]"!==j[b+1]?a:d}).join(""),a(this).attr("name",h)}}}})})}}},a.fn[c]=function(d){var e=arguments;if(void 0===d||"object"==typeof d)return this.each(function(){a.data(this,c)||a.data(this,c,new b(this,d))});if("string"==typeof d&&"_"!==d[0]&&"init"!==d){if(0===Array.prototype.slice.call(e,1).length&&-1!==a.inArray(d,a.fn[c].getters)){var f=a.data(this[0],c);return f[d].apply(f,Array.prototype.slice.call(e,1))}return this.each(function(){var f=a.data(this,c);f instanceof b&&"function"==typeof f[d]&&f[d].apply(f,Array.prototype.slice.call(e,1))})}},a.fn[c].getters=["getOption"],a.fn.closestChild=function(b){var c,d;return c=this.children(),0===c.length?a():(d=c.filter(b),d.length>0?d:c.closestChild(b))},a.fn.extend({triggerAll:function(a,b){var c,d=this,e=a.split(" ");for(c=0;c<e.length;c+=1)d.triggerHandler(e[c],b);return d}})}(jQuery);
assets/js/vendor/jquery.vide.js ADDED
@@ -0,0 +1,501 @@
1
+ /*
2
+ * Vide - v0.5.1
3
+ * Easy as hell jQuery plugin for video backgrounds.
4
+ * http://vodkabears.github.io/vide/
5
+ *
6
+ * Made by Ilya Makarov
7
+ * Under MIT License
8
+ */
9
+ !(function(root, factory) {
10
+ if (typeof define === 'function' && define.amd) {
11
+ define(['jquery'], factory);
12
+ } else if (typeof exports === 'object') {
13
+ factory(require('jquery'));
14
+ } else {
15
+ factory(root.jQuery);
16
+ }
17
+ })(this, function($) {
18
+
19
+ 'use strict';
20
+
21
+ /**
22
+ * Name of the plugin
23
+ * @private
24
+ * @const
25
+ * @type {String}
26
+ */
27
+ var PLUGIN_NAME = 'vide';
28
+
29
+ /**
30
+ * Default settings
31
+ * @private
32
+ * @const
33
+ * @type {Object}
34
+ */
35
+ var DEFAULTS = {
36
+ volume: 1,
37
+ playbackRate: 1,
38
+ muted: true,
39
+ loop: true,
40
+ autoplay: true,
41
+ position: '50% 50%',
42
+ posterType: 'detect',
43
+ resizing: true,
44
+ bgColor: 'transparent',
45
+ className: ''
46
+ };
47
+
48
+ /**
49
+ * Not implemented error message
50
+ * @private
51
+ * @const
52
+ * @type {String}
53
+ */
54
+ var NOT_IMPLEMENTED_MSG = 'Not implemented';
55
+
56
+ /**
57
+ * Parse a string with options
58
+ * @private
59
+ * @param {String} str
60
+ * @returns {Object|String}
61
+ */
62
+ function parseOptions(str) {
63
+ var obj = {};
64
+ var delimiterIndex;
65
+ var option;
66
+ var prop;
67
+ var val;
68
+ var arr;
69
+ var len;
70
+ var i;
71
+
72
+ // Remove spaces around delimiters and split
73
+ arr = str.replace(/\s*:\s*/g, ':').replace(/\s*,\s*/g, ',').split(',');
74
+
75
+ // Parse a string
76
+ for (i = 0, len = arr.length; i < len; i++) {
77
+ option = arr[i];
78
+
79
+ // Ignore urls and a string without colon delimiters
80
+ if (
81
+ option.search(/^(http|https|ftp):\/\//) !== -1 ||
82
+ option.search(':') === -1
83
+ ) {
84
+ break;
85
+ }
86
+
87
+ delimiterIndex = option.indexOf(':');
88
+ prop = option.substring(0, delimiterIndex);
89
+ val = option.substring(delimiterIndex + 1);
90
+
91
+ // If val is an empty string, make it undefined
92
+ if (!val) {
93
+ val = undefined;
94
+ }
95
+
96
+ // Convert a string value if it is like a boolean
97
+ if (typeof val === 'string') {
98
+ val = val === 'true' || (val === 'false' ? false : val);
99
+ }
100
+
101
+ // Convert a string value if it is like a number
102
+ if (typeof val === 'string') {
103
+ val = !isNaN(val) ? +val : val;
104
+ }
105
+
106
+ obj[prop] = val;
107
+ }
108
+
109
+ // If nothing is parsed
110
+ if (prop == null && val == null) {
111
+ return str;
112
+ }
113
+
114
+ return obj;
115
+ }
116
+
117
+ /**
118
+ * Parse a position option
119
+ * @private
120
+ * @param {String} str
121
+ * @returns {Object}
122
+ */
123
+ function parsePosition(str) {
124
+ str = '' + str;
125
+
126
+ // Default value is a center
127
+ var args = str.split(/\s+/);
128
+ var x = '50%';
129
+ var y = '50%';
130
+ var len;
131
+ var arg;
132
+ var i;
133
+
134
+ for (i = 0, len = args.length; i < len; i++) {
135
+ arg = args[i];
136
+
137
+ // Convert values
138
+ if (arg === 'left') {
139
+ x = '0%';
140
+ } else if (arg === 'right') {
141
+ x = '100%';
142
+ } else if (arg === 'top') {
143
+ y = '0%';
144
+ } else if (arg === 'bottom') {
145
+ y = '100%';
146
+ } else if (arg === 'center') {
147
+ if (i === 0) {
148
+ x = '50%';
149
+ } else {
150
+ y = '50%';
151
+ }
152
+ } else {
153
+ if (i === 0) {
154
+ x = arg;
155
+ } else {
156
+ y = arg;
157
+ }
158
+ }
159
+ }
160
+
161
+ return { x: x, y: y };
162
+ }
163
+
164
+ /**
165
+ * Search a poster
166
+ * @private
167
+ * @param {String} path
168
+ * @param {Function} callback
169
+ */
170
+ function findPoster(path, callback) {
171
+ var onLoad = function() {
172
+ callback(this.src);
173
+ };
174
+
175
+ $('<img src="' + path + '.gif">').on('load', onLoad);
176
+ $('<img src="' + path + '.jpg">').on('load', onLoad);
177
+ $('<img src="' + path + '.jpeg">').on('load', onLoad);
178
+ $('<img src="' + path + '.png">').on('load', onLoad);
179
+ }
180
+
181
+ /**
182
+ * Vide constructor
183
+ * @param {HTMLElement} element
184
+ * @param {Object|String} path
185
+ * @param {Object|String} options
186
+ * @constructor
187
+ */
188
+ function Vide(element, path, options) {
189
+ this.$element = $(element);
190
+
191
+ // Parse path
192
+ if (typeof path === 'string') {
193
+ path = parseOptions(path);
194
+ }
195
+
196
+ // Parse options
197
+ if (!options) {
198
+ options = {};
199
+ } else if (typeof options === 'string') {
200
+ options = parseOptions(options);
201
+ }
202
+
203
+ // Remove an extension
204
+ if (typeof path === 'string') {
205
+ path = path.replace(/\.\w*#x2F;, '');
206
+ } else if (typeof path === 'object') {
207
+ for (var i in path) {
208
+ if (path.hasOwnProperty(i)) {
209
+ path[i] = path[i].replace(/\.\w*#x2F;, '');
210
+ }
211
+ }
212
+ }
213
+
214
+ this.settings = $.extend({}, DEFAULTS, options);
215
+ this.path = path;
216
+
217
+ // https://github.com/VodkaBears/Vide/issues/110
218
+ try {
219
+ this.init();
220
+ } catch (e) {
221
+ if (e.message !== NOT_IMPLEMENTED_MSG) {
222
+ throw e;
223
+ }
224
+ }
225
+ }
226
+
227
+ /**
228
+ * Initialization
229
+ * @public
230
+ */
231
+ Vide.prototype.init = function() {
232
+ var vide = this;
233
+ var path = vide.path;
234
+ var poster = path;
235
+ var sources = '';
236
+ var $element = vide.$element;
237
+ var settings = vide.settings;
238
+ var position = parsePosition(settings.position);
239
+ var posterType = settings.posterType;
240
+ var $video;
241
+ var $wrapper;
242
+
243
+ // Set styles of a video wrapper
244
+ $wrapper = vide.$wrapper = $('<div>')
245
+ .addClass(settings.className)
246
+ .css({
247
+ position: 'absolute',
248
+ 'z-index': -1,
249
+ top: 0,
250
+ left: 0,
251
+ bottom: 0,
252
+ right: 0,
253
+ overflow: 'hidden',
254
+ '-webkit-background-size': 'cover',
255
+ '-moz-background-size': 'cover',
256
+ '-o-background-size': 'cover',
257
+ 'background-size': 'cover',
258
+ 'background-color': settings.bgColor,
259
+ 'background-repeat': 'no-repeat',
260
+ 'background-position': position.x + ' ' + position.y
261
+ });
262
+
263
+ // Get a poster path
264
+ if (typeof path === 'object') {
265
+ if (path.poster) {
266
+ poster = path.poster;
267
+ } else {
268
+ if (path.mp4) {
269
+ poster = path.mp4;
270
+ } else if (path.webm) {
271
+ poster = path.webm;
272
+ } else if (path.ogv) {
273
+ poster = path.ogv;
274
+ }
275
+ }
276
+ }
277
+
278
+ // Set a video poster
279
+ if (posterType === 'detect') {
280
+ findPoster(poster, function(url) {
281
+ $wrapper.css('background-image', 'url(' + url + ')');
282
+ });
283
+ } else if (posterType !== 'none') {
284
+ $wrapper.css('background-image', 'url(' + poster + '.' + posterType + ')');
285
+ }
286
+
287
+ // If a parent element has a static position, make it relative
288
+ if ($element.css('position') === 'static') {
289
+ $element.css('position', 'relative');
290
+ }
291
+
292
+ $element.prepend($wrapper);
293
+
294
+ if (typeof path === 'object') {
295
+ if (path.mp4) {
296
+ sources += '<source src="' + path.mp4 + '.mp4" type="video/mp4">';
297
+ }
298
+
299
+ if (path.webm) {
300
+ sources += '<source src="' + path.webm + '.webm" type="video/webm">';
301
+ }
302
+
303
+ if (path.ogv) {
304
+ sources += '<source src="' + path.ogv + '.ogv" type="video/ogg">';
305
+ }
306
+
307
+ $video = vide.$video = $('<video>' + sources + '</video>');
308
+ } else {
309
+ $video = vide.$video = $('<video>' +
310
+ '<source src="' + path + '.mp4" type="video/mp4">' +
311
+ '<source src="' + path + '.webm" type="video/webm">' +
312
+ '<source src="' + path + '.ogv" type="video/ogg">' +
313
+ '</video>');
314
+ }
315
+
316
+ // https://github.com/VodkaBears/Vide/issues/110
317
+ try {
318
+ $video
319
+
320
+ // Set video properties
321
+ .prop({
322
+ autoplay: settings.autoplay,
323
+ loop: settings.loop,
324
+ volume: settings.volume,
325
+ muted: settings.muted,
326
+ defaultMuted: settings.muted,
327
+ playbackRate: settings.playbackRate,
328
+ defaultPlaybackRate: settings.playbackRate
329
+ });
330
+ } catch (e) {
331
+ throw new Error(NOT_IMPLEMENTED_MSG);
332
+ }
333
+
334
+ // Video alignment
335
+ $video.css({
336
+ margin: 'auto',
337
+ position: 'absolute',
338
+ 'z-index': -1,
339
+ top: position.y,
340
+ left: position.x,
341
+ '-webkit-transform': 'translate(-' + position.x + ', -' + position.y + ')',
342
+ '-ms-transform': 'translate(-' + position.x + ', -' + position.y + ')',
343
+ '-moz-transform': 'translate(-' + position.x + ', -' + position.y + ')',
344
+ transform: 'translate(-' + position.x + ', -' + position.y + ')',
345
+
346
+ // Disable visibility, while loading
347
+ visibility: 'hidden',
348
+ opacity: 0
349
+ })
350
+
351
+ // Resize a video, when it's loaded
352
+ .one('canplaythrough.' + PLUGIN_NAME, function() {
353
+ vide.resize();
354
+ })
355
+
356
+ // Make it visible, when it's already playing
357
+ .one('playing.' + PLUGIN_NAME, function() {
358
+ $video.css({
359
+ visibility: 'visible',
360
+ opacity: 1
361
+ });
362
+ $wrapper.css('background-image', 'none');
363
+ });
364
+
365
+ // Resize event is available only for 'window'
366
+ // Use another code solutions to detect DOM elements resizing
367
+ $element.on('resize.' + PLUGIN_NAME, function() {
368
+ if (settings.resizing) {
369
+ vide.resize();
370
+ }
371
+ });
372
+
373
+ // Append a video
374
+ $wrapper.append($video);
375
+ };
376
+
377
+ /**
378
+ * Get a video element
379
+ * @public
380
+ * @returns {HTMLVideoElement}
381
+ */
382
+ Vide.prototype.getVideoObject = function() {
383
+ return this.$video[0];
384
+ };
385
+
386
+ /**
387
+ * Resize a video background
388
+ * @public
389
+ */
390
+ Vide.prototype.resize = function() {
391
+ if (!this.$video) {
392
+ return;
393
+ }
394
+
395
+ var $wrapper = this.$wrapper;
396
+ var $video = this.$video;
397
+ var video = $video[0];
398
+
399
+ // Get a native video size
400
+ var videoHeight = video.videoHeight;
401
+ var videoWidth = video.videoWidth;
402
+
403
+ // Get a wrapper size
404
+ var wrapperHeight = $wrapper.height();
405
+ var wrapperWidth = $wrapper.width();
406
+
407
+ if (wrapperWidth / videoWidth > wrapperHeight / videoHeight) {
408
+ $video.css({
409
+
410
+ // +2 pixels to prevent an empty space after transformation
411
+ width: wrapperWidth + 2,
412
+ height: 'auto'
413
+ });
414
+ } else {
415
+ $video.css({
416
+ width: 'auto',
417
+
418
+ // +2 pixels to prevent an empty space after transformation
419
+ height: wrapperHeight + 2
420
+ });
421
+ }
422
+ };
423
+
424
+ /**
425
+ * Destroy a video background
426
+ * @public
427
+ */
428
+ Vide.prototype.destroy = function() {
429
+ delete $[PLUGIN_NAME].lookup[this.index];
430
+ this.$video && this.$video.off(PLUGIN_NAME);
431
+ this.$element.off(PLUGIN_NAME).removeData(PLUGIN_NAME);
432
+ this.$wrapper.remove();
433
+ };
434
+
435
+ /**
436
+ * Special plugin object for instances.
437
+ * @public
438
+ * @type {Object}
439
+ */
440
+ $[PLUGIN_NAME] = {
441
+ lookup: []
442
+ };
443
+
444
+ /**
445
+ * Plugin constructor
446
+ * @param {Object|String} path
447
+ * @param {Object|String} options
448
+ * @returns {JQuery}
449
+ * @constructor
450
+ */
451
+ $.fn[PLUGIN_NAME] = function(path, options) {
452
+ var instance;
453
+
454
+ this.each(function() {
455
+ instance = $.data(this, PLUGIN_NAME);
456
+
457
+ // Destroy the plugin instance if exists
458
+ instance && instance.destroy();
459
+
460
+ // Create the plugin instance
461
+ instance = new Vide(this, path, options);
462
+ instance.index = $[PLUGIN_NAME].lookup.push(instance) - 1;
463
+ $.data(this, PLUGIN_NAME, instance);
464
+ });
465
+
466
+ return this;
467
+ };
468
+
469
+ $(document).ready(function() {
470
+ var $window = $(window);
471
+
472
+ // Window resize event listener
473
+ $window.on('resize.' + PLUGIN_NAME, function() {
474
+ for (var len = $[PLUGIN_NAME].lookup.length, i = 0, instance; i < len; i++) {
475
+ instance = $[PLUGIN_NAME].lookup[i];
476
+
477
+ if (instance && instance.settings.resizing) {
478
+ instance.resize();
479
+ }
480
+ }
481
+ });
482
+
483
+ // https://github.com/VodkaBears/Vide/issues/68
484
+ $window.on('unload.' + PLUGIN_NAME, function() {
485
+ return false;
486
+ });
487
+
488
+ // Auto initialization
489
+ // Add 'data-vide-bg' attribute with a path to the video without extension
490
+ // Also you can pass options throw the 'data-vide-options' attribute
491
+ // 'data-vide-options' must be like 'muted: false, volume: 0.5'
492
+ $(document).find('[data-' + PLUGIN_NAME + '-bg]').each(function(i, element) {
493
+ var $element = $(element);
494
+ var options = $element.data(PLUGIN_NAME + '-options');
495
+ var path = $element.data(PLUGIN_NAME + '-bg');
496
+
497
+ $element[PLUGIN_NAME](path, options);
498
+ });
499
+ });
500
+
501
+ });
assets/js/vendor/jquery.vide.min.js ADDED
@@ -0,0 +1,9 @@
1
+ /*
2
+ * Vide - v0.5.1
3
+ * Easy as hell jQuery plugin for video backgrounds.
4
+ * http://vodkabears.github.io/vide/
5
+ *
6
+ * Made by Ilya Makarov
7
+ * Under MIT License
8
+ */
9
+ !function(a,b){"function"==typeof define&&define.amd?define(["jquery"],b):b("object"==typeof exports?require("jquery"):a.jQuery)}(this,function(a){"use strict";function b(a){var b,c,d,e,f,g,h,i={};for(f=a.replace(/\s*:\s*/g,":").replace(/\s*,\s*/g,",").split(","),h=0,g=f.length;h<g&&(c=f[h],c.search(/^(http|https|ftp):\/\//)===-1&&c.search(":")!==-1);h++)b=c.indexOf(":"),d=c.substring(0,b),e=c.substring(b+1),e||(e=void 0),"string"==typeof e&&(e="true"===e||"false"!==e&&e),"string"==typeof e&&(e=isNaN(e)?e:+e),i[d]=e;return null==d&&null==e?a:i}function c(a){a=""+a;var b,c,d,e=a.split(/\s+/),f="50%",g="50%";for(d=0,b=e.length;d<b;d++)c=e[d],"left"===c?f="0%":"right"===c?f="100%":"top"===c?g="0%":"bottom"===c?g="100%":"center"===c?0===d?f="50%":g="50%":0===d?f=c:g=c;return{x:f,y:g}}function d(b,c){var d=function(){c(this.src)};a('<img src="'+b+'.gif">').on("load",d),a('<img src="'+b+'.jpg">').on("load",d),a('<img src="'+b+'.jpeg">').on("load",d),a('<img src="'+b+'.png">').on("load",d)}function e(c,d,e){if(this.$element=a(c),"string"==typeof d&&(d=b(d)),e?"string"==typeof e&&(e=b(e)):e={},"string"==typeof d)d=d.replace(/\.\w*#x2F;,"");else if("object"==typeof d)for(var f in d)d.hasOwnProperty(f)&&(d[f]=d[f].replace(/\.\w*#x2F;,""));this.settings=a.extend({},g,e),this.path=d;try{this.init()}catch(i){if(i.message!==h)throw i}}var f="vide",g={volume:1,playbackRate:1,muted:!0,loop:!0,autoplay:!0,position:"50% 50%",posterType:"detect",resizing:!0,bgColor:"transparent",className:""},h="Not implemented";e.prototype.init=function(){var b,e,f=this,g=f.path,i=g,j="",k=f.$element,l=f.settings,m=c(l.position),n=l.posterType;e=f.$wrapper=a("<div>").addClass(l.className).css({position:"absolute","z-index":-1,top:0,left:0,bottom:0,right:0,overflow:"hidden","-webkit-background-size":"cover","-moz-background-size":"cover","-o-background-size":"cover","background-size":"cover","background-color":l.bgColor,"background-repeat":"no-repeat","background-position":m.x+" "+m.y}),"object"==typeof g&&(g.poster?i=g.poster:g.mp4?i=g.mp4:g.webm?i=g.webm:g.ogv&&(i=g.ogv)),"detect"===n?d(i,function(a){e.css("background-image","url("+a+")")}):"none"!==n&&e.css("background-image","url("+i+"."+n+")"),"static"===k.css("position")&&k.css("position","relative"),k.prepend(e),"object"==typeof g?(g.mp4&&(j+='<source src="'+g.mp4+'.mp4" type="video/mp4">'),g.webm&&(j+='<source src="'+g.webm+'.webm" type="video/webm">'),g.ogv&&(j+='<source src="'+g.ogv+'.ogv" type="video/ogg">'),b=f.$video=a("<video>"+j+"</video>")):b=f.$video=a('<video><source src="'+g+'.mp4" type="video/mp4"><source src="'+g+'.webm" type="video/webm"><source src="'+g+'.ogv" type="video/ogg"></video>');try{b.prop({autoplay:l.autoplay,loop:l.loop,volume:l.volume,muted:l.muted,defaultMuted:l.muted,playbackRate:l.playbackRate,defaultPlaybackRate:l.playbackRate})}catch(o){throw new Error(h)}b.css({margin:"auto",position:"absolute","z-index":-1,top:m.y,left:m.x,"-webkit-transform":"translate(-"+m.x+", -"+m.y+")","-ms-transform":"translate(-"+m.x+", -"+m.y+")","-moz-transform":"translate(-"+m.x+", -"+m.y+")",transform:"translate(-"+m.x+", -"+m.y+")",visibility:"hidden",opacity:0}).one("canplaythrough.vide",function(){f.resize()}).one("playing.vide",function(){b.css({visibility:"visible",opacity:1}),e.css("background-image","none")}),k.on("resize.vide",function(){l.resizing&&f.resize()}),e.append(b)},e.prototype.getVideoObject=function(){return this.$video[0]},e.prototype.resize=function(){if(this.$video){var a=this.$wrapper,b=this.$video,c=b[0],d=c.videoHeight,e=c.videoWidth,f=a.height(),g=a.width();g/e>f/d?b.css({width:g+2,height:"auto"}):b.css({width:"auto",height:f+2})}},e.prototype.destroy=function(){delete a[f].lookup[this.index],this.$video&&this.$video.off(f),this.$element.off(f).removeData(f),this.$wrapper.remove()},a[f]={lookup:[]},a.fn[f]=function(b,c){var d;return this.each(function(){d=a.data(this,f),d&&d.destroy(),d=new e(this,b,c),d.index=a[f].lookup.push(d)-1,a.data(this,f,d)}),this},a(document).ready(function(){var b=a(window);b.on("resize.vide",function(){for(var b,c=a[f].lookup.length,d=0;d<c;d++)b=a[f].lookup[d],b&&b.settings.resizing&&b.resize()}),b.on("unload.vide",function(){return!1}),a(document).find("[data-vide-bg]").each(function(b,c){var d=a(c),e=d.data("vide-options"),g=d.data("vide-bg");d[f](g,e)})})});
assets/js/vendor/jquery.youtubebackground.js ADDED
@@ -0,0 +1,334 @@
1
+ /*
2
+ * YoutubeBackground - A wrapper for the Youtube API - Great for fullscreen background videos or just regular videos.
3
+ *
4
+ * Licensed under the MIT license:
5
+ * http://www.opensource.org/licenses/mit-license.php
6
+ *
7
+ *
8
+ * Version: 1.0.5
9
+ *
10
+ */
11
+
12
+ // Chain of Responsibility pattern. Creates base class that can be overridden.
13
+ if ( typeof Object.create !== "function" ) {
14
+ Object.create = function (obj) {
15
+ function F() {
16
+ }
17
+
18
+ F.prototype = obj;
19
+ return new F();
20
+ };
21
+ }
22
+
23
+ (function ($, window, document) {
24
+ var
25
+ loadAPI = function loadAPI(callback) {
26
+
27
+ // Load Youtube API
28
+ var tag = document.createElement('script'),
29
+ head = document.getElementsByTagName('head')[ 0 ];
30
+
31
+ if ( window.location.origin == 'file://' ) {
32
+ tag.src = 'http://www.youtube.com/iframe_api';
33
+ } else {
34
+ tag.src = '//www.youtube.com/iframe_api';
35
+ }
36
+
37
+ head.appendChild(tag);
38
+
39
+ // Clean up Tags.
40
+ head = null;
41
+ tag = null;
42
+
43
+ iframeIsReady(callback);
44
+ },
45
+ iframeIsReady = function iframeIsReady(callback) {
46
+ // Listen for Gobal YT player callback
47
+ if ( typeof YT === 'undefined' && typeof window.loadingPlayer === 'undefined' ) {
48
+ // Prevents Ready Event from being called twice
49
+ window.loadingPlayer = true;
50
+
51
+
52
+ // Creates deferred so, other players know when to wait.
53
+ window.dfd = $.Deferred();
54
+ window.onYouTubeIframeAPIReady = function () {
55
+ window.onYouTubeIframeAPIReady = null;
56
+ window.dfd.resolve("done");
57
+ callback();
58
+ };
59
+ } else if ( typeof YT === 'object' ) {
60
+ callback();
61
+ } else {
62
+ window.dfd.done(function (name) {
63
+ callback();
64
+ });
65
+ }
66
+ };
67
+
68
+ // YTPlayer Object
69
+ YTPlayer = {
70
+ player: null,
71
+
72
+ // Defaults
73
+ defaults: {
74
+ ratio : 16 / 9,
75
+ videoId : 'LSmgKRx5pBo',
76
+ mute : true,
77
+ repeat : true,
78
+ width : $(window).width(),
79
+ playButtonClass : 'YTPlayer-play',
80
+ pauseButtonClass: 'YTPlayer-pause',
81
+ muteButtonClass : 'YTPlayer-mute',
82
+ volumeUpClass : 'YTPlayer-volume-up',
83
+ volumeDownClass : 'YTPlayer-volume-down',
84
+ start : 0,
85
+ pauseOnScroll : false,
86
+ fitToBackground : true,
87
+ playerVars : {
88
+ iv_load_policy: 3,
89
+ modestbranding: 1,
90
+ autoplay : 1,
91
+ controls : 0,
92
+ showinfo : 0,
93
+ wmode : 'opaque',
94
+ branding : 0,
95
+ autohide : 0
96
+ },
97
+ events : null
98
+ },
99
+
100
+ /**
101
+ * @function init
102
+ * Intializes YTPlayer object
103
+ */
104
+ init: function init(node, userOptions) {
105
+ var self = this;
106
+
107
+ self.userOptions = userOptions;
108
+
109
+ self.$body = $('body'),
110
+ self.$node = $(node),
111
+ self.$window = $(window);
112
+
113
+ // Setup event defaults with the reference to this
114
+ self.defaults.events = {
115
+ 'onReady' : function (e) {
116
+ self.onPlayerReady(e);
117
+
118
+ // setup up pause on scroll
119
+ if ( self.options.pauseOnScroll ) {
120
+ self.pauseOnScroll();
121
+ }
122
+
123
+ // Callback for when finished
124
+ if ( typeof self.options.callback == 'function' ) {
125
+ self.options.callback.call(this);
126
+ }
127
+ },
128
+ 'onStateChange': function (e) {
129
+ if ( e.data === 1 ) {
130
+
131
+ self.$node.find('img').fadeOut(400);
132
+ self.$node.addClass('loaded');
133
+ } else if ( e.data === 0 && self.options.repeat ) { // video ended and repeat option is set true
134
+ self.player.seekTo(self.options.start);
135
+ }
136
+ }
137
+ }
138
+
139
+
140
+ self.options = $.extend(true, {}, self.defaults, self.userOptions);
141
+ self.options.height = Math.ceil(self.options.width / self.options.ratio);
142
+ self.ID = (new Date()).getTime();
143
+ self.holderID = 'YTPlayer-ID-' + self.ID;
144
+
145
+ if ( self.options.fitToBackground ) {
146
+ self.createBackgroundVideo();
147
+ } else {
148
+ self.createContainerVideo();
149
+ }
150
+ // Listen for Resize Event
151
+ self.$window.on('resize.YTplayer' + self.ID, function () {
152
+ self.resize(self);
153
+ });
154
+
155
+ loadAPI(self.onYouTubeIframeAPIReady.bind(self));
156
+
157
+ self.resize(self);
158
+
159
+ return self;
160
+ },
161
+
162
+
163
+ /**
164
+ * @function pauseOnScroll
165
+ * Adds window events to pause video on scroll.
166
+ */
167
+ pauseOnScroll : function pauseOnScroll() {
168
+ var self = this;
169
+ self.$window.on('scroll.YTplayer' + self.ID, function () {
170
+ var state = self.player.getPlayerState();
171
+ if ( state === 1 ) {
172
+ self.player.pauseVideo();
173
+ }
174
+ });
175
+ self.$window.scrollStopped(function () {
176
+ var state = self.player.getPlayerState();
177
+ if ( state === 2 ) {
178
+ self.player.playVideo();
179
+ }
180
+ });
181
+ },
182
+ /**
183
+ * @function createContainerVideo
184
+ * Adds HTML for video in a container
185
+ */
186
+ createContainerVideo: function createContainerVideo() {
187
+ var self = this;
188
+
189
+ /*jshint multistr: true */
190
+ var $YTPlayerString = $('<div id="ytplayer-container' + self.ID + '" >\
191
+ <div id="' + self.holderID + '" class="ytplayer-player-inline"></div> \
192
+ </div> \
193
+ <div id="ytplayer-shield" class="ytplayer-shield"></div>');
194
+
195
+ self.$node.append($YTPlayerString);
196
+ self.$YTPlayerString = $YTPlayerString;
197
+ $YTPlayerString = null;
198
+ },
199
+
200
+ /**
201
+ * @function createBackgroundVideo
202
+ * Adds HTML for video background
203
+ */
204
+ createBackgroundVideo: function createBackgroundVideo() {
205
+ /*jshint multistr: true */
206
+ var self = this,
207
+ $YTPlayerString = $('<div id="ytplayer-container' + self.ID + '" class="ytplayer-container background">\
208
+ <div id="' + self.holderID + '" class="ytplayer-player"></div>\
209
+ </div>\
210
+ <div id="ytplayer-shield" class="ytplayer-shield"></div>');
211
+
212
+ self.$node.append($YTPlayerString);
213
+ self.$YTPlayerString = $YTPlayerString;
214
+ $YTPlayerString = null;
215
+ },
216
+
217
+ /**
218
+ * @function resize
219
+ * Resize event to change video size
220
+ */
221
+ resize: function resize(self) {
222
+ //var self = this;
223
+ var container = $(window);
224
+
225
+ if ( !self.options.fitToBackground ) {
226
+ container = self.$node;
227
+ }
228
+
229
+ var width = container.width(),
230
+ pWidth, // player width, to be defined
231
+ height = container.height(),
232
+ pHeight, // player height, tbd
233
+ $YTPlayerPlayer = $('#' + self.holderID);
234
+
235
+ // when screen aspect ratio differs from video, video must center and underlay one dimension
236
+ if ( width / self.options.ratio < height ) {
237
+ pWidth = Math.ceil(height * self.options.ratio); // get new player width
238
+ $YTPlayerPlayer.width(pWidth).height(height).css({
239
+ left: (width - pWidth) / 2,
240
+ top : 0
241
+ }); // player width is greater, offset left; reset top
242
+ } else { // new video width < window width (gap to right)
243
+ pHeight = Math.ceil(width / self.options.ratio); // get new player height
244
+ $YTPlayerPlayer.width(width).height(pHeight).css({
245
+ left: 0,
246
+ top : (height - pHeight) / 2
247
+ }); // player height is greater, offset top; reset left
248
+ }
249
+
250
+ $YTPlayerPlayer = null;
251
+ container = null;
252
+ },
253
+
254
+ /**
255
+ * @function onYouTubeIframeAPIReady
256
+ * @ params {object} YTPlayer object for access to options
257
+ * Youtube API calls this function when the player is ready.
258
+ */
259
+ onYouTubeIframeAPIReady: function onYouTubeIframeAPIReady() {
260
+ var self = this;
261
+ self.player = new window.YT.Player(self.holderID, self.options);
262
+ },
263
+
264
+ /**
265
+ * @function onPlayerReady
266
+ * @ params {event} window event from youtube player
267
+ */
268
+ onPlayerReady: function onPlayerReady(e) {
269
+ if ( this.options.mute ) {
270
+ e.target.mute();
271
+ }
272
+ if ( this.options.playerVars.autoplay ) {
273
+ e.target.playVideo();
274
+ }
275
+ $(document).trigger('YTBGREADY', {player: true});
276
+ },
277
+
278
+ /**
279
+ * @function getPlayer
280
+ * returns youtube player
281
+ */
282
+ getPlayer: function getPlayer() {
283
+ return this.player;
284
+ },
285
+
286
+ /**
287
+ * @function destroy
288
+ * destroys all!
289
+ */
290
+ destroy: function destroy() {
291
+ var self = this;
292
+
293
+ self.$node
294
+ .removeData('yt-init')
295
+ .removeData('ytPlayer')
296
+ .removeClass('loaded');
297
+
298
+ self.$YTPlayerString.remove();
299
+
300
+ $(window).off('resize.YTplayer' + self.ID);
301
+ $(window).off('scroll.YTplayer' + self.ID);
302
+ self.$body = null;
303
+ self.$node = null;
304
+ self.$YTPlayerString = null;
305
+ self.player.destroy();
306
+ self.player = null;
307
+ }
308
+ };
309
+
310
+ // Scroll Stopped event.
311
+ $.fn.scrollStopped = function (callback) {
312
+ var $this = $(this), self = this;
313
+ $this.scroll(function () {
314
+ if ( $this.data('scrollTimeout') ) {
315
+ clearTimeout($this.data('scrollTimeout'));
316
+ }
317
+ $this.data('scrollTimeout', setTimeout(callback, 250, self));
318
+ });
319
+ };
320
+
321
+ // Create plugin
322
+ $.fn.YTPlayer = function (options) {
323
+
324
+ return this.each(function () {
325
+ var el = this;
326
+
327
+ $(el).data("yt-init", true);
328
+ var player = Object.create(YTPlayer);
329
+ player.init(el, options);
330
+ $.data(el, "ytPlayer", player);
331
+ });
332
+ };
333
+
334
+ })(jQuery, window, document);
assets/js/vendor/player.js ADDED
@@ -0,0 +1,2119 @@