j1-template 2023.4.1 → 2023.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/assets/data/masterslider.html +1 -1
  3. data/assets/themes/j1/adapter/js/advertising.js +1 -0
  4. data/assets/themes/j1/adapter/js/algolia.js +1 -0
  5. data/assets/themes/j1/adapter/js/analytics.js +1 -0
  6. data/assets/themes/j1/adapter/js/asciidoctor.js +1 -0
  7. data/assets/themes/j1/adapter/js/bmd.js +1 -0
  8. data/assets/themes/j1/adapter/js/carousel.js +1 -0
  9. data/assets/themes/j1/adapter/js/clipboard.js +1 -0
  10. data/assets/themes/j1/adapter/js/comments.js +1 -0
  11. data/assets/themes/j1/adapter/js/cookieConsent.js +6 -5
  12. data/assets/themes/j1/adapter/js/customFunctions.js +1 -0
  13. data/assets/themes/j1/adapter/js/customModule.js +1 -0
  14. data/assets/themes/j1/adapter/js/dropdowns.js +1 -0
  15. data/assets/themes/j1/adapter/js/fab.js +1 -0
  16. data/assets/themes/j1/adapter/js/framer.js +1 -0
  17. data/assets/themes/j1/adapter/js/justifiedGallery.js +1 -0
  18. data/assets/themes/j1/adapter/js/justifiedGalleryCustomizer.js +1 -1
  19. data/assets/themes/j1/adapter/js/lazyLoader.js +1 -0
  20. data/assets/themes/j1/adapter/js/lightbox.js +1 -0
  21. data/assets/themes/j1/adapter/js/logger.js +1 -0
  22. data/assets/themes/j1/adapter/js/lunr.js +1 -0
  23. data/assets/themes/j1/adapter/js/masonry.js +1 -0
  24. data/assets/themes/j1/adapter/js/masterslider.js +1 -0
  25. data/assets/themes/j1/adapter/js/mmenu.js +1 -0
  26. data/assets/themes/j1/adapter/js/navigator.js +1 -0
  27. data/assets/themes/j1/adapter/js/particles.js +1 -0
  28. data/assets/themes/j1/adapter/js/rangeSlider.js +1 -0
  29. data/assets/themes/j1/adapter/js/rouge.js +2 -1
  30. data/assets/themes/j1/adapter/js/rtable.js +1 -0
  31. data/assets/themes/j1/adapter/js/rtextResizer.js +2 -1
  32. data/assets/themes/j1/adapter/js/scroller.js +1 -0
  33. data/assets/themes/j1/adapter/js/slick.js +3 -2
  34. data/assets/themes/j1/adapter/js/themeToggler.js +1 -0
  35. data/assets/themes/j1/adapter/js/themer.js +1 -0
  36. data/assets/themes/j1/adapter/js/toccer.js +1 -0
  37. data/assets/themes/j1/adapter/js/translator.js +1 -0
  38. data/assets/themes/j1/adapter/js/waves.js +1 -0
  39. data/assets/themes/j1/core/js/template.js +16 -6
  40. data/assets/themes/j1/core/js/template.min.js +1 -1
  41. data/assets/themes/j1/core/js/template.min.js.map +1 -1
  42. data/assets/themes/j1/modules/backstretch/LICENSE-MIT +22 -0
  43. data/assets/themes/j1/modules/backstretch/README.md +411 -0
  44. data/assets/themes/j1/modules/backstretch/js/v2.1.16/backstretch.js +1 -3
  45. data/assets/themes/j1/modules/backstretch/js/v2.1.16/backstretch.min.js +1 -2
  46. data/assets/themes/j1/modules/clipboard/js/clipboard.js +0 -1
  47. data/assets/themes/j1/modules/clipboard/js/clipboard.min.js +3 -2
  48. data/assets/themes/j1/modules/dropdowns/js/cash.js +4 -5
  49. data/assets/themes/j1/modules/dropdowns/js/cash.min.js +19 -0
  50. data/assets/themes/j1/modules/dropdowns/js/dropdowns.min.js +20 -0
  51. data/assets/themes/j1/modules/fab/js/cash.js +0 -2
  52. data/assets/themes/j1/modules/fab/js/cash.min.js +20 -0
  53. data/assets/themes/j1/modules/fab/js/fab.min.js +20 -0
  54. data/assets/themes/j1/modules/iconPicker/icons-libraries/mdi-icons-light.json +0 -22
  55. data/assets/themes/j1/modules/iconPicker/icons-libraries/mdi-icons-light.min.json +1 -1
  56. data/assets/themes/j1/modules/iconifyAPI/js/iconify.js +13 -0
  57. data/assets/themes/j1/modules/msDropdown/css/themes/uno/msDropdown.css +2 -14
  58. data/assets/themes/j1/modules/msDropdown/css/themes/uno/msDropdown.min.css +3 -2
  59. data/assets/themes/j1/modules/rouge/css/base16/theme.min.css +19 -0
  60. data/assets/themes/j1/modules/rouge/css/base16.dark/theme.min.css +19 -0
  61. data/assets/themes/j1/modules/rouge/css/base16.light/theme.min.css +19 -0
  62. data/assets/themes/j1/modules/rouge/css/base16.monokai/theme.min.css +19 -0
  63. data/assets/themes/j1/modules/rouge/css/base16.monokai.dark/theme.min.css +19 -0
  64. data/assets/themes/j1/modules/rouge/css/base16.monokai.light/theme.min.css +19 -0
  65. data/assets/themes/j1/modules/rouge/css/base16.solarized/theme.min.css +19 -0
  66. data/assets/themes/j1/modules/rouge/css/base16.solarized.dark/theme.min.css +19 -0
  67. data/assets/themes/j1/modules/rouge/css/base16.solarized.light/theme.min.css +19 -0
  68. data/assets/themes/j1/modules/rouge/css/colorful/theme.min.css +19 -0
  69. data/assets/themes/j1/modules/rouge/css/github/theme.min.css +19 -0
  70. data/assets/themes/j1/modules/rouge/css/gruvbox/theme.min.css +19 -0
  71. data/assets/themes/j1/modules/rouge/css/gruvbox.dark/theme.min.css +19 -0
  72. data/assets/themes/j1/modules/rouge/css/gruvbox.light/theme.min.css +19 -0
  73. data/assets/themes/j1/modules/rouge/css/igorpro/theme.min.css +19 -0
  74. data/assets/themes/j1/modules/rouge/css/molokai/theme.min.css +19 -0
  75. data/assets/themes/j1/modules/rouge/css/monokai/theme.min.css +19 -0
  76. data/assets/themes/j1/modules/rouge/css/monokai.sublime/theme.min.css +19 -0
  77. data/assets/themes/j1/modules/rouge/css/pastie/theme.min.css +19 -0
  78. data/assets/themes/j1/modules/rouge/css/thankful_eyes/theme.min.css +19 -0
  79. data/assets/themes/j1/modules/rouge/css/tulip/theme.min.css +19 -0
  80. data/assets/themes/j1/modules/rouge/css/uno.dark/theme.min.css +19 -0
  81. data/assets/themes/j1/modules/rouge/css/uno.light/theme.min.css +19 -0
  82. data/assets/themes/j1/modules/slimSelect/css/select.css +486 -479
  83. data/assets/themes/j1/modules/slimSelect/css/select.min.css +8 -0
  84. data/assets/themes/j1/modules/slimSelect/js/select.js +1834 -1827
  85. data/assets/themes/j1/modules/slimSelect/js/select.min.js +8 -0
  86. data/assets/themes/j1/modules/vimeo/froogaloop/js/froogaloop2.js +4 -0
  87. data/lib/j1/version.rb +1 -1
  88. data/lib/starter_web/README.md +5 -5
  89. data/lib/starter_web/_config.yml +1 -1
  90. data/lib/starter_web/_data/modules/defaults/iconPicker.yml +3 -7
  91. data/lib/starter_web/_data/modules/lazyLoader.yml +5 -5
  92. data/lib/starter_web/_data/modules/navigator_menu.yml +7 -4
  93. data/lib/starter_web/_data/resources.yml +23 -28
  94. data/lib/starter_web/_data/templates/feed.xml +1 -1
  95. data/lib/starter_web/_plugins/index/lunr.rb +1 -1
  96. data/lib/starter_web/package.json +1 -1
  97. data/lib/starter_web/pages/public/learn/roundtrip/_includes/attributes.asciidoc +1 -0
  98. data/lib/starter_web/pages/public/learn/roundtrip/_includes/documents/themes_rouge.asciidoc +49 -40
  99. data/lib/starter_web/pages/public/learn/roundtrip/asciidoc_extensions.adoc +0 -1
  100. data/lib/starter_web/pages/public/learn/roundtrip/bootstrap_themes.adoc +20 -16
  101. data/lib/starter_web/pages/public/learn/roundtrip/highlghter_rouge.adoc +204 -0
  102. data/lib/starter_web/pages/public/tools/cheatsheet/gem.adoc +4 -4
  103. data/lib/starter_web/pages/public/tools/cheatsheet/git.adoc +13 -13
  104. data/lib/starter_web/pages/public/tools/cheatsheet/j1.adoc +113 -3
  105. data/lib/starter_web/utilsrv/_defaults/package.json +1 -1
  106. data/lib/starter_web/utilsrv/package.json +1 -1
  107. metadata +38 -9
  108. data/assets/themes/j1/adapter/js/translator.0.js +0 -498
  109. data/assets/themes/j1/adapter/js/translator.10.js +0 -510
  110. data/assets/themes/j1/modules/backstretch/js/v2.1.18/backstretch.js +0 -1627
  111. data/assets/themes/j1/modules/backstretch/js/v2.1.18/backstretch.min.js +0 -20
  112. data/lib/starter_web/pages/public/learn/roundtrip/_includes/attributes.0.asciidoc +0 -112
  113. /data/assets/themes/j1/modules/masonry/js/{masonry.pkgd.js → masonry.js} +0 -0
  114. /data/assets/themes/j1/modules/masonry/js/{masonry.pkgd.min.js → masonry.min.js} +0 -0
