diff --git a/frappe/public/build.json b/frappe/public/build.json index 750b02447c..447019a6cd 100644 --- a/frappe/public/build.json +++ b/frappe/public/build.json @@ -193,7 +193,10 @@ "public/css/list.css", "public/css/tag-it.css", "public/css/calendar.css", - "public/css/gantt.css" + "public/css/gantt.css", + + "public/js/lib/gallery/css/blueimp-gallery.css", + "public/js/lib/gallery/css/blueimp-gallery-indicator.css" ], "js/list.min.js": [ "public/js/frappe/ui/listing.html", @@ -219,6 +222,9 @@ "public/js/frappe/views/calendar_base.js", "public/js/frappe/views/calendar.js", "public/js/frappe/views/ganttview.js", + + "public/js/frappe/list/imageview.js", + "public/js/frappe/list/blueimp-gallery.html", "public/js/frappe/list/image_view_item_row.html", "public/js/frappe/list/image_view_item_main_head.html" ], diff --git a/frappe/public/js/frappe/list/blueimp-gallery.html b/frappe/public/js/frappe/list/blueimp-gallery.html new file mode 100644 index 0000000000..9503e2cce9 --- /dev/null +++ b/frappe/public/js/frappe/list/blueimp-gallery.html @@ -0,0 +1,9 @@ +
\ No newline at end of file diff --git a/frappe/public/js/frappe/list/imageview.js b/frappe/public/js/frappe/list/imageview.js new file mode 100644 index 0000000000..fb2fffeee3 --- /dev/null +++ b/frappe/public/js/frappe/list/imageview.js @@ -0,0 +1,59 @@ +frappe.views.ImageView = Class.extend({ + init: function(opts){ + this.doctype = opts.doctype; + this.docname = opts.docname; + this.container = opts.container; + + this.get_images(this.doctype, this.docname); + }, + + get_images: function(doctype, docname){ + /* get the list of all the Images associated with doc */ + var me = this; + + frappe.call({ + method: "frappe.client.get_list", + args: { + doctype: "File", + fields: [ + "file_name as title", "file_url as href", + "'image/*' as type", "ifnull(thumbnail_url, '') as thumbnail" + ], + filters: [ + ["File", "attached_to_doctype", "=", this.doctype], + ["File", "attached_to_name", "=", this.docname], + ["File", "is_folder", "!=", 1] + ] + }, + freeze: true, + freeze_message: "Fetching Images ..", + callback: function(r){ + if(!r.message){ + msgprint("No Images found") + } else{ + me.render(r.message); + } + } + }); + }, + + render: function(image_list){ + var me = this; + + frappe.require(["assets/frappe/js/lib/gallery/js/blueimp-gallery.js", + "assets/frappe/js/lib/gallery/js/blueimp-gallery-indicator.js"], function(){ + var gallery = blueimp.Gallery(image_list, me.get_options()); + }) + }, + + get_options: function(){ + /* options for the gallery plugin */ + return { + indicatorContainer: 'ol', + thumbnailIndicators: true, + thumbnailProperty: 'thumbnail', + activeIndicatorClass: 'active', + container: this.container.find(".blueimp-gallery") + } + } +}); \ No newline at end of file diff --git a/frappe/public/js/frappe/ui/listing.js b/frappe/public/js/frappe/ui/listing.js index d895130d47..796ef69447 100644 --- a/frappe/public/js/frappe/ui/listing.js +++ b/frappe/public/js/frappe/ui/listing.js @@ -315,11 +315,28 @@ frappe.ui.Listing = Class.extend({ } } else{ - while (values.length) { - row = this.add_row(values[0]); + var cols = values.slice(); + while (cols.length) { + row = this.add_row(cols[0]); $("").appendTo(row) - this.render_image_view_row(row, values.splice(0, 4), this, i); + this.render_image_view_row(row, cols.splice(0, 4), this, i); } + + // remove previous gallery container + this.$w.find(".blueimp-gallery").remove(); + // append gallery div + var gallery = frappe.render_template("blueimp-gallery", {}); + $(gallery).appendTo(this.$w); + + var me = this + this.$w.find(".image-field").click(function(event){ + opts = { + doctype: me.doctype, + docname: $(event.target).attr('data-name'), + container: me.$w + }; + new frappe.views.ImageView(opts); + }) } }, update_paging: function(values) { diff --git a/frappe/public/js/lib/gallery/css/blueimp-gallery-indicator.css b/frappe/public/js/lib/gallery/css/blueimp-gallery-indicator.css new file mode 100644 index 0000000000..9bf18b91a5 --- /dev/null +++ b/frappe/public/js/lib/gallery/css/blueimp-gallery-indicator.css @@ -0,0 +1,72 @@ +@charset "UTF-8"; +/* + * blueimp Gallery Indicator CSS + * https://github.com/blueimp/Gallery + * + * Copyright 2013, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +.blueimp-gallery > .indicator { + position: absolute; + top: auto; + right: 15px; + bottom: 15px; + left: 15px; + margin: 0 40px; + padding: 0; + list-style: none; + text-align: center; + line-height: 10px; + display: none; +} +.blueimp-gallery > .indicator > li { + display: inline-block; + width: 40px; + height: 40px; + margin: 6px 3px 0 3px; + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + border: 1px solid transparent; + background: #ccc; + background: rgba(255, 255, 255, 0.25) center no-repeat; + border-radius: 5px; + box-shadow: 0 0 2px #000; + opacity: 0.5; + cursor: pointer; + background-size: 40px 40px; +} +.blueimp-gallery > .indicator > li:hover, +.blueimp-gallery > .indicator > .active { + background-color: #fff; + border-color: #fff; + opacity: 1; +} +.blueimp-gallery-controls > .indicator { + display: block; + /* Fix z-index issues (controls behind slide element) on Android: */ + -webkit-transform: translateZ(0); + -moz-transform: translateZ(0); + -ms-transform: translateZ(0); + -o-transform: translateZ(0); + transform: translateZ(0); +} +.blueimp-gallery-single > .indicator { + display: none; +} +.blueimp-gallery > .indicator { + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* IE7 fixes */ +*+html .blueimp-gallery > .indicator > li { + display: inline; +} diff --git a/frappe/public/js/lib/gallery/css/blueimp-gallery-video.css b/frappe/public/js/lib/gallery/css/blueimp-gallery-video.css new file mode 100644 index 0000000000..28040902b7 --- /dev/null +++ b/frappe/public/js/lib/gallery/css/blueimp-gallery-video.css @@ -0,0 +1,87 @@ +@charset "UTF-8"; +/* + * blueimp Gallery Video Factory CSS + * https://github.com/blueimp/Gallery + * + * Copyright 2013, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +.blueimp-gallery > .slides > .slide > .video-content > img { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + margin: auto; + width: auto; + height: auto; + max-width: 100%; + max-height: 100%; + /* Prevent artifacts in Mozilla Firefox: */ + -moz-backface-visibility: hidden; +} +.blueimp-gallery > .slides > .slide > .video-content > video { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; +} +.blueimp-gallery > .slides > .slide > .video-content > iframe { + position: absolute; + top: 100%; + left: 0; + width: 100%; + height: 100%; + border: none; +} +.blueimp-gallery > .slides > .slide > .video-playing > iframe { + top: 0; +} +.blueimp-gallery > .slides > .slide > .video-content > a { + position: absolute; + top: 50%; + right: 0; + left: 0; + margin: -64px auto 0; + width: 128px; + height: 128px; + background: url(../img/video-play.png) center no-repeat; + opacity: 0.8; + cursor: pointer; +} +.blueimp-gallery > .slides > .slide > .video-content > a:hover { + opacity: 1; +} +.blueimp-gallery > .slides > .slide > .video-playing > a, +.blueimp-gallery > .slides > .slide > .video-playing > img { + display: none; +} +.blueimp-gallery > .slides > .slide > .video-content > video { + display: none; +} +.blueimp-gallery > .slides > .slide > .video-playing > video { + display: block; +} +.blueimp-gallery > .slides > .slide > .video-loading > a { + background: url(../img/loading.gif) center no-repeat; + background-size: 64px 64px; +} + +/* Replace PNGs with SVGs for capable browsers (excluding IE<9) */ +body:last-child .blueimp-gallery > .slides > .slide > .video-content:not(.video-loading) > a { + background-image: url(../img/video-play.svg); +} + +/* IE7 fixes */ +*+html .blueimp-gallery > .slides > .slide > .video-content { + height: 100%; +} +*+html .blueimp-gallery > .slides > .slide > .video-content > a { + left: 50%; + margin-left: -64px; +} diff --git a/frappe/public/js/lib/gallery/css/blueimp-gallery.css b/frappe/public/js/lib/gallery/css/blueimp-gallery.css new file mode 100644 index 0000000000..b47ea200b4 --- /dev/null +++ b/frappe/public/js/lib/gallery/css/blueimp-gallery.css @@ -0,0 +1,226 @@ +@charset "UTF-8"; +/* + * blueimp Gallery CSS + * https://github.com/blueimp/Gallery + * + * Copyright 2013, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +.blueimp-gallery, +.blueimp-gallery > .slides > .slide > .slide-content { + position: absolute; + top: 0; + right: 0; + bottom: 0; + left: 0; + /* Prevent artifacts in Mozilla Firefox: */ + -moz-backface-visibility: hidden; +} +.blueimp-gallery > .slides > .slide > .slide-content { + margin: auto; + width: auto; + height: auto; + max-width: 100%; + max-height: 100%; + opacity: 1; +} +.blueimp-gallery { + position: fixed; + z-index: 999999; + overflow: hidden; + background: #000; + background: rgba(0, 0, 0, 0.9); + opacity: 0; + display: none; + direction: ltr; + -ms-touch-action: none; + touch-action: none; +} +.blueimp-gallery-carousel { + position: relative; + z-index: auto; + margin: 1em auto; + /* Set the carousel width/height ratio to 16/9: */ + padding-bottom: 56.25%; + box-shadow: 0 0 10px #000; + -ms-touch-action: pan-y; + touch-action: pan-y; +} +.blueimp-gallery-display { + display: block; + opacity: 1; +} +.blueimp-gallery > .slides { + position: relative; + height: 100%; + overflow: hidden; +} +.blueimp-gallery-carousel > .slides { + position: absolute; +} +.blueimp-gallery > .slides > .slide { + position: relative; + float: left; + height: 100%; + text-align: center; + -webkit-transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1.000); + -moz-transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1.000); + -ms-transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1.000); + -o-transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1.000); + transition-timing-function: cubic-bezier(0.645, 0.045, 0.355, 1.000); +} +.blueimp-gallery, +.blueimp-gallery > .slides > .slide > .slide-content { + -webkit-transition: opacity 0.2s linear; + -moz-transition: opacity 0.2s linear; + -ms-transition: opacity 0.2s linear; + -o-transition: opacity 0.2s linear; + transition: opacity 0.2s linear; +} +.blueimp-gallery > .slides > .slide-loading { + background: url(../img/loading.gif) center no-repeat; + background-size: 64px 64px; +} +.blueimp-gallery > .slides > .slide-loading > .slide-content { + opacity: 0; +} +.blueimp-gallery > .slides > .slide-error { + background: url(../img/error.png) center no-repeat; +} +.blueimp-gallery > .slides > .slide-error > .slide-content { + display: none; +} +.blueimp-gallery > .prev, +.blueimp-gallery > .next { + position: absolute; + top: 50%; + left: 15px; + width: 40px; + height: 40px; + margin-top: -23px; + font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; + font-size: 60px; + font-weight: 100; + line-height: 30px; + color: #fff; + text-decoration: none; + text-shadow: 0 0 2px #000; + text-align: center; + background: #222; + background: rgba(0, 0, 0, 0.5); + -webkit-box-sizing: content-box; + -moz-box-sizing: content-box; + box-sizing: content-box; + border: 3px solid #fff; + -webkit-border-radius: 23px; + -moz-border-radius: 23px; + border-radius: 23px; + opacity: 0.5; + cursor: pointer; + display: none; +} +.blueimp-gallery > .next { + left: auto; + right: 15px; +} +.blueimp-gallery > .close, +.blueimp-gallery > .title { + position: absolute; + top: 15px; + left: 15px; + margin: 0 40px 0 0; + font-size: 20px; + line-height: 30px; + color: #fff; + text-shadow: 0 0 2px #000; + opacity: 0.8; + display: none; +} +.blueimp-gallery > .close { + padding: 15px; + right: 15px; + left: auto; + margin: -15px; + font-size: 30px; + text-decoration: none; + cursor: pointer; +} +.blueimp-gallery > .play-pause { + position: absolute; + right: 15px; + bottom: 15px; + width: 15px; + height: 15px; + background: url(../img/play-pause.png) 0 0 no-repeat; + cursor: pointer; + opacity: 0.5; + display: none; +} +.blueimp-gallery-playing > .play-pause { + background-position: -15px 0; +} +.blueimp-gallery > .prev:hover, +.blueimp-gallery > .next:hover, +.blueimp-gallery > .close:hover, +.blueimp-gallery > .title:hover, +.blueimp-gallery > .play-pause:hover { + color: #fff; + opacity: 1; +} +.blueimp-gallery-controls > .prev, +.blueimp-gallery-controls > .next, +.blueimp-gallery-controls > .close, +.blueimp-gallery-controls > .title, +.blueimp-gallery-controls > .play-pause { + display: block; + /* Fix z-index issues (controls behind slide element) on Android: */ + -webkit-transform: translateZ(0); + -moz-transform: translateZ(0); + -ms-transform: translateZ(0); + -o-transform: translateZ(0); + transform: translateZ(0); +} +.blueimp-gallery-single > .prev, +.blueimp-gallery-left > .prev, +.blueimp-gallery-single > .next, +.blueimp-gallery-right > .next, +.blueimp-gallery-single > .play-pause { + display: none; +} +.blueimp-gallery > .slides > .slide > .slide-content, +.blueimp-gallery > .prev, +.blueimp-gallery > .next, +.blueimp-gallery > .close, +.blueimp-gallery > .play-pause { + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} + +/* Replace PNGs with SVGs for capable browsers (excluding IE<9) */ +body:last-child .blueimp-gallery > .slides > .slide-error { + background-image: url(../img/error.svg); +} +body:last-child .blueimp-gallery > .play-pause { + width: 20px; + height: 20px; + background-size: 40px 20px; + background-image: url(../img/play-pause.svg); +} +body:last-child .blueimp-gallery-playing > .play-pause { + background-position: -20px 0; +} + +/* IE7 fixes */ +*+html .blueimp-gallery > .slides > .slide { + min-height: 300px; +} +*+html .blueimp-gallery > .slides > .slide > .slide-content { + position: relative; +} diff --git a/frappe/public/js/lib/gallery/css/blueimp-gallery.less b/frappe/public/js/lib/gallery/css/blueimp-gallery.less new file mode 100644 index 0000000000..dc5ba44a47 --- /dev/null +++ b/frappe/public/js/lib/gallery/css/blueimp-gallery.less @@ -0,0 +1,3 @@ +@import (inline) 'blueimp-gallery.css'; +@import (inline) 'blueimp-gallery-indicator.css'; +@import (inline) 'blueimp-gallery-video.css'; diff --git a/frappe/public/js/lib/gallery/css/blueimp-gallery.min.css b/frappe/public/js/lib/gallery/css/blueimp-gallery.min.css new file mode 100644 index 0000000000..42a66d53ee --- /dev/null +++ b/frappe/public/js/lib/gallery/css/blueimp-gallery.min.css @@ -0,0 +1 @@ +@charset "UTF-8";.blueimp-gallery,.blueimp-gallery>.slides>.slide>.slide-content{position:absolute;top:0;right:0;bottom:0;left:0;-moz-backface-visibility:hidden}.blueimp-gallery>.slides>.slide>.slide-content{margin:auto;width:auto;height:auto;max-width:100%;max-height:100%;opacity:1}.blueimp-gallery{position:fixed;z-index:999999;overflow:hidden;background:#000;background:rgba(0,0,0,.9);opacity:0;display:none;direction:ltr;-ms-touch-action:none;touch-action:none}.blueimp-gallery-carousel{position:relative;z-index:auto;margin:1em auto;padding-bottom:56.25%;box-shadow:0 0 10px #000;-ms-touch-action:pan-y;touch-action:pan-y}.blueimp-gallery-display{display:block;opacity:1}.blueimp-gallery>.slides{position:relative;height:100%;overflow:hidden}.blueimp-gallery-carousel>.slides{position:absolute}.blueimp-gallery>.slides>.slide{position:relative;float:left;height:100%;text-align:center;-webkit-transition-timing-function:cubic-bezier(.645,.045,.355,1);-moz-transition-timing-function:cubic-bezier(.645,.045,.355,1);-ms-transition-timing-function:cubic-bezier(.645,.045,.355,1);-o-transition-timing-function:cubic-bezier(.645,.045,.355,1);transition-timing-function:cubic-bezier(.645,.045,.355,1)}.blueimp-gallery,.blueimp-gallery>.slides>.slide>.slide-content{-webkit-transition:opacity .2s linear;-moz-transition:opacity .2s linear;-ms-transition:opacity .2s linear;-o-transition:opacity .2s linear;transition:opacity .2s linear}.blueimp-gallery>.slides>.slide-loading{background:url(../img/loading.gif) center no-repeat;background-size:64px 64px}.blueimp-gallery>.slides>.slide-loading>.slide-content{opacity:0}.blueimp-gallery>.slides>.slide-error{background:url(../img/error.png) center no-repeat}.blueimp-gallery>.slides>.slide-error>.slide-content{display:none}.blueimp-gallery>.next,.blueimp-gallery>.prev{position:absolute;top:50%;left:15px;width:40px;height:40px;margin-top:-23px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-decoration:none;text-shadow:0 0 2px #000;text-align:center;background:#222;background:rgba(0,0,0,.5);-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;cursor:pointer;display:none}.blueimp-gallery>.next{left:auto;right:15px}.blueimp-gallery>.close,.blueimp-gallery>.title{position:absolute;top:15px;left:15px;margin:0 40px 0 0;font-size:20px;line-height:30px;color:#fff;text-shadow:0 0 2px #000;opacity:.8;display:none}.blueimp-gallery>.close{padding:15px;right:15px;left:auto;margin:-15px;font-size:30px;text-decoration:none;cursor:pointer}.blueimp-gallery>.play-pause{position:absolute;right:15px;bottom:15px;width:15px;height:15px;background:url(../img/play-pause.png) 0 0 no-repeat;cursor:pointer;opacity:.5;display:none}.blueimp-gallery-playing>.play-pause{background-position:-15px 0}.blueimp-gallery>.close:hover,.blueimp-gallery>.next:hover,.blueimp-gallery>.play-pause:hover,.blueimp-gallery>.prev:hover,.blueimp-gallery>.title:hover{color:#fff;opacity:1}.blueimp-gallery-controls>.close,.blueimp-gallery-controls>.next,.blueimp-gallery-controls>.play-pause,.blueimp-gallery-controls>.prev,.blueimp-gallery-controls>.title{display:block;-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);-o-transform:translateZ(0);transform:translateZ(0)}.blueimp-gallery-left>.prev,.blueimp-gallery-right>.next,.blueimp-gallery-single>.next,.blueimp-gallery-single>.play-pause,.blueimp-gallery-single>.prev{display:none}.blueimp-gallery>.close,.blueimp-gallery>.next,.blueimp-gallery>.play-pause,.blueimp-gallery>.prev,.blueimp-gallery>.slides>.slide>.slide-content{-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body:last-child .blueimp-gallery>.slides>.slide-error{background-image:url(../img/error.svg)}body:last-child .blueimp-gallery>.play-pause{width:20px;height:20px;background-size:40px 20px;background-image:url(../img/play-pause.svg)}body:last-child .blueimp-gallery-playing>.play-pause{background-position:-20px 0}.blueimp-gallery>.indicator{position:absolute;top:auto;right:15px;bottom:15px;left:15px;margin:0 40px;padding:0;list-style:none;text-align:center;line-height:10px;display:none}.blueimp-gallery>.indicator>li{display:inline-block;width:9px;height:9px;margin:6px 3px 0 3px;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;border:1px solid transparent;background:#ccc;background:rgba(255,255,255,.25) center no-repeat;border-radius:5px;box-shadow:0 0 2px #000;opacity:.5;cursor:pointer}.blueimp-gallery>.indicator>.active,.blueimp-gallery>.indicator>li:hover{background-color:#fff;border-color:#fff;opacity:1}.blueimp-gallery-controls>.indicator{display:block;-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);-o-transform:translateZ(0);transform:translateZ(0)}.blueimp-gallery-single>.indicator{display:none}.blueimp-gallery>.indicator{-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}.blueimp-gallery>.slides>.slide>.video-content>img{position:absolute;top:0;right:0;bottom:0;left:0;margin:auto;width:auto;height:auto;max-width:100%;max-height:100%;-moz-backface-visibility:hidden}.blueimp-gallery>.slides>.slide>.video-content>video{position:absolute;top:0;left:0;width:100%;height:100%}.blueimp-gallery>.slides>.slide>.video-content>iframe{position:absolute;top:100%;left:0;width:100%;height:100%;border:none}.blueimp-gallery>.slides>.slide>.video-playing>iframe{top:0}.blueimp-gallery>.slides>.slide>.video-content>a{position:absolute;top:50%;right:0;left:0;margin:-64px auto 0;width:128px;height:128px;background:url(../img/video-play.png) center no-repeat;opacity:.8;cursor:pointer}.blueimp-gallery>.slides>.slide>.video-content>a:hover{opacity:1}.blueimp-gallery>.slides>.slide>.video-playing>a,.blueimp-gallery>.slides>.slide>.video-playing>img{display:none}.blueimp-gallery>.slides>.slide>.video-content>video{display:none}.blueimp-gallery>.slides>.slide>.video-playing>video{display:block}.blueimp-gallery>.slides>.slide>.video-loading>a{background:url(../img/loading.gif) center no-repeat;background-size:64px 64px}body:last-child .blueimp-gallery>.slides>.slide>.video-content:not(.video-loading)>a{background-image:url(../img/video-play.svg)}/*# sourceMappingURL=blueimp-gallery.min.css.map */ \ No newline at end of file diff --git a/frappe/public/js/lib/gallery/css/blueimp-gallery.min.css.map b/frappe/public/js/lib/gallery/css/blueimp-gallery.min.css.map new file mode 100644 index 0000000000..f6cafce0be --- /dev/null +++ b/frappe/public/js/lib/gallery/css/blueimp-gallery.min.css.map @@ -0,0 +1 @@ +{"version":3,"sources":["blueimp-gallery.css","blueimp-gallery-indicator.css","blueimp-gallery-video.css"],"names":[],"mappings":"iBAYA,iBACA,+CACE,SAAU,SACV,IAAK,EACL,MAAO,EACP,OAAQ,EACR,KAAM,EAEN,yBAAyE,OAE3E,+CACE,OAAQ,KACR,MAAO,KACP,OAAQ,KACR,UAAW,KACX,WAAY,KACZ,QAAS,EAEX,iBACE,SAAU,MACV,QAAS,OACT,SAAU,OACV,WAAY,KACZ,WAAY,eACZ,QAAS,EACT,QAAS,KACT,UAAW,IACX,iBAAkB,KAClB,aAAc,KAEhB,0BACE,SAAU,SACV,QAAS,KACT,OAAQ,IAAI,KAEZ,eAAkH,OAClH,WAAY,EAAE,EAAE,KAAK,KACrB,iBAAkB,MAClB,aAAc,MAEhB,yBACE,QAAS,MACT,QAAS,EAEX,yBACE,SAAU,SACV,OAAQ,KACR,SAAU,OAEZ,kCACE,SAAU,SAEZ,gCACE,SAAU,SACV,MAAO,KACP,OAAQ,KACR,WAAY,OACZ,mCAAoC,+BACjC,gCAAiC,+BAChC,+BAAgC,+BAC/B,8BAA+B,+BAC5B,2BAA4B,+BAEtC,iBACA,+CACE,mBAAoB,QAAQ,IAAK,OAC9B,gBAAiB,QAAQ,IAAK,OAC7B,eAAgB,QAAQ,IAAK,OAC5B,cAAe,QAAQ,IAAK,OACzB,WAAY,QAAQ,IAAK,OAEnC,wCACE,WAAY,wBAAuB,OAAO,UAC1C,gBAAiB,KAAK,KAExB,uDACE,QAAS,EAEX,sCACE,WAAY,sBAAqB,OAAO,UAE1C,qDACE,QAAS,KAGX,uBADA,uBAEE,SAAU,SACV,IAAK,IACL,KAAM,KACN,MAAO,KACP,OAAQ,KACR,WAAY,MACZ,YAAa,iBAAkB,UAAW,MAAO,WACjD,UAAW,KACX,YAAa,IACb,YAAa,KACb,MAAO,KACP,gBAAiB,KACjB,YAAa,EAAE,EAAE,IAAI,KACrB,WAAY,OACZ,WAAY,KACZ,WAAY,eACZ,mBAAoB,YACjB,gBAAiB,YACZ,WAAY,YACpB,OAAQ,IAAI,MAAM,KAClB,sBAAuB,KACpB,mBAAoB,KACf,cAAe,KACvB,QAAS,GACT,OAAQ,QACR,QAAS,KAEX,uBACE,KAAM,KACN,MAAO,KAET,wBACA,wBACE,SAAU,SACV,IAAK,KACL,KAAM,KACN,OAAQ,EAAE,KAAK,EAAE,EACjB,UAAW,KACX,YAAa,KACb,MAAO,KACP,YAAa,EAAE,EAAE,IAAI,KACrB,QAAS,GACT,QAAS,KAEX,wBACE,QAAS,KACT,MAAO,KACP,KAAM,KACN,OAAQ,MACR,UAAW,KACX,gBAAiB,KACjB,OAAQ,QAEV,6BACE,SAAU,SACV,MAAO,KACP,OAAQ,KACR,MAAO,KACP,OAAQ,KACR,WAAY,2BAA0B,EAAE,EAAE,UAC1C,OAAQ,QACR,QAAS,GACT,QAAS,KAEX,qCACE,oBAAqB,MAAM,EAI7B,8BADA,6BAGA,mCAJA,6BAGA,8BAEE,MAAO,KACP,QAAS,EAIX,iCADA,gCAGA,sCAJA,gCAGA,iCAEE,QAAS,MAET,kBAA0L,cACvL,eAAgB,cACf,cAAe,cACd,aAAc,cACX,UAAW,cAGrB,4BAEA,6BADA,8BAEA,oCAJA,8BAKE,QAAS,KAKX,wBADA,uBAEA,6BAHA,uBADA,+CAKE,oBAAqB,KACpB,mBAAoB,KAClB,iBAAkB,KACjB,gBAAiB,KACb,YAAa,KAIvB,sDACE,iBAAkB,sBAEpB,6CACE,MAAO,KACP,OAAQ,KACR,gBAAiB,KAAK,KACtB,iBAAkB,2BAEpB,qDACE,oBAAqB,MAAM,EC5M7B,4BACE,SAAU,SACV,IAAK,KACL,MAAO,KACP,OAAQ,KACR,KAAM,KACN,OAAQ,EAAE,KACV,QAAS,EACT,WAAY,KACZ,WAAY,OACZ,YAAa,KACb,QAAS,KAEX,+BACE,QAAS,aACT,MAAO,IACP,OAAQ,IACR,OAAQ,IAAI,IAAI,EAAE,IAClB,mBAAoB,YACjB,gBAAiB,YACZ,WAAY,YACpB,OAAQ,IAAI,MAAM,YAClB,WAAY,KACZ,WAAY,sBAA0B,OAAO,UAC7C,cAAe,IACf,WAAY,EAAE,EAAE,IAAI,KACpB,QAAS,GACT,OAAQ,QAGV,oCADA,qCAEE,iBAAkB,KAClB,aAAc,KACd,QAAS,EAEX,qCACE,QAAS,MAET,kBAA2F,cACxF,eAAgB,cACf,cAAe,cACd,aAAc,cACX,UAAW,cAErB,mCACE,QAAS,KAEX,4BACE,oBAAqB,KACpB,mBAAoB,KAClB,iBAAkB,KACjB,gBAAiB,KACb,YAAa,KCpDvB,mDACE,SAAU,SACV,IAAK,EACL,MAAO,EACP,OAAQ,EACR,KAAM,EACN,OAAQ,KACR,MAAO,KACP,OAAQ,KACR,UAAW,KACX,WAAY,KAEZ,yBAAyE,OAE3E,qDACE,SAAU,SACV,IAAK,EACL,KAAM,EACN,MAAO,KACP,OAAQ,KAEV,sDACE,SAAU,SACV,IAAK,KACL,KAAM,EACN,MAAO,KACP,OAAQ,KACR,OAAQ,KAEV,sDACE,IAAK,EAEP,iDACE,SAAU,SACV,IAAK,IACL,MAAO,EACP,KAAM,EACN,OAAQ,MAAM,KAAK,EACnB,MAAO,MACP,OAAQ,MACR,WAAY,2BAA0B,OAAO,UAC7C,QAAS,GACT,OAAQ,QAEV,uDACE,QAAS,EAEX,iDACA,mDACE,QAAS,KAEX,qDACE,QAAS,KAEX,qDACE,QAAS,MAEX,iDACE,WAAY,wBAAuB,OAAO,UAC1C,gBAAiB,KAAK,KAIxB,qFACE,iBAAkB"} \ No newline at end of file diff --git a/frappe/public/js/lib/gallery/css/demo.css b/frappe/public/js/lib/gallery/css/demo.css new file mode 100644 index 0000000000..7b7b08b4c7 --- /dev/null +++ b/frappe/public/js/lib/gallery/css/demo.css @@ -0,0 +1,51 @@ +/* + * blueimp Gallery Demo CSS + * https://github.com/blueimp/Gallery + * + * Copyright 2013, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +body { + max-width: 750px; + margin: 0 auto; + padding: 1em; + font-family: 'Lucida Grande', 'Lucida Sans Unicode', Arial, sans-serif; + font-size: 1em; + line-height: 1.4em; + background: #222; + color: #fff; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} +a { + color: orange; + text-decoration: none; +} +img { + border: 0; + vertical-align: middle; +} +h1 { + line-height: 1em; +} +h2, +.links { + text-align: center; +} + +@media (min-width: 481px) { + .navigation { + list-style: none; + padding: 0; + } + .navigation li { + display: inline-block; + } + .navigation li:not(:first-child):before { + content: '| '; + } +} diff --git a/frappe/public/js/lib/gallery/img/error.png b/frappe/public/js/lib/gallery/img/error.png new file mode 100644 index 0000000000..a5577c33ab Binary files /dev/null and b/frappe/public/js/lib/gallery/img/error.png differ diff --git a/frappe/public/js/lib/gallery/img/error.svg b/frappe/public/js/lib/gallery/img/error.svg new file mode 100644 index 0000000000..184206a144 --- /dev/null +++ b/frappe/public/js/lib/gallery/img/error.svg @@ -0,0 +1,5 @@ + + diff --git a/frappe/public/js/lib/gallery/img/loading.gif b/frappe/public/js/lib/gallery/img/loading.gif new file mode 100644 index 0000000000..90f28cbdbb Binary files /dev/null and b/frappe/public/js/lib/gallery/img/loading.gif differ diff --git a/frappe/public/js/lib/gallery/img/play-pause.png b/frappe/public/js/lib/gallery/img/play-pause.png new file mode 100644 index 0000000000..ece6cfb9b7 Binary files /dev/null and b/frappe/public/js/lib/gallery/img/play-pause.png differ diff --git a/frappe/public/js/lib/gallery/img/play-pause.svg b/frappe/public/js/lib/gallery/img/play-pause.svg new file mode 100644 index 0000000000..a7f1f50cd3 --- /dev/null +++ b/frappe/public/js/lib/gallery/img/play-pause.svg @@ -0,0 +1,6 @@ + + diff --git a/frappe/public/js/lib/gallery/img/video-play.png b/frappe/public/js/lib/gallery/img/video-play.png new file mode 100644 index 0000000000..353e3a592d Binary files /dev/null and b/frappe/public/js/lib/gallery/img/video-play.png differ diff --git a/frappe/public/js/lib/gallery/img/video-play.svg b/frappe/public/js/lib/gallery/img/video-play.svg new file mode 100644 index 0000000000..b5ea206dde --- /dev/null +++ b/frappe/public/js/lib/gallery/img/video-play.svg @@ -0,0 +1,5 @@ + + diff --git a/frappe/public/js/lib/gallery/js/blueimp-gallery-fullscreen.js b/frappe/public/js/lib/gallery/js/blueimp-gallery-fullscreen.js new file mode 100644 index 0000000000..ee402578d4 --- /dev/null +++ b/frappe/public/js/lib/gallery/js/blueimp-gallery-fullscreen.js @@ -0,0 +1,89 @@ +/* + * blueimp Gallery Fullscreen JS + * https://github.com/blueimp/Gallery + * + * Copyright 2013, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +/* global define, window, document */ + +;(function (factory) { + 'use strict' + if (typeof define === 'function' && define.amd) { + // Register as an anonymous AMD module: + define([ + './blueimp-helper', + './blueimp-gallery' + ], factory) + } else { + // Browser globals: + factory( + window.blueimp.helper || window.jQuery, + window.blueimp.Gallery + ) + } +}(function ($, Gallery) { + 'use strict' + + $.extend(Gallery.prototype.options, { + // Defines if the gallery should open in fullscreen mode: + fullScreen: false + }) + + var initialize = Gallery.prototype.initialize + var close = Gallery.prototype.close + + $.extend(Gallery.prototype, { + getFullScreenElement: function () { + return document.fullscreenElement || + document.webkitFullscreenElement || + document.mozFullScreenElement || + document.msFullscreenElement + }, + + requestFullScreen: function (element) { + if (element.requestFullscreen) { + element.requestFullscreen() + } else if (element.webkitRequestFullscreen) { + element.webkitRequestFullscreen() + } else if (element.mozRequestFullScreen) { + element.mozRequestFullScreen() + } else if (element.msRequestFullscreen) { + element.msRequestFullscreen() + } + }, + + exitFullScreen: function () { + if (document.exitFullscreen) { + document.exitFullscreen() + } else if (document.webkitCancelFullScreen) { + document.webkitCancelFullScreen() + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen() + } else if (document.msExitFullscreen) { + document.msExitFullscreen() + } + }, + + initialize: function () { + initialize.call(this) + if (this.options.fullScreen && !this.getFullScreenElement()) { + this.requestFullScreen(this.container[0]) + } + }, + + close: function () { + if (this.getFullScreenElement() === this.container[0]) { + this.exitFullScreen() + } + close.call(this) + } + + }) + + return Gallery +})) diff --git a/frappe/public/js/lib/gallery/js/blueimp-gallery-indicator.js b/frappe/public/js/lib/gallery/js/blueimp-gallery-indicator.js new file mode 100644 index 0000000000..0470f6326a --- /dev/null +++ b/frappe/public/js/lib/gallery/js/blueimp-gallery-indicator.js @@ -0,0 +1,155 @@ +/* + * blueimp Gallery Indicator JS + * https://github.com/blueimp/Gallery + * + * Copyright 2013, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +/* global define, window, document */ + +;(function (factory) { + 'use strict' + if (typeof define === 'function' && define.amd) { + // Register as an anonymous AMD module: + define([ + './blueimp-helper', + './blueimp-gallery' + ], factory) + } else { + // Browser globals: + factory( + window.blueimp.helper || window.jQuery, + window.blueimp.Gallery + ) + } +}(function ($, Gallery) { + 'use strict' + + $.extend(Gallery.prototype.options, { + // The tag name, Id, element or querySelector of the indicator container: + indicatorContainer: 'ol', + // The class for the active indicator: + activeIndicatorClass: 'active', + // The list object property (or data attribute) with the thumbnail URL, + // used as alternative to a thumbnail child element: + thumbnailProperty: 'thumbnail', + // Defines if the gallery indicators should display a thumbnail: + thumbnailIndicators: true + }) + + var initSlides = Gallery.prototype.initSlides + var addSlide = Gallery.prototype.addSlide + var resetSlides = Gallery.prototype.resetSlides + var handleClick = Gallery.prototype.handleClick + var handleSlide = Gallery.prototype.handleSlide + var handleClose = Gallery.prototype.handleClose + + $.extend(Gallery.prototype, { + createIndicator: function (obj) { + var indicator = this.indicatorPrototype.cloneNode(false) + var title = this.getItemProperty(obj, this.options.titleProperty) + var thumbnailProperty = this.options.thumbnailProperty + var thumbnailUrl + var thumbnail + if (this.options.thumbnailIndicators) { + if (thumbnailProperty) { + thumbnailUrl = this.getItemProperty(obj, thumbnailProperty) + } + if (thumbnailUrl === undefined) { + thumbnail = obj.getElementsByTagName && $(obj).find('img')[0] + if (thumbnail) { + thumbnailUrl = thumbnail.src + } + } + if (thumbnailUrl) { + indicator.style.backgroundImage = 'url("' + thumbnailUrl + '")' + } + } + if (title) { + indicator.title = title + } + return indicator + }, + + addIndicator: function (index) { + if (this.indicatorContainer.length) { + var indicator = this.createIndicator(this.list[index]) + indicator.setAttribute('data-index', index) + this.indicatorContainer[0].appendChild(indicator) + this.indicators.push(indicator) + } + }, + + setActiveIndicator: function (index) { + if (this.indicators) { + if (this.activeIndicator) { + this.activeIndicator + .removeClass(this.options.activeIndicatorClass) + } + this.activeIndicator = $(this.indicators[index]) + this.activeIndicator + .addClass(this.options.activeIndicatorClass) + } + }, + + initSlides: function (reload) { + if (!reload) { + this.indicatorContainer = this.container.find( + this.options.indicatorContainer + ) + if (this.indicatorContainer.length) { + this.indicatorPrototype = document.createElement('li') + this.indicators = this.indicatorContainer[0].children + } + } + initSlides.call(this, reload) + }, + + addSlide: function (index) { + addSlide.call(this, index) + this.addIndicator(index) + }, + + resetSlides: function () { + resetSlides.call(this) + this.indicatorContainer.empty() + this.indicators = [] + }, + + handleClick: function (event) { + var target = event.target || event.srcElement + var parent = target.parentNode + if (parent === this.indicatorContainer[0]) { + // Click on indicator element + this.preventDefault(event) + this.slide(this.getNodeIndex(target)) + } else if (parent.parentNode === this.indicatorContainer[0]) { + // Click on indicator child element + this.preventDefault(event) + this.slide(this.getNodeIndex(parent)) + } else { + return handleClick.call(this, event) + } + }, + + handleSlide: function (index) { + handleSlide.call(this, index) + this.setActiveIndicator(index) + }, + + handleClose: function () { + if (this.activeIndicator) { + this.activeIndicator + .removeClass(this.options.activeIndicatorClass) + } + handleClose.call(this) + } + + }) + + return Gallery +})) diff --git a/frappe/public/js/lib/gallery/js/blueimp-gallery-video.js b/frappe/public/js/lib/gallery/js/blueimp-gallery-video.js new file mode 100644 index 0000000000..1df05ca5c7 --- /dev/null +++ b/frappe/public/js/lib/gallery/js/blueimp-gallery-video.js @@ -0,0 +1,170 @@ +/* + * blueimp Gallery Video Factory JS + * https://github.com/blueimp/Gallery + * + * Copyright 2013, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +/* global define, window, document */ + +;(function (factory) { + 'use strict' + if (typeof define === 'function' && define.amd) { + // Register as an anonymous AMD module: + define([ + './blueimp-helper', + './blueimp-gallery' + ], factory) + } else { + // Browser globals: + factory( + window.blueimp.helper || window.jQuery, + window.blueimp.Gallery + ) + } +}(function ($, Gallery) { + 'use strict' + + $.extend(Gallery.prototype.options, { + // The class for video content elements: + videoContentClass: 'video-content', + // The class for video when it is loading: + videoLoadingClass: 'video-loading', + // The class for video when it is playing: + videoPlayingClass: 'video-playing', + // The list object property (or data attribute) for the video poster URL: + videoPosterProperty: 'poster', + // The list object property (or data attribute) for the video sources array: + videoSourcesProperty: 'sources' + }) + + var handleSlide = Gallery.prototype.handleSlide + + $.extend(Gallery.prototype, { + handleSlide: function (index) { + handleSlide.call(this, index) + if (this.playingVideo) { + this.playingVideo.pause() + } + }, + + videoFactory: function (obj, callback, videoInterface) { + var that = this + var options = this.options + var videoContainerNode = this.elementPrototype.cloneNode(false) + var videoContainer = $(videoContainerNode) + var errorArgs = [{ + type: 'error', + target: videoContainerNode + }] + var video = videoInterface || document.createElement('video') + var url = this.getItemProperty(obj, options.urlProperty) + var type = this.getItemProperty(obj, options.typeProperty) + var title = this.getItemProperty(obj, options.titleProperty) + var posterUrl = this.getItemProperty(obj, options.videoPosterProperty) + var posterImage + var sources = this.getItemProperty( + obj, + options.videoSourcesProperty + ) + var source + var playMediaControl + var isLoading + var hasControls + videoContainer.addClass(options.videoContentClass) + if (title) { + videoContainerNode.title = title + } + if (video.canPlayType) { + if (url && type && video.canPlayType(type)) { + video.src = url + } else { + while (sources && sources.length) { + source = sources.shift() + url = this.getItemProperty(source, options.urlProperty) + type = this.getItemProperty(source, options.typeProperty) + if (url && type && video.canPlayType(type)) { + video.src = url + break + } + } + } + } + if (posterUrl) { + video.poster = posterUrl + posterImage = this.imagePrototype.cloneNode(false) + $(posterImage).addClass(options.toggleClass) + posterImage.src = posterUrl + posterImage.draggable = false + videoContainerNode.appendChild(posterImage) + } + playMediaControl = document.createElement('a') + playMediaControl.setAttribute('target', '_blank') + if (!videoInterface) { + playMediaControl.setAttribute('download', title) + } + playMediaControl.href = url + if (video.src) { + video.controls = true + ;(videoInterface || $(video)) + .on('error', function () { + that.setTimeout(callback, errorArgs) + }) + .on('pause', function () { + isLoading = false + videoContainer + .removeClass(that.options.videoLoadingClass) + .removeClass(that.options.videoPlayingClass) + if (hasControls) { + that.container.addClass(that.options.controlsClass) + } + delete that.playingVideo + if (that.interval) { + that.play() + } + }) + .on('playing', function () { + isLoading = false + videoContainer + .removeClass(that.options.videoLoadingClass) + .addClass(that.options.videoPlayingClass) + if (that.container.hasClass(that.options.controlsClass)) { + hasControls = true + that.container.removeClass(that.options.controlsClass) + } else { + hasControls = false + } + }) + .on('play', function () { + window.clearTimeout(that.timeout) + isLoading = true + videoContainer.addClass(that.options.videoLoadingClass) + that.playingVideo = video + }) + $(playMediaControl).on('click', function (event) { + that.preventDefault(event) + if (isLoading) { + video.pause() + } else { + video.play() + } + }) + videoContainerNode.appendChild( + (videoInterface && videoInterface.element) || video + ) + } + videoContainerNode.appendChild(playMediaControl) + this.setTimeout(callback, [{ + type: 'load', + target: videoContainerNode + }]) + return videoContainerNode + } + }) + + return Gallery +})) diff --git a/frappe/public/js/lib/gallery/js/blueimp-gallery-vimeo.js b/frappe/public/js/lib/gallery/js/blueimp-gallery-vimeo.js new file mode 100644 index 0000000000..2045347804 --- /dev/null +++ b/frappe/public/js/lib/gallery/js/blueimp-gallery-vimeo.js @@ -0,0 +1,213 @@ +/* + * blueimp Gallery Vimeo Video Factory JS + * https://github.com/blueimp/Gallery + * + * Copyright 2013, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +/* global define, window, document, $f */ + +;(function (factory) { + 'use strict' + if (typeof define === 'function' && define.amd) { + // Register as an anonymous AMD module: + define([ + './blueimp-helper', + './blueimp-gallery-video' + ], factory) + } else { + // Browser globals: + factory( + window.blueimp.helper || window.jQuery, + window.blueimp.Gallery + ) + } +}(function ($, Gallery) { + 'use strict' + + if (!window.postMessage) { + return Gallery + } + + $.extend(Gallery.prototype.options, { + // The list object property (or data attribute) with the Vimeo video id: + vimeoVideoIdProperty: 'vimeo', + // The URL for the Vimeo video player, can be extended with custom parameters: + // https://developer.vimeo.com/player/embedding + vimeoPlayerUrl: '//player.vimeo.com/video/VIDEO_ID?api=1&player_id=PLAYER_ID', + // The prefix for the Vimeo video player ID: + vimeoPlayerIdPrefix: 'vimeo-player-', + // Require a click on the native Vimeo player for the initial playback: + vimeoClickToPlay: true + }) + + var textFactory = Gallery.prototype.textFactory || + Gallery.prototype.imageFactory + var VimeoPlayer = function (url, videoId, playerId, clickToPlay) { + this.url = url + this.videoId = videoId + this.playerId = playerId + this.clickToPlay = clickToPlay + this.element = document.createElement('div') + this.listeners = {} + } + var counter = 0 + + $.extend(VimeoPlayer.prototype, { + canPlayType: function () { + return true + }, + + on: function (type, func) { + this.listeners[type] = func + return this + }, + + loadAPI: function () { + var that = this + var apiUrl = '//f.vimeocdn.com/js/froogaloop2.min.js' + var scriptTags = document.getElementsByTagName('script') + var i = scriptTags.length + var scriptTag + var called + function callback () { + if (!called && that.playOnReady) { + that.play() + } + called = true + } + while (i) { + i -= 1 + if (scriptTags[i].src === apiUrl) { + scriptTag = scriptTags[i] + break + } + } + if (!scriptTag) { + scriptTag = document.createElement('script') + scriptTag.src = apiUrl + } + $(scriptTag).on('load', callback) + scriptTags[0].parentNode.insertBefore(scriptTag, scriptTags[0]) + // Fix for cached scripts on IE 8: + if (/loaded|complete/.test(scriptTag.readyState)) { + callback() + } + }, + + onReady: function () { + var that = this + this.ready = true + this.player.addEvent('play', function () { + that.hasPlayed = true + that.onPlaying() + }) + this.player.addEvent('pause', function () { + that.onPause() + }) + this.player.addEvent('finish', function () { + that.onPause() + }) + if (this.playOnReady) { + this.play() + } + }, + + onPlaying: function () { + if (this.playStatus < 2) { + this.listeners.playing() + this.playStatus = 2 + } + }, + + onPause: function () { + this.listeners.pause() + delete this.playStatus + }, + + insertIframe: function () { + var iframe = document.createElement('iframe') + iframe.src = this.url + .replace('VIDEO_ID', this.videoId) + .replace('PLAYER_ID', this.playerId) + iframe.id = this.playerId + this.element.parentNode.replaceChild(iframe, this.element) + this.element = iframe + }, + + play: function () { + var that = this + if (!this.playStatus) { + this.listeners.play() + this.playStatus = 1 + } + if (this.ready) { + if (!this.hasPlayed && (this.clickToPlay || (window.navigator && + /iP(hone|od|ad)/.test(window.navigator.platform)))) { + // Manually trigger the playing callback if clickToPlay + // is enabled and to workaround a limitation in iOS, + // which requires synchronous user interaction to start + // the video playback: + this.onPlaying() + } else { + this.player.api('play') + } + } else { + this.playOnReady = true + if (!window.$f) { + this.loadAPI() + } else if (!this.player) { + this.insertIframe() + this.player = $f(this.element) + this.player.addEvent('ready', function () { + that.onReady() + }) + } + } + }, + + pause: function () { + if (this.ready) { + this.player.api('pause') + } else if (this.playStatus) { + delete this.playOnReady + this.listeners.pause() + delete this.playStatus + } + } + + }) + + $.extend(Gallery.prototype, { + VimeoPlayer: VimeoPlayer, + + textFactory: function (obj, callback) { + var options = this.options + var videoId = this.getItemProperty(obj, options.vimeoVideoIdProperty) + if (videoId) { + if (this.getItemProperty(obj, options.urlProperty) === undefined) { + obj[options.urlProperty] = '//vimeo.com/' + videoId + } + counter += 1 + return this.videoFactory( + obj, + callback, + new VimeoPlayer( + options.vimeoPlayerUrl, + videoId, + options.vimeoPlayerIdPrefix + counter, + options.vimeoClickToPlay + ) + ) + } + return textFactory.call(this, obj, callback) + } + + }) + + return Gallery +})) diff --git a/frappe/public/js/lib/gallery/js/blueimp-gallery-youtube.js b/frappe/public/js/lib/gallery/js/blueimp-gallery-youtube.js new file mode 100644 index 0000000000..9d4f23e62d --- /dev/null +++ b/frappe/public/js/lib/gallery/js/blueimp-gallery-youtube.js @@ -0,0 +1,228 @@ +/* + * blueimp Gallery YouTube Video Factory JS + * https://github.com/blueimp/Gallery + * + * Copyright 2013, Sebastian Tschan + * https://blueimp.net + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +/* global define, window, document, YT */ + +;(function (factory) { + 'use strict' + if (typeof define === 'function' && define.amd) { + // Register as an anonymous AMD module: + define([ + './blueimp-helper', + './blueimp-gallery-video' + ], factory) + } else { + // Browser globals: + factory( + window.blueimp.helper || window.jQuery, + window.blueimp.Gallery + ) + } +}(function ($, Gallery) { + 'use strict' + + if (!window.postMessage) { + return Gallery + } + + $.extend(Gallery.prototype.options, { + // The list object property (or data attribute) with the YouTube video id: + youTubeVideoIdProperty: 'youtube', + // Optional object with parameters passed to the YouTube video player: + // https://developers.google.com/youtube/player_parameters + youTubePlayerVars: { + wmode: 'transparent' + }, + // Require a click on the native YouTube player for the initial playback: + youTubeClickToPlay: true + }) + + var textFactory = Gallery.prototype.textFactory || + Gallery.prototype.imageFactory + var YouTubePlayer = function (videoId, playerVars, clickToPlay) { + this.videoId = videoId + this.playerVars = playerVars + this.clickToPlay = clickToPlay + this.element = document.createElement('div') + this.listeners = {} + } + + $.extend(YouTubePlayer.prototype, { + canPlayType: function () { + return true + }, + + on: function (type, func) { + this.listeners[type] = func + return this + }, + + loadAPI: function () { + var that = this + var onYouTubeIframeAPIReady = window.onYouTubeIframeAPIReady + var apiUrl = '//www.youtube.com/iframe_api' + var scriptTags = document.getElementsByTagName('script') + var i = scriptTags.length + var scriptTag + window.onYouTubeIframeAPIReady = function () { + if (onYouTubeIframeAPIReady) { + onYouTubeIframeAPIReady.apply(this) + } + if (that.playOnReady) { + that.play() + } + } + while (i) { + i -= 1 + if (scriptTags[i].src === apiUrl) { + return + } + } + scriptTag = document.createElement('script') + scriptTag.src = apiUrl + scriptTags[0].parentNode.insertBefore(scriptTag, scriptTags[0]) + }, + + onReady: function () { + this.ready = true + if (this.playOnReady) { + this.play() + } + }, + + onPlaying: function () { + if (this.playStatus < 2) { + this.listeners.playing() + this.playStatus = 2 + } + }, + + onPause: function () { + Gallery.prototype.setTimeout.call( + this, + this.checkSeek, + null, + 2000 + ) + }, + + checkSeek: function () { + if (this.stateChange === YT.PlayerState.PAUSED || + this.stateChange === YT.PlayerState.ENDED) { + // check if current state change is actually paused + this.listeners.pause() + delete this.playStatus + } + }, + + onStateChange: function (event) { + switch (event.data) { + case YT.PlayerState.PLAYING: + this.hasPlayed = true + this.onPlaying() + break + case YT.PlayerState.PAUSED: + case YT.PlayerState.ENDED: + this.onPause() + break + } + // Save most recent state change to this.stateChange + this.stateChange = event.data + }, + + onError: function (event) { + this.listeners.error(event) + }, + + play: function () { + var that = this + if (!this.playStatus) { + this.listeners.play() + this.playStatus = 1 + } + if (this.ready) { + if (!this.hasPlayed && (this.clickToPlay || (window.navigator && + /iP(hone|od|ad)/.test(window.navigator.platform)))) { + // Manually trigger the playing callback if clickToPlay + // is enabled and to workaround a limitation in iOS, + // which requires synchronous user interaction to start + // the video playback: + this.onPlaying() + } else { + this.player.playVideo() + } + } else { + this.playOnReady = true + if (!(window.YT && YT.Player)) { + this.loadAPI() + } else if (!this.player) { + this.player = new YT.Player(this.element, { + videoId: this.videoId, + playerVars: this.playerVars, + events: { + onReady: function () { + that.onReady() + }, + onStateChange: function (event) { + that.onStateChange(event) + }, + onError: function (event) { + that.onError(event) + } + } + }) + } + } + }, + + pause: function () { + if (this.ready) { + this.player.pauseVideo() + } else if (this.playStatus) { + delete this.playOnReady + this.listeners.pause() + delete this.playStatus + } + } + + }) + + $.extend(Gallery.prototype, { + YouTubePlayer: YouTubePlayer, + + textFactory: function (obj, callback) { + var options = this.options + var videoId = this.getItemProperty(obj, options.youTubeVideoIdProperty) + if (videoId) { + if (this.getItemProperty(obj, options.urlProperty) === undefined) { + obj[options.urlProperty] = '//www.youtube.com/watch?v=' + videoId + } + if (this.getItemProperty(obj, options.videoPosterProperty) === undefined) { + obj[options.videoPosterProperty] = '//img.youtube.com/vi/' + videoId + + '/maxresdefault.jpg' + } + return this.videoFactory( + obj, + callback, + new YouTubePlayer( + videoId, + options.youTubePlayerVars, + options.youTubeClickToPlay + ) + ) + } + return textFactory.call(this, obj, callback) + } + + }) + + return Gallery +})) diff --git a/frappe/public/js/lib/gallery/js/blueimp-gallery.js b/frappe/public/js/lib/gallery/js/blueimp-gallery.js new file mode 100644 index 0000000000..d7c827d4a9 --- /dev/null +++ b/frappe/public/js/lib/gallery/js/blueimp-gallery.js @@ -0,0 +1,1389 @@ +/* + * blueimp Gallery JS + * https://github.com/blueimp/Gallery + * + * Copyright 2013, Sebastian Tschan + * https://blueimp.net + * + * Swipe implementation based on + * https://github.com/bradbirdsall/Swipe + * + * Licensed under the MIT license: + * http://www.opensource.org/licenses/MIT + */ + +/* global define, window, document, DocumentTouch */ + +;(function (factory) { + 'use strict' + if (typeof define === 'function' && define.amd) { + // Register as an anonymous AMD module: + define(['./blueimp-helper'], factory) + } else { + // Browser globals: + window.blueimp = window.blueimp || {} + window.blueimp.Gallery = factory( + window.blueimp.helper || window.jQuery + ) + } +}(function ($) { + 'use strict' + + function Gallery (list, options) { + if (document.body.style.maxHeight === undefined) { + // document.body.style.maxHeight is undefined on IE6 and lower + return null + } + if (!this || this.options !== Gallery.prototype.options) { + // Called as function instead of as constructor, + // so we simply return a new instance: + return new Gallery(list, options) + } + if (!list || !list.length) { + this.console.log( + 'blueimp Gallery: No or empty list provided as first argument.', + list + ) + return + } + this.list = list + this.num = list.length + this.initOptions(options) + this.initialize() + } + + $.extend(Gallery.prototype, { + options: { + // The Id, element or querySelector of the gallery widget: + container: '#blueimp-gallery', + // The tag name, Id, element or querySelector of the slides container: + slidesContainer: 'div', + // The tag name, Id, element or querySelector of the title element: + titleElement: 'h3', + // The class to add when the gallery is visible: + displayClass: 'blueimp-gallery-display', + // The class to add when the gallery controls are visible: + controlsClass: 'blueimp-gallery-controls', + // The class to add when the gallery only displays one element: + singleClass: 'blueimp-gallery-single', + // The class to add when the left edge has been reached: + leftEdgeClass: 'blueimp-gallery-left', + // The class to add when the right edge has been reached: + rightEdgeClass: 'blueimp-gallery-right', + // The class to add when the automatic slideshow is active: + playingClass: 'blueimp-gallery-playing', + // The class for all slides: + slideClass: 'slide', + // The slide class for loading elements: + slideLoadingClass: 'slide-loading', + // The slide class for elements that failed to load: + slideErrorClass: 'slide-error', + // The class for the content element loaded into each slide: + slideContentClass: 'slide-content', + // The class for the "toggle" control: + toggleClass: 'toggle', + // The class for the "prev" control: + prevClass: 'prev', + // The class for the "next" control: + nextClass: 'next', + // The class for the "close" control: + closeClass: 'close', + // The class for the "play-pause" toggle control: + playPauseClass: 'play-pause', + // The list object property (or data attribute) with the object type: + typeProperty: 'type', + // The list object property (or data attribute) with the object title: + titleProperty: 'title', + // The list object property (or data attribute) with the object URL: + urlProperty: 'href', + // The list object property (or data attribute) with the object srcset URL(s): + srcsetProperty: 'urlset', + // The gallery listens for transitionend events before triggering the + // opened and closed events, unless the following option is set to false: + displayTransition: true, + // Defines if the gallery slides are cleared from the gallery modal, + // or reused for the next gallery initialization: + clearSlides: true, + // Defines if images should be stretched to fill the available space, + // while maintaining their aspect ratio (will only be enabled for browsers + // supporting background-size="contain", which excludes IE < 9). + // Set to "cover", to make images cover all available space (requires + // support for background-size="cover", which excludes IE < 9): + stretchImages: false, + // Toggle the controls on pressing the Return key: + toggleControlsOnReturn: true, + // Toggle the controls on slide click: + toggleControlsOnSlideClick: true, + // Toggle the automatic slideshow interval on pressing the Space key: + toggleSlideshowOnSpace: true, + // Navigate the gallery by pressing left and right on the keyboard: + enableKeyboardNavigation: true, + // Close the gallery on pressing the Esc key: + closeOnEscape: true, + // Close the gallery when clicking on an empty slide area: + closeOnSlideClick: true, + // Close the gallery by swiping up or down: + closeOnSwipeUpOrDown: true, + // Emulate touch events on mouse-pointer devices such as desktop browsers: + emulateTouchEvents: true, + // Stop touch events from bubbling up to ancestor elements of the Gallery: + stopTouchEventsPropagation: false, + // Hide the page scrollbars: + hidePageScrollbars: true, + // Stops any touches on the container from scrolling the page: + disableScroll: true, + // Carousel mode (shortcut for carousel specific options): + carousel: false, + // Allow continuous navigation, moving from last to first + // and from first to last slide: + continuous: true, + // Remove elements outside of the preload range from the DOM: + unloadElements: true, + // Start with the automatic slideshow: + startSlideshow: false, + // Delay in milliseconds between slides for the automatic slideshow: + slideshowInterval: 5000, + // The starting index as integer. + // Can also be an object of the given list, + // or an equal object with the same url property: + index: 0, + // The number of elements to load around the current index: + preloadRange: 2, + // The transition speed between slide changes in milliseconds: + transitionSpeed: 400, + // The transition speed for automatic slide changes, set to an integer + // greater 0 to override the default transition speed: + slideshowTransitionSpeed: undefined, + // The event object for which the default action will be canceled + // on Gallery initialization (e.g. the click event to open the Gallery): + event: undefined, + // Callback function executed when the Gallery is initialized. + // Is called with the gallery instance as "this" object: + onopen: undefined, + // Callback function executed when the Gallery has been initialized + // and the initialization transition has been completed. + // Is called with the gallery instance as "this" object: + onopened: undefined, + // Callback function executed on slide change. + // Is called with the gallery instance as "this" object and the + // current index and slide as arguments: + onslide: undefined, + // Callback function executed after the slide change transition. + // Is called with the gallery instance as "this" object and the + // current index and slide as arguments: + onslideend: undefined, + // Callback function executed on slide content load. + // Is called with the gallery instance as "this" object and the + // slide index and slide element as arguments: + onslidecomplete: undefined, + // Callback function executed when the Gallery is about to be closed. + // Is called with the gallery instance as "this" object: + onclose: undefined, + // Callback function executed when the Gallery has been closed + // and the closing transition has been completed. + // Is called with the gallery instance as "this" object: + onclosed: undefined + }, + + carouselOptions: { + hidePageScrollbars: false, + toggleControlsOnReturn: false, + toggleSlideshowOnSpace: false, + enableKeyboardNavigation: false, + closeOnEscape: false, + closeOnSlideClick: false, + closeOnSwipeUpOrDown: false, + disableScroll: false, + startSlideshow: true + }, + + console: window.console && typeof window.console.log === 'function' + ? window.console + : {log: function () {}}, + + // Detect touch, transition, transform and background-size support: + support: (function (element) { + var support = { + touch: window.ontouchstart !== undefined || + (window.DocumentTouch && document instanceof DocumentTouch) + } + var transitions = { + webkitTransition: { + end: 'webkitTransitionEnd', + prefix: '-webkit-' + }, + MozTransition: { + end: 'transitionend', + prefix: '-moz-' + }, + OTransition: { + end: 'otransitionend', + prefix: '-o-' + }, + transition: { + end: 'transitionend', + prefix: '' + } + } + var prop + for (prop in transitions) { + if (transitions.hasOwnProperty(prop) && + element.style[prop] !== undefined) { + support.transition = transitions[prop] + support.transition.name = prop + break + } + } + function elementTests () { + var transition = support.transition + var prop + var translateZ + document.body.appendChild(element) + if (transition) { + prop = transition.name.slice(0, -9) + 'ransform' + if (element.style[prop] !== undefined) { + element.style[prop] = 'translateZ(0)' + translateZ = window.getComputedStyle(element) + .getPropertyValue(transition.prefix + 'transform') + support.transform = { + prefix: transition.prefix, + name: prop, + translate: true, + translateZ: !!translateZ && translateZ !== 'none' + } + } + } + if (element.style.backgroundSize !== undefined) { + support.backgroundSize = {} + element.style.backgroundSize = 'contain' + support.backgroundSize.contain = window + .getComputedStyle(element) + .getPropertyValue('background-size') === 'contain' + element.style.backgroundSize = 'cover' + support.backgroundSize.cover = window + .getComputedStyle(element) + .getPropertyValue('background-size') === 'cover' + } + document.body.removeChild(element) + } + if (document.body) { + elementTests() + } else { + $(document).on('DOMContentLoaded', elementTests) + } + return support + // Test element, has to be standard HTML and must not be hidden + // for the CSS3 tests using window.getComputedStyle to be applicable: + }(document.createElement('div'))), + + requestAnimationFrame: window.requestAnimationFrame || + window.webkitRequestAnimationFrame || + window.mozRequestAnimationFrame, + + initialize: function () { + this.initStartIndex() + if (this.initWidget() === false) { + return false + } + this.initEventListeners() + // Load the slide at the given index: + this.onslide(this.index) + // Manually trigger the slideend event for the initial slide: + this.ontransitionend() + // Start the automatic slideshow if applicable: + if (this.options.startSlideshow) { + this.play() + } + }, + + slide: function (to, speed) { + window.clearTimeout(this.timeout) + var index = this.index + var direction + var naturalDirection + var diff + if (index === to || this.num === 1) { + return + } + if (!speed) { + speed = this.options.transitionSpeed + } + if (this.support.transform) { + if (!this.options.continuous) { + to = this.circle(to) + } + // 1: backward, -1: forward: + direction = Math.abs(index - to) / (index - to) + // Get the actual position of the slide: + if (this.options.continuous) { + naturalDirection = direction + direction = -this.positions[this.circle(to)] / this.slideWidth + // If going forward but to < index, use to = slides.length + to + // If going backward but to > index, use to = -slides.length + to + if (direction !== naturalDirection) { + to = -direction * this.num + to + } + } + diff = Math.abs(index - to) - 1 + // Move all the slides between index and to in the right direction: + while (diff) { + diff -= 1 + this.move( + this.circle((to > index ? to : index) - diff - 1), + this.slideWidth * direction, + 0 + ) + } + to = this.circle(to) + this.move(index, this.slideWidth * direction, speed) + this.move(to, 0, speed) + if (this.options.continuous) { + this.move( + this.circle(to - direction), + -(this.slideWidth * direction), + 0 + ) + } + } else { + to = this.circle(to) + this.animate(index * -this.slideWidth, to * -this.slideWidth, speed) + } + this.onslide(to) + }, + + getIndex: function () { + return this.index + }, + + getNumber: function () { + return this.num + }, + + prev: function () { + if (this.options.continuous || this.index) { + this.slide(this.index - 1) + } + }, + + next: function () { + if (this.options.continuous || this.index < this.num - 1) { + this.slide(this.index + 1) + } + }, + + play: function (time) { + var that = this + window.clearTimeout(this.timeout) + this.interval = time || this.options.slideshowInterval + if (this.elements[this.index] > 1) { + this.timeout = this.setTimeout( + (!this.requestAnimationFrame && this.slide) || function (to, speed) { + that.animationFrameId = that.requestAnimationFrame.call( + window, + function () { + that.slide(to, speed) + } + ) + }, + [this.index + 1, this.options.slideshowTransitionSpeed], + this.interval + ) + } + this.container.addClass(this.options.playingClass) + }, + + pause: function () { + window.clearTimeout(this.timeout) + this.interval = null + this.container.removeClass(this.options.playingClass) + }, + + add: function (list) { + var i + if (!list.concat) { + // Make a real array out of the list to add: + list = Array.prototype.slice.call(list) + } + if (!this.list.concat) { + // Make a real array out of the Gallery list: + this.list = Array.prototype.slice.call(this.list) + } + this.list = this.list.concat(list) + this.num = this.list.length + if (this.num > 2 && this.options.continuous === null) { + this.options.continuous = true + this.container.removeClass(this.options.leftEdgeClass) + } + this.container + .removeClass(this.options.rightEdgeClass) + .removeClass(this.options.singleClass) + for (i = this.num - list.length; i < this.num; i += 1) { + this.addSlide(i) + this.positionSlide(i) + } + this.positions.length = this.num + this.initSlides(true) + }, + + resetSlides: function () { + this.slidesContainer.empty() + this.unloadAllSlides() + this.slides = [] + }, + + handleClose: function () { + var options = this.options + this.destroyEventListeners() + // Cancel the slideshow: + this.pause() + this.container[0].style.display = 'none' + this.container + .removeClass(options.displayClass) + .removeClass(options.singleClass) + .removeClass(options.leftEdgeClass) + .removeClass(options.rightEdgeClass) + if (options.hidePageScrollbars) { + document.body.style.overflow = this.bodyOverflowStyle + } + if (this.options.clearSlides) { + this.resetSlides() + } + if (this.options.onclosed) { + this.options.onclosed.call(this) + } + }, + + close: function () { + var that = this + function closeHandler (event) { + if (event.target === that.container[0]) { + that.container.off( + that.support.transition.end, + closeHandler + ) + that.handleClose() + } + } + if (this.options.onclose) { + this.options.onclose.call(this) + } + if (this.support.transition && this.options.displayTransition) { + this.container.on( + this.support.transition.end, + closeHandler + ) + this.container.removeClass(this.options.displayClass) + } else { + this.handleClose() + } + }, + + circle: function (index) { + // Always return a number inside of the slides index range: + return (this.num + (index % this.num)) % this.num + }, + + move: function (index, dist, speed) { + this.translateX(index, dist, speed) + this.positions[index] = dist + }, + + translate: function (index, x, y, speed) { + var style = this.slides[index].style + var transition = this.support.transition + var transform = this.support.transform + style[transition.name + 'Duration'] = speed + 'ms' + style[transform.name] = 'translate(' + x + 'px, ' + y + 'px)' + + (transform.translateZ ? ' translateZ(0)' : '') + }, + + translateX: function (index, x, speed) { + this.translate(index, x, 0, speed) + }, + + translateY: function (index, y, speed) { + this.translate(index, 0, y, speed) + }, + + animate: function (from, to, speed) { + if (!speed) { + this.slidesContainer[0].style.left = to + 'px' + return + } + var that = this + var start = new Date().getTime() + var timer = window.setInterval(function () { + var timeElap = new Date().getTime() - start + if (timeElap > speed) { + that.slidesContainer[0].style.left = to + 'px' + that.ontransitionend() + window.clearInterval(timer) + return + } + that.slidesContainer[0].style.left = (((to - from) * + (Math.floor((timeElap / speed) * 100) / 100)) + + from) + 'px' + }, 4) + }, + + preventDefault: function (event) { + if (event.preventDefault) { + event.preventDefault() + } else { + event.returnValue = false + } + }, + + stopPropagation: function (event) { + if (event.stopPropagation) { + event.stopPropagation() + } else { + event.cancelBubble = true + } + }, + + onresize: function () { + this.initSlides(true) + }, + + onmousedown: function (event) { + // Trigger on clicks of the left mouse button only + // and exclude video elements: + if (event.which && event.which === 1 && + event.target.nodeName !== 'VIDEO') { + // Preventing the default mousedown action is required + // to make touch emulation work with Firefox: + event.preventDefault() + ;(event.originalEvent || event).touches = [{ + pageX: event.pageX, + pageY: event.pageY + }] + this.ontouchstart(event) + } + }, + + onmousemove: function (event) { + if (this.touchStart) { + (event.originalEvent || event).touches = [{ + pageX: event.pageX, + pageY: event.pageY + }] + this.ontouchmove(event) + } + }, + + onmouseup: function (event) { + if (this.touchStart) { + this.ontouchend(event) + delete this.touchStart + } + }, + + onmouseout: function (event) { + if (this.touchStart) { + var target = event.target + var related = event.relatedTarget + if (!related || (related !== target && + !$.contains(target, related))) { + this.onmouseup(event) + } + } + }, + + ontouchstart: function (event) { + if (this.options.stopTouchEventsPropagation) { + this.stopPropagation(event) + } + // jQuery doesn't copy touch event properties by default, + // so we have to access the originalEvent object: + var touches = (event.originalEvent || event).touches[0] + this.touchStart = { + // Remember the initial touch coordinates: + x: touches.pageX, + y: touches.pageY, + // Store the time to determine touch duration: + time: Date.now() + } + // Helper variable to detect scroll movement: + this.isScrolling = undefined + // Reset delta values: + this.touchDelta = {} + }, + + ontouchmove: function (event) { + if (this.options.stopTouchEventsPropagation) { + this.stopPropagation(event) + } + // jQuery doesn't copy touch event properties by default, + // so we have to access the originalEvent object: + var touches = (event.originalEvent || event).touches[0] + var scale = (event.originalEvent || event).scale + var index = this.index + var touchDeltaX + var indices + // Ensure this is a one touch swipe and not, e.g. a pinch: + if (touches.length > 1 || (scale && scale !== 1)) { + return + } + if (this.options.disableScroll) { + event.preventDefault() + } + // Measure change in x and y coordinates: + this.touchDelta = { + x: touches.pageX - this.touchStart.x, + y: touches.pageY - this.touchStart.y + } + touchDeltaX = this.touchDelta.x + // Detect if this is a vertical scroll movement (run only once per touch): + if (this.isScrolling === undefined) { + this.isScrolling = this.isScrolling || + Math.abs(touchDeltaX) < Math.abs(this.touchDelta.y) + } + if (!this.isScrolling) { + // Always prevent horizontal scroll: + event.preventDefault() + // Stop the slideshow: + window.clearTimeout(this.timeout) + if (this.options.continuous) { + indices = [ + this.circle(index + 1), + index, + this.circle(index - 1) + ] + } else { + // Increase resistance if first slide and sliding left + // or last slide and sliding right: + this.touchDelta.x = touchDeltaX = + touchDeltaX / + ( + ((!index && touchDeltaX > 0) || + (index === this.num - 1 && touchDeltaX < 0)) + ? (Math.abs(touchDeltaX) / this.slideWidth + 1) + : 1 + ) + indices = [index] + if (index) { + indices.push(index - 1) + } + if (index < this.num - 1) { + indices.unshift(index + 1) + } + } + while (indices.length) { + index = indices.pop() + this.translateX(index, touchDeltaX + this.positions[index], 0) + } + } else if (this.options.closeOnSwipeUpOrDown) { + this.translateY(index, this.touchDelta.y + this.positions[index], 0) + } + }, + + ontouchend: function (event) { + if (this.options.stopTouchEventsPropagation) { + this.stopPropagation(event) + } + var index = this.index + var speed = this.options.transitionSpeed + var slideWidth = this.slideWidth + var isShortDuration = Number(Date.now() - this.touchStart.time) < 250 + // Determine if slide attempt triggers next/prev slide: + var isValidSlide = + (isShortDuration && Math.abs(this.touchDelta.x) > 20) || + Math.abs(this.touchDelta.x) > slideWidth / 2 + // Determine if slide attempt is past start or end: + var isPastBounds = (!index && this.touchDelta.x > 0) || + (index === this.num - 1 && this.touchDelta.x < 0) + var isValidClose = !isValidSlide && this.options.closeOnSwipeUpOrDown && + ((isShortDuration && Math.abs(this.touchDelta.y) > 20) || + Math.abs(this.touchDelta.y) > this.slideHeight / 2) + var direction + var indexForward + var indexBackward + var distanceForward + var distanceBackward + if (this.options.continuous) { + isPastBounds = false + } + // Determine direction of swipe (true: right, false: left): + direction = this.touchDelta.x < 0 ? -1 : 1 + if (!this.isScrolling) { + if (isValidSlide && !isPastBounds) { + indexForward = index + direction + indexBackward = index - direction + distanceForward = slideWidth * direction + distanceBackward = -slideWidth * direction + if (this.options.continuous) { + this.move(this.circle(indexForward), distanceForward, 0) + this.move(this.circle(index - 2 * direction), distanceBackward, 0) + } else if (indexForward >= 0 && + indexForward < this.num) { + this.move(indexForward, distanceForward, 0) + } + this.move(index, this.positions[index] + distanceForward, speed) + this.move( + this.circle(indexBackward), + this.positions[this.circle(indexBackward)] + distanceForward, + speed + ) + index = this.circle(indexBackward) + this.onslide(index) + } else { + // Move back into position + if (this.options.continuous) { + this.move(this.circle(index - 1), -slideWidth, speed) + this.move(index, 0, speed) + this.move(this.circle(index + 1), slideWidth, speed) + } else { + if (index) { + this.move(index - 1, -slideWidth, speed) + } + this.move(index, 0, speed) + if (index < this.num - 1) { + this.move(index + 1, slideWidth, speed) + } + } + } + } else { + if (isValidClose) { + this.close() + } else { + // Move back into position + this.translateY(index, 0, speed) + } + } + }, + + ontouchcancel: function (event) { + if (this.touchStart) { + this.ontouchend(event) + delete this.touchStart + } + }, + + ontransitionend: function (event) { + var slide = this.slides[this.index] + if (!event || slide === event.target) { + if (this.interval) { + this.play() + } + this.setTimeout( + this.options.onslideend, + [this.index, slide] + ) + } + }, + + oncomplete: function (event) { + var target = event.target || event.srcElement + var parent = target && target.parentNode + var index + if (!target || !parent) { + return + } + index = this.getNodeIndex(parent) + $(parent).removeClass(this.options.slideLoadingClass) + if (event.type === 'error') { + $(parent).addClass(this.options.slideErrorClass) + this.elements[index] = 3 // Fail + } else { + this.elements[index] = 2 // Done + } + // Fix for IE7's lack of support for percentage max-height: + if (target.clientHeight > this.container[0].clientHeight) { + target.style.maxHeight = this.container[0].clientHeight + } + if (this.interval && this.slides[this.index] === parent) { + this.play() + } + this.setTimeout( + this.options.onslidecomplete, + [index, parent] + ) + }, + + onload: function (event) { + this.oncomplete(event) + }, + + onerror: function (event) { + this.oncomplete(event) + }, + + onkeydown: function (event) { + switch (event.which || event.keyCode) { + case 13: // Return + if (this.options.toggleControlsOnReturn) { + this.preventDefault(event) + this.toggleControls() + } + break + case 27: // Esc + if (this.options.closeOnEscape) { + this.close() + // prevent Esc from closing other things + event.stopImmediatePropagation() + } + break + case 32: // Space + if (this.options.toggleSlideshowOnSpace) { + this.preventDefault(event) + this.toggleSlideshow() + } + break + case 37: // Left + if (this.options.enableKeyboardNavigation) { + this.preventDefault(event) + this.prev() + } + break + case 38: // Up + if (this.options.enableKeyboardNavigation) { + this.preventDefault(event) + console.log("UP Key") + } + break + case 39: // Right + if (this.options.enableKeyboardNavigation) { + this.preventDefault(event) + this.next() + } + break + case 40: // Down + if (this.options.enableKeyboardNavigation) { + this.preventDefault(event) + console.log("Down Key") + } + break + } + }, + + handleClick: function (event) { + var options = this.options + var target = event.target || event.srcElement + var parent = target.parentNode + function isTarget (className) { + return $(target).hasClass(className) || + $(parent).hasClass(className) + } + if (isTarget(options.toggleClass)) { + // Click on "toggle" control + this.preventDefault(event) + this.toggleControls() + } else if (isTarget(options.prevClass)) { + // Click on "prev" control + this.preventDefault(event) + this.prev() + } else if (isTarget(options.nextClass)) { + // Click on "next" control + this.preventDefault(event) + this.next() + } else if (isTarget(options.closeClass)) { + // Click on "close" control + this.preventDefault(event) + this.close() + } else if (isTarget(options.playPauseClass)) { + // Click on "play-pause" control + this.preventDefault(event) + this.toggleSlideshow() + } else if (parent === this.slidesContainer[0]) { + // Click on slide background + if (options.closeOnSlideClick) { + this.preventDefault(event) + this.close() + } else if (options.toggleControlsOnSlideClick) { + this.preventDefault(event) + this.toggleControls() + } + } else if (parent.parentNode && + parent.parentNode === this.slidesContainer[0]) { + // Click on displayed element + if (options.toggleControlsOnSlideClick) { + this.preventDefault(event) + this.toggleControls() + } + } + }, + + onclick: function (event) { + if (this.options.emulateTouchEvents && + this.touchDelta && (Math.abs(this.touchDelta.x) > 20 || + Math.abs(this.touchDelta.y) > 20)) { + delete this.touchDelta + return + } + return this.handleClick(event) + }, + + updateEdgeClasses: function (index) { + if (!index) { + this.container.addClass(this.options.leftEdgeClass) + } else { + this.container.removeClass(this.options.leftEdgeClass) + } + if (index === this.num - 1) { + this.container.addClass(this.options.rightEdgeClass) + } else { + this.container.removeClass(this.options.rightEdgeClass) + } + }, + + handleSlide: function (index) { + if (!this.options.continuous) { + this.updateEdgeClasses(index) + } + this.loadElements(index) + if (this.options.unloadElements) { + this.unloadElements(index) + } + this.setTitle(index) + }, + + onslide: function (index) { + this.index = index + this.handleSlide(index) + this.setTimeout(this.options.onslide, [index, this.slides[index]]) + }, + + setTitle: function (index) { + var text = this.slides[index].firstChild.title + var titleElement = this.titleElement + if (titleElement.length) { + this.titleElement.empty() + if (text) { + titleElement[0].appendChild(document.createTextNode(text)) + } + } + }, + + setTimeout: function (func, args, wait) { + var that = this + return func && window.setTimeout(function () { + func.apply(that, args || []) + }, wait || 0) + }, + + imageFactory: function (obj, callback) { + var that = this + var img = this.imagePrototype.cloneNode(false) + var url = obj + var backgroundSize = this.options.stretchImages + var called + var element + var title + function callbackWrapper (event) { + if (!called) { + event = { + type: event.type, + target: element + } + if (!element.parentNode) { + // Fix for IE7 firing the load event for + // cached images before the element could + // be added to the DOM: + return that.setTimeout(callbackWrapper, [event]) + } + called = true + $(img).off('load error', callbackWrapper) + if (backgroundSize) { + if (event.type === 'load') { + element.style.background = 'url("' + url + + '") center no-repeat' + element.style.backgroundSize = backgroundSize + } + } + callback(event) + } + } + if (typeof url !== 'string') { + url = this.getItemProperty(obj, this.options.urlProperty) + title = this.getItemProperty(obj, this.options.titleProperty) + } + if (backgroundSize === true) { + backgroundSize = 'contain' + } + backgroundSize = this.support.backgroundSize && + this.support.backgroundSize[backgroundSize] && backgroundSize + if (backgroundSize) { + element = this.elementPrototype.cloneNode(false) + } else { + element = img + img.draggable = false + } + if (title) { + element.title = title + } + $(img).on('load error', callbackWrapper) + img.src = url + return element + }, + + createElement: function (obj, callback) { + var type = obj && this.getItemProperty(obj, this.options.typeProperty) + var factory = (type && this[type.split('/')[0] + 'Factory']) || + this.imageFactory + var element = obj && factory.call(this, obj, callback) + var srcset = this.getItemProperty(obj, this.options.srcsetProperty) + if (!element) { + element = this.elementPrototype.cloneNode(false) + this.setTimeout(callback, [{ + type: 'error', + target: element + }]) + } + if (srcset) { + element.setAttribute('srcset', srcset) + } + $(element).addClass(this.options.slideContentClass) + return element + }, + + loadElement: function (index) { + if (!this.elements[index]) { + if (this.slides[index].firstChild) { + this.elements[index] = $(this.slides[index]) + .hasClass(this.options.slideErrorClass) ? 3 : 2 + } else { + this.elements[index] = 1 // Loading + $(this.slides[index]).addClass(this.options.slideLoadingClass) + this.slides[index].appendChild(this.createElement( + this.list[index], + this.proxyListener + )) + } + } + }, + + loadElements: function (index) { + var limit = Math.min(this.num, this.options.preloadRange * 2 + 1) + var j = index + var i + for (i = 0; i < limit; i += 1) { + // First load the current slide element (0), + // then the next one (+1), + // then the previous one (-2), + // then the next after next (+2), etc.: + j += i * (i % 2 === 0 ? -1 : 1) + // Connect the ends of the list to load slide elements for + // continuous navigation: + j = this.circle(j) + this.loadElement(j) + } + }, + + unloadElements: function (index) { + var i, + diff + for (i in this.elements) { + if (this.elements.hasOwnProperty(i)) { + diff = Math.abs(index - i) + if (diff > this.options.preloadRange && + diff + this.options.preloadRange < this.num) { + this.unloadSlide(i) + delete this.elements[i] + } + } + } + }, + + addSlide: function (index) { + var slide = this.slidePrototype.cloneNode(false) + slide.setAttribute('data-index', index) + this.slidesContainer[0].appendChild(slide) + this.slides.push(slide) + }, + + positionSlide: function (index) { + var slide = this.slides[index] + slide.style.width = this.slideWidth + 'px' + if (this.support.transform) { + slide.style.left = (index * -this.slideWidth) + 'px' + this.move( + index, this.index > index + ? -this.slideWidth + : (this.index < index ? this.slideWidth : 0), + 0 + ) + } + }, + + initSlides: function (reload) { + var clearSlides, + i + if (!reload) { + this.positions = [] + this.positions.length = this.num + this.elements = {} + this.imagePrototype = document.createElement('img') + this.elementPrototype = document.createElement('div') + this.slidePrototype = document.createElement('div') + $(this.slidePrototype).addClass(this.options.slideClass) + this.slides = this.slidesContainer[0].children + clearSlides = this.options.clearSlides || + this.slides.length !== this.num + } + this.slideWidth = this.container[0].offsetWidth + this.slideHeight = this.container[0].offsetHeight + this.slidesContainer[0].style.width = + (this.num * this.slideWidth) + 'px' + if (clearSlides) { + this.resetSlides() + } + for (i = 0; i < this.num; i += 1) { + if (clearSlides) { + this.addSlide(i) + } + this.positionSlide(i) + } + // Reposition the slides before and after the given index: + if (this.options.continuous && this.support.transform) { + this.move(this.circle(this.index - 1), -this.slideWidth, 0) + this.move(this.circle(this.index + 1), this.slideWidth, 0) + } + if (!this.support.transform) { + this.slidesContainer[0].style.left = + (this.index * -this.slideWidth) + 'px' + } + }, + + unloadSlide: function (index) { + var slide, + firstChild + slide = this.slides[index] + firstChild = slide.firstChild + if (firstChild !== null) { + slide.removeChild(firstChild) + } + }, + + unloadAllSlides: function () { + var i, + len + for (i = 0, len = this.slides.length; i < len; i++) { + this.unloadSlide(i) + } + }, + + toggleControls: function () { + var controlsClass = this.options.controlsClass + if (this.container.hasClass(controlsClass)) { + this.container.removeClass(controlsClass) + } else { + this.container.addClass(controlsClass) + } + }, + + toggleSlideshow: function () { + if (!this.interval) { + this.play() + } else { + this.pause() + } + }, + + getNodeIndex: function (element) { + return parseInt(element.getAttribute('data-index'), 10) + }, + + getNestedProperty: function (obj, property) { + property.replace( + // Matches native JavaScript notation in a String, + // e.g. '["doubleQuoteProp"].dotProp[2]' + /\[(?:'([^']+)'|"([^"]+)"|(\d+))\]|(?:(?:^|\.)([^\.\[]+))/g, + function (str, singleQuoteProp, doubleQuoteProp, arrayIndex, dotProp) { + var prop = dotProp || singleQuoteProp || doubleQuoteProp || + (arrayIndex && parseInt(arrayIndex, 10)) + if (str && obj) { + obj = obj[prop] + } + } + ) + return obj + }, + + getDataProperty: function (obj, property) { + if (obj.getAttribute) { + var prop = obj.getAttribute('data-' + + property.replace(/([A-Z])/g, '-$1').toLowerCase()) + if (typeof prop === 'string') { + if (/^(true|false|null|-?\d+(\.\d+)?|\{[\s\S]*\}|\[[\s\S]*\])$/ + .test(prop)) { + try { + return $.parseJSON(prop) + } catch (ignore) {} + } + return prop + } + } + }, + + getItemProperty: function (obj, property) { + var prop = obj[property] + if (prop === undefined) { + prop = this.getDataProperty(obj, property) + if (prop === undefined) { + prop = this.getNestedProperty(obj, property) + } + } + return prop + }, + + initStartIndex: function () { + var index = this.options.index + var urlProperty = this.options.urlProperty + var i + // Check if the index is given as a list object: + if (index && typeof index !== 'number') { + for (i = 0; i < this.num; i += 1) { + if (this.list[i] === index || + this.getItemProperty(this.list[i], urlProperty) === + this.getItemProperty(index, urlProperty)) { + index = i + break + } + } + } + // Make sure the index is in the list range: + this.index = this.circle(parseInt(index, 10) || 0) + }, + + initEventListeners: function () { + var that = this + var slidesContainer = this.slidesContainer + function proxyListener (event) { + var type = that.support.transition && + that.support.transition.end === event.type + ? 'transitionend' + : event.type + that['on' + type](event) + } + $(window).on('resize', proxyListener) + $(document.body).on('keydown', proxyListener) + this.container.on('click', proxyListener) + if (this.support.touch) { + slidesContainer + .on('touchstart touchmove touchend touchcancel', proxyListener) + } else if (this.options.emulateTouchEvents && + this.support.transition) { + slidesContainer + .on('mousedown mousemove mouseup mouseout', proxyListener) + } + if (this.support.transition) { + slidesContainer.on( + this.support.transition.end, + proxyListener + ) + } + this.proxyListener = proxyListener + }, + + destroyEventListeners: function () { + var slidesContainer = this.slidesContainer + var proxyListener = this.proxyListener + $(window).off('resize', proxyListener) + $(document.body).off('keydown', proxyListener) + this.container.off('click', proxyListener) + if (this.support.touch) { + slidesContainer + .off('touchstart touchmove touchend touchcancel', proxyListener) + } else if (this.options.emulateTouchEvents && + this.support.transition) { + slidesContainer + .off('mousedown mousemove mouseup mouseout', proxyListener) + } + if (this.support.transition) { + slidesContainer.off( + this.support.transition.end, + proxyListener + ) + } + }, + + handleOpen: function () { + if (this.options.onopened) { + this.options.onopened.call(this) + } + }, + + initWidget: function () { + var that = this + function openHandler (event) { + if (event.target === that.container[0]) { + that.container.off( + that.support.transition.end, + openHandler + ) + that.handleOpen() + } + } + this.container = $(this.options.container) + if (!this.container.length) { + this.console.log( + 'blueimp Gallery: Widget container not found.', + this.options.container + ) + return false + } + this.slidesContainer = this.container.find( + this.options.slidesContainer + ).first() + if (!this.slidesContainer.length) { + this.console.log( + 'blueimp Gallery: Slides container not found.', + this.options.slidesContainer + ) + return false + } + this.titleElement = this.container.find( + this.options.titleElement + ).first() + if (this.num === 1) { + this.container.addClass(this.options.singleClass) + } + if (this.options.onopen) { + this.options.onopen.call(this) + } + if (this.support.transition && this.options.displayTransition) { + this.container.on( + this.support.transition.end, + openHandler + ) + } else { + this.handleOpen() + } + if (this.options.hidePageScrollbars) { + // Hide the page scrollbars: + this.bodyOverflowStyle = document.body.style.overflow + document.body.style.overflow = 'hidden' + } + this.container[0].style.display = 'block' + this.initSlides() + this.container.addClass(this.options.displayClass) + }, + + initOptions: function (options) { + // Create a copy of the prototype options: + this.options = $.extend({}, this.options) + // Check if carousel mode is enabled: + if ((options && options.carousel) || + (this.options.carousel && (!options || options.carousel !== false))) { + $.extend(this.options, this.carouselOptions) + } + // Override any given options: + $.extend(this.options, options) + if (this.num < 3) { + // 1 or 2 slides cannot be displayed continuous, + // remember the original option by setting to null instead of false: + this.options.continuous = this.options.continuous ? null : false + } + if (!this.support.transition) { + this.options.emulateTouchEvents = false + } + if (this.options.event) { + this.preventDefault(this.options.event) + } + } + + }) + + return Gallery +})) diff --git a/frappe/public/js/lib/gallery/js/blueimp-gallery.min.js b/frappe/public/js/lib/gallery/js/blueimp-gallery.min.js new file mode 100644 index 0000000000..a8d76b95b7 --- /dev/null +++ b/frappe/public/js/lib/gallery/js/blueimp-gallery.min.js @@ -0,0 +1,3 @@ +!function(){"use strict";function t(t,e){var i;for(i in e)e.hasOwnProperty(i)&&(t[i]=e[i]);return t}function e(t){if(!this||this.find!==e.prototype.find)return new e(t);if(this.length=0,t)if("string"==typeof t&&(t=this.find(t)),t.nodeType||t===t.window)this.length=1,this[0]=t;else{var i=t.length;for(this.length=i;i;)i-=1,this[i]=t[i]}}e.extend=t,e.contains=function(t,e){do if(e=e.parentNode,e===t)return!0;while(e);return!1},e.parseJSON=function(t){return window.JSON&&JSON.parse(t)},t(e.prototype,{find:function(t){var i=this[0]||document;return"string"==typeof t&&(t=i.querySelectorAll?i.querySelectorAll(t):"#"===t.charAt(0)?i.getElementById(t.slice(1)):i.getElementsByTagName(t)),new e(t)},hasClass:function(t){return this[0]?new RegExp("(^|\\s+)"+t+"(\\s+|$)").test(this[0].className):!1},addClass:function(t){for(var e,i=this.length;i;){if(i-=1,e=this[i],!e.className)return e.className=t,this;if(this.hasClass(t))return this;e.className+=" "+t}return this},removeClass:function(t){for(var e,i=new RegExp("(^|\\s+)"+t+"(\\s+|$)"),s=this.length;s;)s-=1,e=this[s],e.className=e.className.replace(i," ");return this},on:function(t,e){for(var i,s,n=t.split(/\s+/);n.length;)for(t=n.shift(),i=this.length;i;)i-=1,s=this[i],s.addEventListener?s.addEventListener(t,e,!1):s.attachEvent&&s.attachEvent("on"+t,e);return this},off:function(t,e){for(var i,s,n=t.split(/\s+/);n.length;)for(t=n.shift(),i=this.length;i;)i-=1,s=this[i],s.removeEventListener?s.removeEventListener(t,e,!1):s.detachEvent&&s.detachEvent("on"+t,e);return this},empty:function(){for(var t,e=this.length;e;)for(e-=1,t=this[e];t.hasChildNodes();)t.removeChild(t.lastChild);return this},first:function(){return new e(this[0])}}),"function"==typeof define&&define.amd?define(function(){return e}):(window.blueimp=window.blueimp||{},window.blueimp.helper=e)}(),function(t){"use strict";"function"==typeof define&&define.amd?define(["./blueimp-helper"],t):(window.blueimp=window.blueimp||{},window.blueimp.Gallery=t(window.blueimp.helper||window.jQuery))}(function(t){"use strict";function e(t,i){return void 0===document.body.style.maxHeight?null:this&&this.options===e.prototype.options?t&&t.length?(this.list=t,this.num=t.length,this.initOptions(i),void this.initialize()):void this.console.log("blueimp Gallery: No or empty list provided as first argument.",t):new e(t,i)}return t.extend(e.prototype,{options:{container:"#blueimp-gallery",slidesContainer:"div",titleElement:"h3",displayClass:"blueimp-gallery-display",controlsClass:"blueimp-gallery-controls",singleClass:"blueimp-gallery-single",leftEdgeClass:"blueimp-gallery-left",rightEdgeClass:"blueimp-gallery-right",playingClass:"blueimp-gallery-playing",slideClass:"slide",slideLoadingClass:"slide-loading",slideErrorClass:"slide-error",slideContentClass:"slide-content",toggleClass:"toggle",prevClass:"prev",nextClass:"next",closeClass:"close",playPauseClass:"play-pause",typeProperty:"type",titleProperty:"title",urlProperty:"href",srcsetProperty:"urlset",displayTransition:!0,clearSlides:!0,stretchImages:!1,toggleControlsOnReturn:!0,toggleControlsOnSlideClick:!0,toggleSlideshowOnSpace:!0,enableKeyboardNavigation:!0,closeOnEscape:!0,closeOnSlideClick:!0,closeOnSwipeUpOrDown:!0,emulateTouchEvents:!0,stopTouchEventsPropagation:!1,hidePageScrollbars:!0,disableScroll:!0,carousel:!1,continuous:!0,unloadElements:!0,startSlideshow:!1,slideshowInterval:5e3,index:0,preloadRange:2,transitionSpeed:400,slideshowTransitionSpeed:void 0,event:void 0,onopen:void 0,onopened:void 0,onslide:void 0,onslideend:void 0,onslidecomplete:void 0,onclose:void 0,onclosed:void 0},carouselOptions:{hidePageScrollbars:!1,toggleControlsOnReturn:!1,toggleSlideshowOnSpace:!1,enableKeyboardNavigation:!1,closeOnEscape:!1,closeOnSlideClick:!1,closeOnSwipeUpOrDown:!1,disableScroll:!1,startSlideshow:!0},console:window.console&&"function"==typeof window.console.log?window.console:{log:function(){}},support:function(e){function i(){var t,i,s=n.transition;document.body.appendChild(e),s&&(t=s.name.slice(0,-9)+"ransform",void 0!==e.style[t]&&(e.style[t]="translateZ(0)",i=window.getComputedStyle(e).getPropertyValue(s.prefix+"transform"),n.transform={prefix:s.prefix,name:t,translate:!0,translateZ:!!i&&"none"!==i})),void 0!==e.style.backgroundSize&&(n.backgroundSize={},e.style.backgroundSize="contain",n.backgroundSize.contain="contain"===window.getComputedStyle(e).getPropertyValue("background-size"),e.style.backgroundSize="cover",n.backgroundSize.cover="cover"===window.getComputedStyle(e).getPropertyValue("background-size")),document.body.removeChild(e)}var s,n={touch:void 0!==window.ontouchstart||window.DocumentTouch&&document instanceof DocumentTouch},o={webkitTransition:{end:"webkitTransitionEnd",prefix:"-webkit-"},MozTransition:{end:"transitionend",prefix:"-moz-"},OTransition:{end:"otransitionend",prefix:"-o-"},transition:{end:"transitionend",prefix:""}};for(s in o)if(o.hasOwnProperty(s)&&void 0!==e.style[s]){n.transition=o[s],n.transition.name=s;break}return document.body?i():t(document).on("DOMContentLoaded",i),n}(document.createElement("div")),requestAnimationFrame:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame,initialize:function(){return this.initStartIndex(),this.initWidget()===!1?!1:(this.initEventListeners(),this.onslide(this.index),this.ontransitionend(),void(this.options.startSlideshow&&this.play()))},slide:function(t,e){window.clearTimeout(this.timeout);var i,s,n,o=this.index;if(o!==t&&1!==this.num){if(e||(e=this.options.transitionSpeed),this.support.transform){for(this.options.continuous||(t=this.circle(t)),i=Math.abs(o-t)/(o-t),this.options.continuous&&(s=i,i=-this.positions[this.circle(t)]/this.slideWidth,i!==s&&(t=-i*this.num+t)),n=Math.abs(o-t)-1;n;)n-=1,this.move(this.circle((t>o?t:o)-n-1),this.slideWidth*i,0);t=this.circle(t),this.move(o,this.slideWidth*i,e),this.move(t,0,e),this.options.continuous&&this.move(this.circle(t-i),-(this.slideWidth*i),0)}else t=this.circle(t),this.animate(o*-this.slideWidth,t*-this.slideWidth,e);this.onslide(t)}},getIndex:function(){return this.index},getNumber:function(){return this.num},prev:function(){(this.options.continuous||this.index)&&this.slide(this.index-1)},next:function(){(this.options.continuous||this.index