Shapely Companion - Version 1.2.3

Version Description

Download this release

Release Info

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

Version 1.2.3

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 +266 -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 +87 -0
  37. inc/widgets/class-shapely-video.php +250 -0
  38. languages/shapely.pot +426 -0
  39. readme.txt +86 -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 @@