@@ -1,1627 +0,0 @@
1
- /*
2
- # -----------------------------------------------------------------------------
3
- # ~/assets/themes/j1/modules/backstretch/js/backstretch.js
4
- # Backstretch v.2.1.18 (Sept 2019) implementation for J1 Theme
5
- #
6
- # Product/Info:
7
- # https://jekyll.one
8
- # http://srobbin.com/jquery-plugins/backstretch/
9
- #
10
- # Copyright (C) 2023 Juergen Adams
11
- # Copyright (C) 2012 Scott Robbin
12
- # Copyright (C) 2019 Daniel Cohen Gindi
13
- #
14
- # J1 Theme is licensed under the MIT License.
15
- # See: https://github.com/jekyll-one-org/j1-template/blob/main/LICENSE.md
16
- # Backstretch is licensed under the MIT License.
17
- # See: https://github.com/jquery-backstretch/jquery-backstretch
18
- # -----------------------------------------------------------------------------
19
- */
20
-
21
- // -----------------------------------------------------------------------------
22
- // ESLint shimming
23
- // -----------------------------------------------------------------------------
24
- /* eslint indent: "off" */
25
- /* eslint no-unused-vars: "off" */
26
- /* eslint no-undef: "off" */
27
- /* eslint no-useless-escape: "off" */
28
- /* eslint no-prototype-builtins: "off" */
29
- /* eslint no-shadow-restricted-names: "off" */
30
- // -----------------------------------------------------------------------------
31
- ;(function ($, window, undefined) {
32
- 'use strict';
33
-
34
- /** @const */
35
- var YOUTUBE_REGEXP = /^.*(youtu\.be\/|youtube\.com\/v\/|youtube\.com\/embed\/|youtube\.com\/watch\?v=|youtube\.com\/watch\?.*\&v=)([^#\&\?]*).*/i;
36
-
37
- /* PLUGIN DEFINITION
38
- * ========================= */
39
-
40
- $.fn.backstretch = function (images, options) {
41
- var args = arguments;
42
-
43
- /*
44
- * Scroll the page one pixel to get the right window height on iOS
45
- * Pretty harmless for everyone else
46
- */
47
- if ($(window).scrollTop() === 0) {
48
- window.scrollTo(0, 0);
49
- }
50
-
51
- var returnValues;
52
-
53
- this.each(function (eachIndex) {
54
- var $this = $(this)
55
- , obj = $this.data('backstretch');
56
-
57
- // Do we already have an instance attached to this element?
58
- if (obj) {
59
-
60
- // Is this a method they're trying to execute?
61
- if (typeof args[0] === 'string' &&
62
- typeof obj[args[0]] === 'function') {
63
-
64
- // Call the method
65
- var returnValue = obj[args[0]].apply(obj, Array.prototype.slice.call(args, 1));
66
- if (returnValue === obj) { // If a method is chaining
67
- returnValue = undefined;
68
- }
69
- if (returnValue !== undefined) {
70
- returnValues = returnValues || [];
71
- returnValues[eachIndex] = returnValue;
72
- }
73
-
74
- return; // Nothing further to do
75
- }
76
-
77
- // Merge the old options with the new
78
- options = $.extend(obj.options, options);
79
-
80
- // Remove the old instance
81
- if (typeof obj === 'object' && 'destroy' in obj) {
82
- obj.destroy(true);
83
- }
84
- }
85
-
86
- // We need at least one image
87
- if (!images || (images && images.length === 0)) {
88
- var cssBackgroundImage = $this.css('background-image');
89
- if (cssBackgroundImage && cssBackgroundImage !== 'none') {
90
- images = [{url: $this.css('backgroundImage').replace(/url\(|\)|"|'/g, "")}];
91
- }
92
- else {
93
- $.error('No images were supplied for Backstretch, or element must have a CSS-defined background image.');
94
- }
95
- }
96
-
97
- obj = new Backstretch(this, images, options || {});
98
- $this.data('backstretch', obj);
99
- });
100
-
101
- return returnValues ? returnValues.length === 1 ? returnValues[0] : returnValues : this;
102
- };
103
-
104
- // If no element is supplied, we'll attach to body
105
- $.backstretch = function (images, options) {
106
- // Return the instance
107
- return $('body')
108
- .backstretch(images, options)
109
- .data('backstretch');
110
- };
111
-
112
- // Custom selector
113
- $.expr[':'].backstretch = function (elem) {
114
- return $(elem).data('backstretch') !== undefined;
115
- };
116
-
117
- /* DEFAULTS
118
- * ========================= */
119
-
120
- $.fn.backstretch.defaults = {
121
- duration: 5000 // Amount of time in between slides (if slideshow)
122
- , transition: 'fade' // Type of transition between slides
123
- , transitionDuration: 0 // Duration of transition between slides
124
- , animateFirst: true // Animate the transition of first image of slideshow in?
125
- , alignX: 0.5 // The x-alignment for the image, can be 'left'|'center'|'right' or any number between 0.0 and 1.0
126
- , alignY: 0.5 // The y-alignment for the image, can be 'top'|'center'|'bottom' or any number between 0.0 and 1.0
127
- , paused: false // Whether the images should slide after given duration
128
- , start: 0 // Index of the first image to show
129
- , preload: 2 // How many images preload at a time?
130
- , preloadSize: 1 // How many images can we preload in parallel?
131
- , resolutionRefreshRate: 2500 // How long to wait before switching resolution?
132
- , resolutionChangeRatioThreshold: 0.1 // How much a change should it be before switching resolution?
133
- };
134
-
135
- /* STYLES
136
- *
137
- * Baked-in styles that we'll apply to our elements.
138
- * In an effort to keep the plugin simple, these are not exposed as options.
139
- * That said, anyone can override these in their own stylesheet.
140
- * ========================= */
141
- var styles = {
142
- wrap: {
143
- left: 0
144
- , top: 0
145
- , overflow: 'hidden'
146
- , margin: 0
147
- , padding: 0
148
- , height: '100%'
149
- , width: '100%'
150
- , zIndex: -999999
151
- }
152
- , itemWrapper: {
153
- position: 'absolute'
154
- , display: 'none'
155
- , margin: 0
156
- , padding: 0
157
- , border: 'none'
158
- , width: '100%'
159
- , height: '100%'
160
- , zIndex: -999999
161
- }
162
- , item: {
163
- position: 'absolute'
164
- , margin: 0
165
- , padding: 0
166
- , border: 'none'
167
- , width: '100%'
168
- , height: '100%'
169
- , maxWidth: 'none'
170
- }
171
- };
172
-
173
- /* Given an array of different options for an image,
174
- * choose the optimal image for the container size.
175
- *
176
- * Given an image template (a string with {{ width }} and/or
177
- * {{height}} inside) and a container object, returns the
178
- * image url with the exact values for the size of that
179
- * container.
180
- *
181
- * Returns an array of urls optimized for the specified resolution.
182
- *
183
- */
184
- var optimalSizeImages = (function () {
185
-
186
- /* Sorts the array of image sizes based on width */
187
- var widthInsertSort = function (arr) {
188
- for (var i = 1; i < arr.length; i++) {
189
- var tmp = arr[i],
190
- j = i;
191
- while (arr[j - 1] && parseInt(arr[j - 1].width, 10) > parseInt(tmp.width, 10)) {
192
- arr[j] = arr[j - 1];
193
- --j;
194
- }
195
- arr[j] = tmp;
196
- }
197
-
198
- return arr;
199
- };
200
-
201
- /* Given an array of various sizes of the same image and a container width,
202
- * return the best image.
203
- */
204
- var selectBest = function (containerWidth, containerHeight, imageSizes) {
205
-
206
- var devicePixelRatio = window.devicePixelRatio || 1;
207
- var deviceOrientation = getDeviceOrientation();
208
- var windowOrientation = getWindowOrientation();
209
- var wrapperOrientation = (containerHeight > containerWidth) ?
210
- 'portrait' :
211
- (containerWidth > containerHeight ? 'landscape' : 'square');
212
-
213
- var lastAllowedImage = 0;
214
- var testWidth;
215
-
216
- for (var j = 0, image; j < imageSizes.length; j++) {
217
-
218
- image = imageSizes[j];
219
-
220
- // In case a new image was pushed in, process it:
221
- if (typeof image === 'string') {
222
- image = imageSizes[j] = {url: image};
223
- }
224
-
225
- if (image.pixelRatio && image.pixelRatio !== 'auto' && parseFloat(image.pixelRatio) !== devicePixelRatio) {
226
- // We disallowed choosing this image for current device pixel ratio,
227
- // So skip this one.
228
- continue;
229
- }
230
-
231
- if (image.deviceOrientation && image.deviceOrientation !== deviceOrientation) {
232
- // We disallowed choosing this image for current device orientation,
233
- // So skip this one.
234
- continue;
235
- }
236
-
237
- if (image.windowOrientation && image.windowOrientation !== deviceOrientation) {
238
- // We disallowed choosing this image for current window orientation,
239
- // So skip this one.
240
- continue;
241
- }
242
-
243
- if (image.orientation && image.orientation !== wrapperOrientation) {
244
- // We disallowed choosing this image for current element's orientation,
245
- // So skip this one.
246
- continue;
247
- }
248
-
249
- // Mark this one as the last one we investigated
250
- // which does not violate device pixel ratio rules.
251
- // We may choose this one later if there's no match.
252
- lastAllowedImage = j;
253
-
254
- // For most images, we match the specified width against element width,
255
- // And enforcing a limit depending on the "pixelRatio" property if specified.
256
- // But if a pixelRatio="auto", then we consider the width as the physical width of the image,
257
- // And match it while considering the device's pixel ratio.
258
- testWidth = containerWidth;
259
- if (image.pixelRatio === 'auto') {
260
- containerWidth *= devicePixelRatio;
261
- }
262
-
263
- // Stop when the width of the image is larger or equal to the container width
264
- if (image.width >= testWidth) {
265
- break;
266
- }
267
- }
268
-
269
- // Use the image located at where we stopped
270
- return imageSizes[Math.min(j, lastAllowedImage)];
271
- };
272
-
273
- var replaceTagsInUrl = function (url, templateReplacer) {
274
-
275
- if (typeof url === 'string') {
276
- url = url.replace(/{{(width|height)}}/g, templateReplacer);
277
- }
278
- else if (url instanceof Array) {
279
- for (var i = 0; i < url.length; i++) {
280
- if (url[i].src) {
281
- url[i].src = replaceTagsInUrl(url[i].src, templateReplacer);
282
- }
283
- else {
284
- url[i] = replaceTagsInUrl(url[i], templateReplacer);
285
- }
286
- }
287
- }
288
-
289
- return url;
290
- };
291
-
292
- return function ($container, images) {
293
- var containerWidth = $container.width(),
294
- containerHeight = $container.height();
295
-
296
- var chosenImages = [];
297
-
298
- var templateReplacer = function (match, key) {
299
- if (key === 'width') {
300
- return containerWidth;
301
- }
302
- if (key === 'height') {
303
- return containerHeight;
304
- }
305
- return match;
306
- };
307
-
308
- for (var i = 0; i < images.length; i++) {
309
- if ($.isArray(images[i])) {
310
- images[i] = widthInsertSort(images[i]);
311
- var chosen = selectBest(containerWidth, containerHeight, images[i]);
312
- chosenImages.push(chosen);
313
- }
314
- else {
315
- // In case a new image was pushed in, process it:
316
- if (typeof images[i] === 'string') {
317
- images[i] = {url: images[i]};
318
- }
319
-
320
- var item = $.extend({}, images[i]);
321
- item.url = replaceTagsInUrl(item.url, templateReplacer);
322
- chosenImages.push(item);
323
- }
324
- }
325
- return chosenImages;
326
- };
327
-
328
- })();
329
-
330
- var isVideoSource = function (source) {
331
- return YOUTUBE_REGEXP.test(source.url) || source.isVideo;
332
- };
333
-
334
- /* Preload images */
335
- var preload = (function (sources, startAt, count, batchSize, callback) {
336
- // Plugin cache
337
- var cache = [];
338
-
339
- // Wrapper for cache
340
- var caching = function (image) {
341
- for (var i = 0; i < cache.length; i++) {
342
- if (cache[i].src === image.src) {
343
- return cache[i];
344
- }
345
- }
346
- cache.push(image);
347
- return image;
348
- };
349
-
350
- // Execute callback
351
- var exec = function (sources, callback, last) {
352
- if (typeof callback === 'function') {
353
- callback.call(sources, last);
354
- }
355
- };
356
-
357
- // Closure to hide cache
358
- return function preload(sources, startAt, count, batchSize, callback) {
359
- // Check input data
360
- if (typeof sources === 'undefined') {
361
- return;
362
- }
363
- if (!$.isArray(sources)) {
364
- sources = [sources];
365
- }
366
-
367
- if (arguments.length < 5 && typeof arguments[arguments.length - 1] === 'function') {
368
- callback = arguments[arguments.length - 1];
369
- }
370
-
371
- startAt = (typeof startAt === 'function' || !startAt) ? 0 : startAt;
372
- count = (typeof count === 'function' || !count || count < 0) ? sources.length : Math.min(count, sources.length);
373
- batchSize = (typeof batchSize === 'function' || !batchSize) ? 1 : batchSize;
374
-
375
- if (startAt >= sources.length) {
376
- startAt = 0;
377
- count = 0;
378
- }
379
- if (batchSize < 0) {
380
- batchSize = count;
381
- }
382
- batchSize = Math.min(batchSize, count);
383
-
384
- var next = sources.slice(startAt + batchSize, count - batchSize);
385
- sources = sources.slice(startAt, batchSize);
386
- count = sources.length;
387
-
388
- // If sources array is empty
389
- if (!count) {
390
- exec(sources, callback, true);
391
- return;
392
- }
393
-
394
- // Image loading callback
395
- var countLoaded = 0;
396
-
397
- var loaded = function () {
398
- countLoaded++;
399
- if (countLoaded !== count) {
400
- return;
401
- }
402
-
403
- exec(sources, callback, !next);
404
- preload(next, 0, 0, batchSize, callback);
405
- };
406
-
407
- // Loop sources to preload
408
- var image;
409
-
410
- for (var i = 0; i < sources.length; i++) {
411
-
412
- if (isVideoSource(sources[i])) {
413
-
414
- // Do not preload videos. There are issues with that.
415
- // First - we need to keep an instance of the preloaded and use that exactly, not a copy.
416
- // Second - there are memory issues.
417
- // If there will be a requirement from users - I'll try to implement this.
418
-
419
- continue;
420
-
421
- }
422
- else {
423
-
424
- image = new Image();
425
- image.src = sources[i].url;
426
-
427
- image = caching(image);
428
-
429
- if (image.complete) {
430
- loaded();
431
- }
432
- else {
433
- $(image).on('load error', loaded);
434
- }
435
-
436
- }
437
-
438
- }
439
- };
440
- })();
441
-
442
- /* Process images array */
443
- var processImagesArray = function (images) {
444
- var processed = [];
445
- for (var i = 0; i < images.length; i++) {
446
- if (typeof images[i] === 'string') {
447
- processed.push({url: images[i]});
448
- }
449
- else if ($.isArray(images[i])) {
450
- processed.push(processImagesArray(images[i]));
451
- }
452
- else {
453
- processed.push(processOptions(images[i]));
454
- }
455
- }
456
- return processed;
457
- };
458
-
459
- /* Process options */
460
- var processOptions = function (options, required) {
461
-
462
- // Convert old options
463
-
464
- // centeredX/centeredY are deprecated
465
- if (options.centeredX || options.centeredY) {
466
- if (window.console && window.console.log) {
467
- window.console.log('jquery.backstretch: `centeredX`/`centeredY` is deprecated, please use `alignX`/`alignY`');
468
- }
469
- if (options.centeredX) {
470
- options.alignX = 0.5;
471
- }
472
- if (options.centeredY) {
473
- options.alignY = 0.5;
474
- }
475
- }
476
-
477
- // Deprecated spec
478
- if (options.speed !== undefined) {
479
-
480
- if (window.console && window.console.log) {
481
- window.console.log('jquery.backstretch: `speed` is deprecated, please use `transitionDuration`');
482
- }
483
-
484
- options.transitionDuration = options.speed;
485
- options.transition = 'fade';
486
- }
487
-
488
- // Typo
489
- if (options.resolutionChangeRatioTreshold !== undefined) {
490
- window.console.log('jquery.backstretch: `treshold` is a typo!');
491
- options.resolutionChangeRatioThreshold = options.resolutionChangeRatioTreshold;
492
- }
493
-
494
- // Current spec that needs processing
495
-
496
- if (options.fadeFirst !== undefined) {
497
- options.animateFirst = options.fadeFirst;
498
- }
499
-
500
- if (options.fade !== undefined) {
501
- options.transitionDuration = options.fade;
502
- options.transition = 'fade';
503
- }
504
-
505
- if (options.scale) {
506
- options.scale = validScale(options.scale);
507
- }
508
-
509
- return processAlignOptions(options);
510
- };
511
-
512
- /* Process align options */
513
- var processAlignOptions = function (options, required) {
514
- if (options.alignX === 'left') {
515
- options.alignX = 0.0;
516
- }
517
- else if (options.alignX === 'center') {
518
- options.alignX = 0.5;
519
- }
520
- else if (options.alignX === 'right') {
521
- options.alignX = 1.0;
522
- }
523
- else {
524
- if (options.alignX !== undefined || required) {
525
- options.alignX = parseFloat(options.alignX);
526
- if (isNaN(options.alignX)) {
527
- options.alignX = 0.5;
528
- }
529
- }
530
- }
531
-
532
- if (options.alignY === 'top') {
533
- options.alignY = 0.0;
534
- }
535
- else if (options.alignY === 'center') {
536
- options.alignY = 0.5;
537
- }
538
- else if (options.alignY === 'bottom') {
539
- options.alignY = 1.0;
540
- }
541
- else {
542
- if (options.alignX !== undefined || required) {
543
- options.alignY = parseFloat(options.alignY);
544
- if (isNaN(options.alignY)) {
545
- options.alignY = 0.5;
546
- }
547
- }
548
- }
549
-
550
- return options;
551
- };
552
-
553
- var SUPPORTED_SCALE_OPTIONS = {
554
- 'cover': 'cover',
555
- 'fit': 'fit',
556
- 'fit-smaller': 'fit-smaller',
557
- 'fill': 'fill'
558
- };
559
-
560
- function validScale(scale) {
561
- if (!SUPPORTED_SCALE_OPTIONS.hasOwnProperty(scale)) {
562
- return 'cover';
563
- }
564
- return scale;
565
- }
566
-
567
- /* CLASS DEFINITION
568
- * ========================= */
569
- var Backstretch = function (container, images, options) {
570
- this.options = $.extend({}, $.fn.backstretch.defaults, options || {});
571
-
572
- this.firstShow = true;
573
-
574
- // Process options
575
- processOptions(this.options, true);
576
-
577
- /* In its simplest form, we allow Backstretch to be called on an image path.
578
- * e.g. $.backstretch('/path/to/image.jpg')
579
- * So, we need to turn this back into an array.
580
- */
581
- this.images = processImagesArray($.isArray(images) ? images : [images]);
582
-
583
- /**
584
- * Paused-Option
585
- */
586
- if (this.options.paused) {
587
- this.paused = true;
588
- }
589
-
590
- /**
591
- * Start-Option (Index)
592
- */
593
- if (this.options.start >= this.images.length) {
594
- this.options.start = this.images.length - 1;
595
- }
596
- if (this.options.start < 0) {
597
- this.options.start = 0;
598
- }
599
-
600
- // Convenience reference to know if the container is body.
601
- this.isBody = container === document.body;
602
-
603
- /* We're keeping track of a few different elements
604
- *
605
- * Container: the element that Backstretch was called on.
606
- * Wrap: a DIV that we place the image into, so we can hide the overflow.
607
- * Root: Convenience reference to help calculate the correct height.
608
- */
609
- var $window = $(window);
610
- this.$container = $(container);
611
- this.$root = this.isBody ? supportsFixedPosition ? $window : $(document) : this.$container;
612
-
613
- this.originalImages = this.images;
614
- this.images = optimalSizeImages(
615
- this.options.alwaysTestWindowResolution ? $window : this.$root,
616
- this.originalImages);
617
-
618
- /**
619
- * Pre-Loading.
620
- * This is the first image, so we will preload a minimum of 1 images.
621
- */
622
- preload(this.images, this.options.start || 0, this.options.preload || 1);
623
-
624
- // Don't create a new wrap if one already exists (from a previous instance of Backstretch)
625
- var $existing = this.$container.children(".backstretch").first();
626
- this.$wrap = $existing.length ? $existing :
627
- $('<div class="backstretch"></div>')
628
- .css(this.options.bypassCss ? {} : styles.wrap)
629
- .appendTo(this.$container);
630
-
631
- if (!this.options.bypassCss) {
632
-
633
- // Non-body elements need some style adjustments
634
- if (!this.isBody) {
635
- // If the container is statically positioned, we need to make it relative,
636
- // and if no zIndex is defined, we should set it to zero.
637
- var position = this.$container.css('position')
638
- , zIndex = this.$container.css('zIndex');
639
-
640
- this.$container.css({
641
- position: position === 'static' ? 'relative' : position
642
- , zIndex: zIndex === 'auto' ? 0 : zIndex
643
- });
644
-
645
- // Needs a higher z-index
646
- this.$wrap.css({zIndex: -999998});
647
- }
648
-
649
- // Fixed or absolute positioning?
650
- this.$wrap.css({
651
- position: this.isBody && supportsFixedPosition ? 'fixed' : 'absolute'
652
- });
653
-
654
- }
655
-
656
- // Set the first image
657
- this.index = this.options.start;
658
- this.show(this.index);
659
-
660
- // Listen for resize
661
- $window.on('resize.backstretch', $.proxy(this.resize, this))
662
- .on('orientationchange.backstretch', $.proxy(function () {
663
- // Need to do this in order to get the right window height
664
- if (this.isBody && window.pageYOffset === 0) {
665
- window.scrollTo(0, 1);
666
- this.resize();
667
- }
668
- }, this));
669
- };
670
-
671
- var performTransition = function (options) {
672
-
673
- var transition = options.transition || 'fade';
674
-
675
- // Look for multiple options
676
- if (typeof transition === 'string' && transition.indexOf('|') > -1) {
677
- transition = transition.split('|');
678
- }
679
-
680
- if (transition instanceof Array) {
681
- transition = transition[Math.round(Math.random() * (transition.length - 1))];
682
- }
683
-
684
- var $new = options['new'];
685
- var $old = options['old'] ? options['old'] : $([]);
686
-
687
- switch (transition.toString().toLowerCase()) {
688
-
689
- default:
690
- case 'fade':
691
- $new.fadeIn({
692
- duration: options.duration,
693
- complete: options.complete,
694
- easing: options.easing || undefined
695
- });
696
- break;
697
-
698
- case 'fadeinout':
699
- case 'fade_in_out':
700
-
701
- var fadeInNew = function () {
702
- $new.fadeIn({
703
- duration: options.duration / 2,
704
- complete: options.complete,
705
- easing: options.easing || undefined
706
- });
707
- };
708
-
709
- if ($old.length) {
710
- $old.fadeOut({
711
- duration: options.duration / 2,
712
- complete: fadeInNew,
713
- easing: options.easing || undefined
714
- });
715
- }
716
- else {
717
- fadeInNew();
718
- }
719
-
720
- break;
721
-
722
- case 'pushleft':
723
- case 'push_left':
724
- case 'pushright':
725
- case 'push_right':
726
- case 'pushup':
727
- case 'push_up':
728
- case 'pushdown':
729
- case 'push_down':
730
- case 'coverleft':
731
- case 'cover_left':
732
- case 'coverright':
733
- case 'cover_right':
734
- case 'coverup':
735
- case 'cover_up':
736
- case 'coverdown':
737
- case 'cover_down':
738
-
739
- var transitionParts = transition.match(/^(cover|push)_?(.*)$/);
740
-
741
- var animProp = transitionParts[2] === 'left' ? 'right' :
742
- transitionParts[2] === 'right' ? 'left' :
743
- transitionParts[2] === 'down' ? 'top' :
744
- transitionParts[2] === 'up' ? 'bottom' :
745
- 'right';
746
-
747
- var newCssStart = {
748
- 'display': ''
749
- }, newCssAnim = {};
750
- newCssStart[animProp] = '-100%';
751
- newCssAnim[animProp] = 0;
752
-
753
- $new
754
- .css(newCssStart)
755
- .animate(newCssAnim, {
756
- duration: options.duration,
757
- complete: function () {
758
- $new.css(animProp, '');
759
- options.complete.apply(this, arguments);
760
- },
761
- easing: options.easing || undefined
762
- });
763
-
764
- if (transitionParts[1] === 'push' && $old.length) {
765
- var oldCssAnim = {};
766
- oldCssAnim[animProp] = '100%';
767
-
768
- $old
769
- .animate(oldCssAnim, {
770
- duration: options.duration,
771
- complete: function () {
772
- $old.css('display', 'none');
773
- },
774
- easing: options.easing || undefined
775
- });
776
- }
777
-
778
- break;
779
- }
780
-
781
- };
782
-
783
- /* PUBLIC METHODS
784
- * ========================= */
785
- Backstretch.prototype = {
786
-
787
- resize: function () {
788
- try {
789
-
790
- // Check for a better suited image after the resize
791
- var $resTest = this.options.alwaysTestWindowResolution ? $(window) : this.$root;
792
- var newContainerWidth = $resTest.width();
793
- var newContainerHeight = $resTest.height();
794
- var changeRatioW = newContainerWidth / (this._lastResizeContainerWidth || 0);
795
- var changeRatioH = newContainerHeight / (this._lastResizeContainerHeight || 0);
796
- var resolutionChangeRatioThreshold = this.options.resolutionChangeRatioThreshold || 0.0;
797
-
798
- // check for big changes in container size
799
- if ((newContainerWidth !== this._lastResizeContainerWidth ||
800
- newContainerHeight !== this._lastResizeContainerHeight) &&
801
- ((Math.abs(changeRatioW - 1) >= resolutionChangeRatioThreshold || isNaN(changeRatioW)) ||
802
- (Math.abs(changeRatioH - 1) >= resolutionChangeRatioThreshold || isNaN(changeRatioH)))) {
803
-
804
- this._lastResizeContainerWidth = newContainerWidth;
805
- this._lastResizeContainerHeight = newContainerHeight;
806
-
807
- // Big change: rebuild the entire images array
808
- this.images = optimalSizeImages($resTest, this.originalImages);
809
-
810
- // Preload them (they will be automatically inserted on the next cycle)
811
- if (this.options.preload) {
812
- preload(this.images, (this.index + 1) % this.images.length, this.options.preload);
813
- }
814
-
815
- // In case there is no cycle and the new source is different than the current
816
- if (this.images.length === 1 &&
817
- this._currentImage.url !== this.images[0].url) {
818
-
819
- // Wait a little an update the image being showed
820
- var that = this;
821
- clearTimeout(that._selectAnotherResolutionTimeout);
822
- that._selectAnotherResolutionTimeout = setTimeout(function () {
823
- that.show(0);
824
- }, this.options.resolutionRefreshRate);
825
- }
826
- }
827
-
828
- var bgCSS = {left: 0, top: 0, right: 'auto', bottom: 'auto'}
829
-
830
- , boxWidth = this.isBody ? this.$root.width() : this.$root.innerWidth()
831
- , boxHeight = this.isBody
832
- ? (window.innerHeight ? window.innerHeight : this.$root.height())
833
- : this.$root.innerHeight()
834
-
835
- , naturalWidth = this.$itemWrapper.data('width')
836
- , naturalHeight = this.$itemWrapper.data('height')
837
-
838
- , ratio = (naturalWidth / naturalHeight) || 1
839
-
840
- , alignX = this._currentImage.alignX === undefined ? this.options.alignX : this._currentImage.alignX
841
- , alignY = this._currentImage.alignY === undefined ? this.options.alignY : this._currentImage.alignY
842
- , scale = validScale(this._currentImage.scale || this.options.scale);
843
-
844
- var width, height;
845
-
846
- if (scale === 'fit' || scale === 'fit-smaller') {
847
- width = naturalWidth;
848
- height = naturalHeight;
849
-
850
- if (width > boxWidth ||
851
- height > boxHeight ||
852
- scale === 'fit-smaller') {
853
- var boxRatio = boxWidth / boxHeight;
854
- if (boxRatio > ratio) {
855
- width = Math.floor(boxHeight * ratio);
856
- height = boxHeight;
857
- }
858
- else if (boxRatio < ratio) {
859
- width = boxWidth;
860
- height = Math.floor(boxWidth / ratio);
861
- }
862
- else {
863
- width = boxWidth;
864
- height = boxHeight;
865
- }
866
- }
867
- }
868
- else if (scale === 'fill') {
869
- width = boxWidth;
870
- height = boxHeight;
871
- }
872
- else { // 'cover'
873
- width = Math.max(boxHeight * ratio, boxWidth);
874
- height = Math.max(width / ratio, boxHeight);
875
- }
876
-
877
- // Make adjustments based on image ratio
878
- bgCSS.top = -(height - boxHeight) * alignY;
879
- bgCSS.left = -(width - boxWidth) * alignX;
880
- bgCSS.width = width;
881
- bgCSS.height = height;
882
-
883
- if (!this.options.bypassCss) {
884
-
885
- this.$wrap
886
- .css({width: boxWidth, height: boxHeight})
887
- .find('>.backstretch-item').not('.deleteable')
888
- .each(function () {
889
- var $wrapper = $(this);
890
- $wrapper.find('img,video,iframe')
891
- .css(bgCSS);
892
- });
893
- }
894
-
895
- var evt = $.Event('backstretch.resize', {
896
- relatedTarget: this.$container[0]
897
- });
898
- this.$container.trigger(evt, this);
899
-
900
- }
901
- catch (err) {
902
- // IE7 seems to trigger resize before the image is loaded.
903
- // This try/catch block is a hack to let it fail gracefully.
904
- }
905
-
906
- return this;
907
- }
908
-
909
- // Show the slide at a certain position
910
- , show: function (newIndex, overrideOptions) {
911
-
912
- // Validate index
913
- if (Math.abs(newIndex) > this.images.length - 1) {
914
- return;
915
- }
916
-
917
- // Vars
918
- var that = this
919
- , $oldItemWrapper = that.$wrap.find('>.backstretch-item').addClass('deleteable')
920
- , oldVideoWrapper = that.videoWrapper
921
- , evtOptions = {relatedTarget: that.$container[0]};
922
-
923
- // Trigger the "before" event
924
- that.$container.trigger($.Event('backstretch.before', evtOptions), [that, newIndex]);
925
-
926
- // Set the new frame index
927
- this.index = newIndex;
928
- var selectedImage = that.images[newIndex];
929
-
930
- // Pause the slideshow
931
- clearTimeout(that._cycleTimeout);
932
-
933
- // New image
934
-
935
- delete that.videoWrapper; // Current item may not be a video
936
-
937
- var isVideo = isVideoSource(selectedImage);
938
- if (isVideo) {
939
- that.videoWrapper = new VideoWrapper(selectedImage);
940
- that.$item = that.videoWrapper.$video.css('pointer-events', 'none');
941
- }
942
- else {
943
- that.$item = $('<img />');
944
- }
945
-
946
- that.$itemWrapper = $('<div class="backstretch-item">')
947
- .append(that.$item);
948
-
949
- if (this.options.bypassCss) {
950
- that.$itemWrapper.css({
951
- 'display': 'none'
952
- });
953
- }
954
- else {
955
- that.$itemWrapper.css(styles.itemWrapper);
956
- that.$item.css(styles.item);
957
- }
958
-
959
- that.$item.bind(isVideo ? 'canplay' : 'load', function (e) {
960
- var $this = $(this)
961
- , $wrapper = $this.parent()
962
- , options = $wrapper.data('options');
963
-
964
- if (overrideOptions) {
965
- options = $.extend({}, options, overrideOptions);
966
- }
967
-
968
- var imgWidth = this.naturalWidth || this.videoWidth || this.width
969
- , imgHeight = this.naturalHeight || this.videoHeight || this.height;
970
-
971
- // Save the natural dimensions
972
- $wrapper
973
- .data('width', imgWidth)
974
- .data('height', imgHeight);
975
-
976
- var getOption = function (opt) {
977
- return options[opt] !== undefined ?
978
- options[opt] :
979
- that.options[opt];
980
- };
981
-
982
- var transition = getOption('transition');
983
- var transitionEasing = getOption('transitionEasing');
984
- var transitionDuration = getOption('transitionDuration');
985
-
986
- // Show the image, then delete the old one
987
- var bringInNextImage = function () {
988
-
989
- if (oldVideoWrapper) {
990
- oldVideoWrapper.stop();
991
- oldVideoWrapper.destroy();
992
- }
993
-
994
- $oldItemWrapper.remove();
995
-
996
- // Resume the slideshow
997
- if (!that.paused && that.images.length > 1) {
998
- that.cycle();
999
- }
1000
-
1001
- // Now we can clear the background on the element, to spare memory
1002
- if (!that.options.bypassCss && !that.isBody) {
1003
- that.$container.css('background-image', 'none');
1004
- }
1005
-
1006
- // Trigger the "after" and "show" events
1007
- // "show" is being deprecated
1008
- $(['after', 'show']).each(function () {
1009
- that.$container.trigger($.Event('backstretch.' + this, evtOptions), [that, newIndex]);
1010
- });
1011
-
1012
- if (isVideo) {
1013
- that.videoWrapper.play();
1014
- }
1015
- };
1016
-
1017
- if ((that.firstShow && !that.options.animateFirst) || !transitionDuration || !transition) {
1018
- // Avoid transition on first show or if there's no transitionDuration value
1019
- $wrapper.show();
1020
- bringInNextImage();
1021
- }
1022
- else {
1023
-
1024
- performTransition({
1025
- 'new': $wrapper,
1026
- old: $oldItemWrapper,
1027
- transition: transition,
1028
- duration: transitionDuration,
1029
- easing: transitionEasing,
1030
- complete: bringInNextImage
1031
- });
1032
-
1033
- }
1034
-
1035
- that.firstShow = false;
1036
-
1037
- // Resize
1038
- that.resize();
1039
- });
1040
-
1041
- that.$itemWrapper.appendTo(that.$wrap);
1042
-
1043
- that.$item.attr('alt', selectedImage.alt || '');
1044
- that.$itemWrapper.data('options', selectedImage);
1045
-
1046
- if (!isVideo) {
1047
- that.$item.attr('src', selectedImage.url);
1048
- }
1049
-
1050
- that._currentImage = selectedImage;
1051
-
1052
- return that;
1053
- }
1054
-
1055
- , current: function () {
1056
- return this.index;
1057
- }
1058
-
1059
- , next: function () {
1060
- var args = Array.prototype.slice.call(arguments, 0);
1061
- args.unshift(this.index < this.images.length - 1 ? this.index + 1 : 0);
1062
- return this.show.apply(this, args);
1063
- }
1064
-
1065
- , prev: function () {
1066
- var args = Array.prototype.slice.call(arguments, 0);
1067
- args.unshift(this.index === 0 ? this.images.length - 1 : this.index - 1);
1068
- return this.show.apply(this, args);
1069
- }
1070
-
1071
- , pause: function () {
1072
- // Pause the slideshow
1073
- this.paused = true;
1074
-
1075
- if (this.videoWrapper) {
1076
- this.videoWrapper.pause();
1077
- }
1078
-
1079
- return this;
1080
- }
1081
-
1082
- , resume: function () {
1083
- // Resume the slideshow
1084
- this.paused = false;
1085
-
1086
- if (this.videoWrapper) {
1087
- this.videoWrapper.play();
1088
- }
1089
-
1090
- this.cycle();
1091
- return this;
1092
- }
1093
-
1094
- , cycle: function () {
1095
- // Start/resume the slideshow
1096
- if (this.images.length > 1) {
1097
- // Clear the timeout, just in case
1098
- clearTimeout(this._cycleTimeout);
1099
-
1100
- var duration = (this._currentImage && this._currentImage.duration) || this.options.duration;
1101
- var isVideo = isVideoSource(this._currentImage);
1102
-
1103
- var callNext = function () {
1104
- this.$item.off('.cycle');
1105
-
1106
- // Check for paused slideshow
1107
- if (!this.paused) {
1108
- this.next();
1109
- }
1110
- };
1111
-
1112
- // Special video handling
1113
- if (isVideo) {
1114
-
1115
- // Leave video at last frame
1116
- if (!this._currentImage.loop) {
1117
- var lastFrameTimeout = 0;
1118
-
1119
- this.$item
1120
- .on('playing.cycle', function () {
1121
- var player = $(this).data('player');
1122
-
1123
- clearTimeout(lastFrameTimeout);
1124
- lastFrameTimeout = setTimeout(function () {
1125
- player.pause();
1126
- player.$video.trigger('ended');
1127
- }, (player.getDuration() - player.getCurrentTime()) * 1000);
1128
- })
1129
- .on('ended.cycle', function () {
1130
- clearTimeout(lastFrameTimeout);
1131
- });
1132
- }
1133
-
1134
- // On error go to next
1135
- this.$item.on('error.cycle initerror.cycle', $.proxy(callNext, this));
1136
- }
1137
-
1138
- if (isVideo && !this._currentImage.duration) {
1139
- // It's a video - playing until end
1140
- this.$item.on('ended.cycle', $.proxy(callNext, this));
1141
-
1142
- }
1143
- else {
1144
- // Cycling according to specified duration
1145
- this._cycleTimeout = setTimeout($.proxy(callNext, this), duration);
1146
- }
1147
-
1148
- }
1149
- return this;
1150
- }
1151
-
1152
- , destroy: function (preserveBackground) {
1153
- // Stop the resize events
1154
- $(window).off('resize.backstretch orientationchange.backstretch');
1155
-
1156
- // Stop any videos
1157
- if (this.videoWrapper) {
1158
- this.videoWrapper.destroy();
1159
- }
1160
-
1161
- // Clear the timeout
1162
- clearTimeout(this._cycleTimeout);
1163
-
1164
- // Remove Backstretch
1165
- if (!preserveBackground) {
1166
- this.$wrap.remove();
1167
- }
1168
- this.$container.removeData('backstretch');
1169
- }
1170
- };
1171
-
1172
- /**
1173
- * Video Abstraction Layer
1174
- *
1175
- * Static methods:
1176
- * > VideoWrapper.loadYoutubeAPI() -> Call in order to load the Youtube API.
1177
- * An 'youtube_api_load' event will be triggered on $(window) when the API is loaded.
1178
- *
1179
- * Generic:
1180
- * > player.type -> type of the video
1181
- * > player.video / player.$video -> contains the element holding the video
1182
- * > player.play() -> plays the video
1183
- * > player.pause() -> pauses the video
1184
- * > player.setCurrentTime(position) -> seeks to a position by seconds
1185
- *
1186
- * Youtube:
1187
- * > player.ytId will contain the youtube ID if the source is a youtube url
1188
- * > player.ytReady is a flag telling whether the youtube source is ready for playback
1189
- * */
1190
-
1191
- var VideoWrapper = function () { this.init.apply(this, arguments); };
1192
-
1193
- /**
1194
- * @param {Object} options
1195
- * @param {String|Array<String>|Array<{{src: String, type: String?}}>} options.url
1196
- * @param {Boolean} options.loop=false
1197
- * @param {Boolean?} options.mute=true
1198
- * @param {String?} options.poster
1199
- * loop, mute, poster
1200
- */
1201
- VideoWrapper.prototype.init = function (options) {
1202
-
1203
- var that = this;
1204
-
1205
- var $video;
1206
-
1207
- var setVideoElement = function () {
1208
- that.$video = $video;
1209
- that.video = $video[0];
1210
- };
1211
-
1212
- // Determine video type
1213
-
1214
- var videoType = 'video';
1215
-
1216
- if (!(options.url instanceof Array) &&
1217
- YOUTUBE_REGEXP.test(options.url)) {
1218
- videoType = 'youtube';
1219
- }
1220
-
1221
- that.type = videoType;
1222
-
1223
- if (videoType === 'youtube') {
1224
-
1225
- // Try to load the API in the meantime
1226
- VideoWrapper.loadYoutubeAPI();
1227
-
1228
- that.ytId = options.url.match(YOUTUBE_REGEXP)[2];
1229
- var src = 'https://www.youtube.com/embed/' + that.ytId +
1230
- '?rel=0&autoplay=0&showinfo=0&controls=0&modestbranding=1' +
1231
- '&cc_load_policy=0&disablekb=1&iv_load_policy=3&loop=0' +
1232
- '&enablejsapi=1&origin=' + encodeURIComponent(window.location.origin);
1233
-
1234
- that.__ytStartMuted = !!options.mute || options.mute === undefined;
1235
-
1236
- $video = $('<iframe />')
1237
- .attr({'src_to_load': src})
1238
- .css({'border': 0, 'margin': 0, 'padding': 0})
1239
- .data('player', that);
1240
-
1241
- if (options.loop) {
1242
- $video.on('ended.loop', function () {
1243
- if (!that.__manuallyStopped) {
1244
- that.play();
1245
- }
1246
- });
1247
- }
1248
-
1249
- that.ytReady = false;
1250
-
1251
- setVideoElement();
1252
-
1253
- if (window['YT'] && window['YT'].loaded) {
1254
- that._initYoutube();
1255
- $video.trigger('initsuccess');
1256
- }
1257
- else {
1258
- $(window).one('youtube_api_load', function () {
1259
- that._initYoutube();
1260
- $video.trigger('initsuccess');
1261
- });
1262
- }
1263
-
1264
- }
1265
- else {
1266
- // Traditional <video> tag with multiple sources
1267
-
1268
- $video = $('<video>')
1269
- .prop('autoplay', false)
1270
- .prop('controls', false)
1271
- .prop('loop', !!options.loop)
1272
- .prop('muted', !!options.mute || options.mute === undefined)
1273
-
1274
- // Let the first frames be available before playback, as we do transitions
1275
- .prop('preload', 'auto')
1276
- .prop('poster', options.poster || '');
1277
-
1278
- var sources = (options.url instanceof Array) ? options.url : [options.url];
1279
-
1280
- for (var i = 0; i < sources.length; i++) {
1281
- var sourceItem = sources[i];
1282
- if (typeof (sourceItem) === 'string') {
1283
- sourceItem = {src: sourceItem};
1284
- }
1285
- $('<source>')
1286
- .attr('src', sourceItem.src)
1287
- // Make sure to not specify type if unknown -
1288
- // so the browser will try to autodetect.
1289
- .attr('type', sourceItem.type || null)
1290
- .appendTo($video);
1291
- }
1292
-
1293
- if (!$video[0].canPlayType || !sources.length) {
1294
- $video.trigger('initerror');
1295
- }
1296
- else {
1297
- $video.trigger('initsuccess');
1298
- }
1299
-
1300
- setVideoElement();
1301
- }
1302
-
1303
- };
1304
-
1305
- VideoWrapper.prototype._initYoutube = function () {
1306
- var that = this;
1307
-
1308
- var YT = window['YT'];
1309
-
1310
- that.$video
1311
- .attr('src', that.$video.attr('src_to_load'))
1312
- .removeAttr('src_to_load');
1313
-
1314
- // It won't init if it's not in the DOM, so we emulate that
1315
- var hasParent = !!that.$video[0].parentNode;
1316
- if (!hasParent) {
1317
- var $tmpParent = $('<div>').css('display', 'none !important').appendTo(document.body);
1318
- that.$video.appendTo($tmpParent);
1319
- }
1320
-
1321
- var player = new YT.Player(that.video, {
1322
- events: {
1323
- 'onReady': function () {
1324
-
1325
- if (that.__ytStartMuted) {
1326
- player.mute();
1327
- }
1328
-
1329
- if (!hasParent) {
1330
- // Restore parent to old state - without interrupting any changes
1331
- if (that.$video[0].parentNode === $tmpParent[0]) {
1332
- that.$video.detach();
1333
- }
1334
- $tmpParent.remove();
1335
- }
1336
-
1337
- that.ytReady = true;
1338
- that._updateYoutubeSize();
1339
- that.$video.trigger('canplay');
1340
- },
1341
- 'onStateChange': function (event) {
1342
- switch (event.data) {
1343
- case YT.PlayerState.PLAYING:
1344
- that.$video.trigger('playing');
1345
- break;
1346
- case YT.PlayerState.ENDED:
1347
- that.$video.trigger('ended');
1348
- break;
1349
- case YT.PlayerState.PAUSED:
1350
- that.$video.trigger('pause');
1351
- break;
1352
- case YT.PlayerState.BUFFERING:
1353
- that.$video.trigger('waiting');
1354
- break;
1355
- case YT.PlayerState.CUED:
1356
- that.$video.trigger('canplay');
1357
- break;
1358
- }
1359
- },
1360
- 'onPlaybackQualityChange': function () {
1361
- that._updateYoutubeSize();
1362
- that.$video.trigger('resize');
1363
- },
1364
- 'onError': function (err) {
1365
- that.hasError = true;
1366
- that.$video.trigger({'type': 'error', 'error': err});
1367
- }
1368
- }
1369
- });
1370
-
1371
- that.ytPlayer = player;
1372
-
1373
- return that;
1374
- };
1375
-
1376
- VideoWrapper.prototype._updateYoutubeSize = function () {
1377
- var that = this;
1378
-
1379
- switch (that.ytPlayer.getPlaybackQuality() || 'medium') {
1380
- case 'small':
1381
- that.video.videoWidth = 426;
1382
- that.video.videoHeight = 240;
1383
- break;
1384
- case 'medium':
1385
- that.video.videoWidth = 640;
1386
- that.video.videoHeight = 360;
1387
- break;
1388
- default:
1389
- case 'large':
1390
- that.video.videoWidth = 854;
1391
- that.video.videoHeight = 480;
1392
- break;
1393
- case 'hd720':
1394
- that.video.videoWidth = 1280;
1395
- that.video.videoHeight = 720;
1396
- break;
1397
- case 'hd1080':
1398
- that.video.videoWidth = 1920;
1399
- that.video.videoHeight = 1080;
1400
- break;
1401
- case 'highres':
1402
- that.video.videoWidth = 2560;
1403
- that.video.videoHeight = 1440;
1404
- break;
1405
- }
1406
-
1407
- return that;
1408
- };
1409
-
1410
- VideoWrapper.prototype.play = function () {
1411
- var that = this;
1412
-
1413
- that.__manuallyStopped = false;
1414
-
1415
- if (that.type === 'youtube') {
1416
- if (that.ytReady) {
1417
- that.$video.trigger('play');
1418
- that.ytPlayer.playVideo();
1419
- }
1420
- }
1421
- else {
1422
- that.video.play();
1423
- }
1424
-
1425
- return that;
1426
- };
1427
-
1428
- VideoWrapper.prototype.pause = function () {
1429
- var that = this;
1430
-
1431
- that.__manuallyStopped = false;
1432
-
1433
- if (that.type === 'youtube') {
1434
- if (that.ytReady) {
1435
- that.ytPlayer.pauseVideo();
1436
- }
1437
- }
1438
- else {
1439
- that.video.pause();
1440
- }
1441
-
1442
- return that;
1443
- };
1444
-
1445
- VideoWrapper.prototype.stop = function () {
1446
- var that = this;
1447
-
1448
- that.__manuallyStopped = true;
1449
-
1450
- if (that.type === 'youtube') {
1451
- if (that.ytReady) {
1452
- that.ytPlayer.pauseVideo();
1453
- that.ytPlayer.seekTo(0);
1454
- }
1455
- }
1456
- else {
1457
- that.video.pause();
1458
- that.video.currentTime = 0;
1459
- }
1460
-
1461
- return that;
1462
- };
1463
-
1464
- VideoWrapper.prototype.destroy = function () {
1465
- var that = this;
1466
-
1467
- if (that.ytPlayer) {
1468
- that.ytPlayer.destroy();
1469
- }
1470
-
1471
- that.$video.remove();
1472
-
1473
- return that;
1474
- };
1475
-
1476
- VideoWrapper.prototype.getCurrentTime = function (seconds) {
1477
- var that = this;
1478
-
1479
- if (that.type === 'youtube') {
1480
- if (that.ytReady) {
1481
- return that.ytPlayer.getCurrentTime();
1482
- }
1483
- }
1484
- else {
1485
- return that.video.currentTime;
1486
- }
1487
-
1488
- return 0;
1489
- };
1490
-
1491
- VideoWrapper.prototype.setCurrentTime = function (seconds) {
1492
- var that = this;
1493
-
1494
- if (that.type === 'youtube') {
1495
- if (that.ytReady) {
1496
- that.ytPlayer.seekTo(seconds, true);
1497
- }
1498
- }
1499
- else {
1500
- that.video.currentTime = seconds;
1501
- }
1502
-
1503
- return that;
1504
- };
1505
-
1506
- VideoWrapper.prototype.getDuration = function () {
1507
- var that = this;
1508
-
1509
- if (that.type === 'youtube') {
1510
- if (that.ytReady) {
1511
- return that.ytPlayer.getDuration();
1512
- }
1513
- }
1514
- else {
1515
- return that.video.duration;
1516
- }
1517
-
1518
- return 0;
1519
- };
1520
-
1521
- /**
1522
- * This will load the youtube API (if not loaded yet)
1523
- * Use $(window).one('youtube_api_load', ...) to listen for API loaded event
1524
- */
1525
- VideoWrapper.loadYoutubeAPI = function () {
1526
- if (window['YT'] && window['__yt_load_event_interval__']) {
1527
- return;
1528
- }
1529
-
1530
- if (!window['YT'] && !$('script[src*=www\\.youtube\\.com\\/iframe_api]').length) {
1531
- $('<script type="text/javascript" src="https://www.youtube.com/iframe_api">').appendTo('body');
1532
- }
1533
-
1534
- window['__yt_load_event_interval__'] = setInterval(function () {
1535
- if (window['YT'] && window['YT'].loaded) {
1536
- $(window).trigger('youtube_api_load');
1537
- clearTimeout(window['__yt_load_event_interval__']);
1538
- delete window['__yt_load_event_interval__'];
1539
- }
1540
- }, 50);
1541
- };
1542
-
1543
- var getDeviceOrientation = function () {
1544
-
1545
- if ('matchMedia' in window) {
1546
- if (window.matchMedia("(orientation: portrait)").matches) {
1547
- return 'portrait';
1548
- }
1549
- else if (window.matchMedia("(orientation: landscape)").matches) {
1550
- return 'landscape';
1551
- }
1552
- }
1553
-
1554
- if (screen.height > screen.width) {
1555
- return 'portrait';
1556
- }
1557
-
1558
- // Even square devices have orientation,
1559
- // but a desktop browser may be too old for `matchMedia`.
1560
- // Defaulting to `landscape` for the VERY rare case of a square desktop screen is good enough.
1561
- return 'landscape';
1562
- };
1563
-
1564
- var getWindowOrientation = function () {
1565
- if (window.innerHeight > window.innerWidth) {
1566
- return 'portrait';
1567
- }
1568
- if (window.innerWidth > window.innerHeight) {
1569
- return 'landscape';
1570
- }
1571
-
1572
- return 'square';
1573
- };
1574
-
1575
- /* SUPPORTS FIXED POSITION?
1576
- *
1577
- * Based on code from jQuery Mobile 1.1.0
1578
- * http://jquerymobile.com/
1579
- *
1580
- * In a nutshell, we need to figure out if fixed positioning is supported.
1581
- * Unfortunately, this is very difficult to do on iOS, and usually involves
1582
- * injecting content, scrolling the page, etc.. It's ugly.
1583
- * jQuery Mobile uses this workaround. It's not ideal, but works.
1584
- *
1585
- * Modified to detect IE6
1586
- * ========================= */
1587
-
1588
- var supportsFixedPosition = (function () {
1589
- var ua = navigator.userAgent
1590
- , platform = navigator.platform
1591
- // Rendering engine is Webkit, and capture major version
1592
- , wkmatch = ua.match(/AppleWebKit\/([0-9]+)/)
1593
- , wkversion = !!wkmatch && wkmatch[1]
1594
- , ffmatch = ua.match(/Fennec\/([0-9]+)/)
1595
- , ffversion = !!ffmatch && ffmatch[1]
1596
- , operammobilematch = ua.match(/Opera Mobi\/([0-9]+)/)
1597
- , omversion = !!operammobilematch && operammobilematch[1]
1598
- , iematch = ua.match(/MSIE ([0-9]+)/)
1599
- , ieversion = !!iematch && iematch[1];
1600
-
1601
- return !(
1602
- // iOS 4.3 and older : Platform is iPhone/Pad/Touch and Webkit version is less than 534 (ios5)
1603
- ((platform.indexOf("iPhone") > -1 || platform.indexOf("iPad") > -1 || platform.indexOf(
1604
- "iPod") > -1) && wkversion && wkversion < 534) ||
1605
-
1606
- // Opera Mini
1607
- (window.operamini && ({}).toString.call(window.operamini) === "[object OperaMini]") ||
1608
- (operammobilematch && omversion < 7458) ||
1609
-
1610
- //Android lte 2.1: Platform is Android and Webkit version is less than 533 (Android 2.2)
1611
- (ua.indexOf("Android") > -1 && wkversion && wkversion < 533) ||
1612
-
1613
- // Firefox Mobile before 6.0 -
1614
- (ffversion && ffversion < 6) ||
1615
-
1616
- // WebOS less than 3
1617
- ("palmGetResource" in window && wkversion && wkversion < 534) ||
1618
-
1619
- // MeeGo
1620
- (ua.indexOf("MeeGo") > -1 && ua.indexOf("NokiaBrowser/8.5.0") > -1) ||
1621
-
1622
- // IE6
1623
- (ieversion && ieversion <= 6)
1624
- );
1625
- }());
1626
-
1627
- }(jQuery, window));