Index: debian/univention-management-console-module-appcenter.install
===================================================================
--- debian/univention-management-console-module-appcenter.install (Revision 67996)
+++ debian/univention-management-console-module-appcenter.install (Arbeitskopie)
@@ -12,3 +12,5 @@
umc/js/appcenter/star.svg usr/share/univention-management-console-frontend/js/umc/modules/appcenter
umc/js/appcenter/carouselArrowRight.svg usr/share/univention-management-console-frontend/js/umc/modules/appcenter
umc/js/appcenter/statusIcons.svg usr/share/univention-management-console-frontend/js/umc/modules/appcenter
+umc/js/appcenter/thumbnailError.svg usr/share/univention-management-console-frontend/js/umc/modules/appcenter
+umc/js/appcenter/videoPlayButton.svg usr/share/univention-management-console-frontend/js/umc/modules/appcenter
Index: umc/js/appcenter/AppDetailsPage.js
===================================================================
--- umc/js/appcenter/AppDetailsPage.js (Revision 67996)
+++ umc/js/appcenter/AppDetailsPage.js (Arbeitskopie)
@@ -63,9 +63,9 @@
"umc/widgets/Grid",
"umc/modules/appcenter/AppCenterGallery",
"umc/modules/appcenter/App",
- "umc/modules/appcenter/Carousel",
+ "umc/modules/appcenter/ThumbnailGallery",
"umc/i18n!umc/modules/appcenter"
-], function(declare, lang, kernel, array, dojoEvent, all, json, when, query, ioQuery, topic, Deferred, domConstruct, domClass, on, domStyle, Memory, Observable, Tooltip, Lightbox, entities, UMCApplication, tools, dialog, TitlePane, ContainerWidget, ProgressBar, Page, Text, Button, CheckBox, Grid, AppCenterGallery, App, Carousel, _) {
+], function(declare, lang, kernel, array, dojoEvent, all, json, when, query, ioQuery, topic, Deferred, domConstruct, domClass, on, domStyle, Memory, Observable, Tooltip, Lightbox, entities, UMCApplication, tools, dialog, TitlePane, ContainerWidget, ProgressBar, Page, Text, Button, CheckBox, Grid, AppCenterGallery, App, ThumbnailGallery, _) {
var adaptedGrid = declare([Grid], {
_updateContextActions: function() {
@@ -554,10 +554,10 @@
src: ithumb
};
});
- this.carousel = new Carousel({
+ this.thumbnailGallery = new ThumbnailGallery({
items: urls
});
- styleContainer.addChild(this.carousel);
+ styleContainer.addChild(this.thumbnailGallery);
this._detailsContainer.addChild(styleContainer);
}
@@ -568,6 +568,22 @@
content: this._detailsContainer,
'class': 'appDetailsPane'
});
+
+ //handle behaviour of the thumbnailGallery based on wether
+ //the titlepane is closed or not
+ if (!detailsPane.open) {
+ this.thumbnailGallery._stopFirstResize = true;
+ }
+ detailsPane.watch('open', lang.hitch(this, function(variable, oldVal, titlePaneIsOpen) {
+ if (titlePaneIsOpen) {
+ this.thumbnailGallery._handleResize();
+ } else {
+ if (this.thumbnailGallery.isBigThumbnails) {
+ this.thumbnailGallery.toggleThumbSize();
+ }
+ this.thumbnailGallery.pauseAllVideos();
+ }
+ }));
this._mainRegionContainer.addChild(detailsPane, isAppInstalled ? null : 0);
},
Index: umc/js/appcenter/Carousel.js
===================================================================
--- umc/js/appcenter/Carousel.js (Revision 67996)
+++ umc/js/appcenter/Carousel.js (Arbeitskopie)
@@ -1,490 +0,0 @@
-/*
- * Copyright 2011-2016 Univention GmbH
- *
- * http://www.univention.de/
- *
- * All rights reserved.
- *
- * The source code of this program is made available
- * under the terms of the GNU Affero General Public License version 3
- * (GNU AGPL V3) as published by the Free Software Foundation.
- *
- * Binary versions of this program provided by Univention to you as
- * well as other copyrighted, protected or trademarked materials like
- * Logos, graphics, fonts, specific documentations and configurations,
- * cryptographic keys etc. are subject to a license agreement between
- * you and Univention and not subject to the GNU AGPL V3.
- *
- * In the case you use this program under the terms of the GNU AGPL V3,
- * the program is provided in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Affero General Public License for more details.
- *
- * You should have received a copy of the GNU Affero General Public
- * License with the Debian GNU/Linux or Univention distribution in file
- * /usr/share/common-licenses/AGPL-3; if not, see
- * .
- */
-/*global define,console,require*/
-
-define([
- "dojo/_base/declare",
- "dojo/_base/array",
- "dojo/_base/lang",
- "dojo/_base/kernel",
- "dojo/_base/window",
- "dojo/on",
- "dojo/query",
- "dojo/dom-construct",
- "dojo/dom-style",
- "dojo/dom-geometry",
- "dojo/dom-class",
- "dojox/html/styles",
- "umc/tools",
- "umc/widgets/ContainerWidget",
- "umc/widgets/_RegisterOnShowMixin",
- "dojo/domReady!"
-], function(declare, array, lang, kernel, baseWin, on, query, domConstruct, domStyle, domGeometry, domClass, styles, tools, ContainerWidget, _RegisterOnShowMixin) {
- return declare("umc.modules.appcenter.Carousel_new", [ContainerWidget, _RegisterOnShowMixin], {
- baseClass: 'umcCarouselWidget',
-
- outerContainer: null,
- contentSlider: null,
- contentSliderOffset: null,
-
- shownItemIndex: null,
-
- itemHeight: null,
- heighestImg: null,
-
- // items: Array
- // array of objects({src: })
- // where is either an image or a youtube video url
- items: null,
- itemNodes: null,
-
- allItemsLoaded: null,
-
- bigThumbnails: null,
-
- _resizeDeferred: null,
-
- postMixInProperties: function() {
- this.itemHeight = this.itemHeight || 200;
- this.itemNodes = [];
- this.contentSliderOffset = 0;
- this.shownItemIndex = 0;
- this.allItemsLoaded = false;
- this.bigThumbnails = false;
- },
-
- buildRendering: function() {
- this.inherited(arguments);
-
- this.outerContainer = new ContainerWidget({
- 'class': 'carouselOuterContainer'
- });
-
- this.addChild(this.outerContainer);
- this.own(this.outerContainer);
-
- this.renderContentSlider();
- this.renderNavButtons();
- this.renderScaleButton();
-
- },
-
- postCreate: function() {
- if (window.YT && window.YT.loaded) {
- setTimeout(lang.hitch(this, function() {
- this._renderYoutubeVideos();
- }), 500);
- } else {
- //load youtube api
- var tag = document.createElement('script');
- tag = domConstruct.create('script', {
- id: "youtubeAPI",
- src: "https://www.youtube.com/iframe_api"
- });
- var firstScriptTag = document.getElementsByTagName('script')[0];
- firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
-
- window.onYouTubeIframeAPIReady = lang.hitch(this, function() {
- this._renderYoutubeVideos();
- });
- }
- onPlayerReady = lang.hitch(this, function() {
- this.imagesLoaded();
- });
- },
-
- renderContentSlider: function() {
- this.contentSliderWrapper = new ContainerWidget({
- 'class': 'contentSliderWrapper'
- });
- this.contentSlider = new ContainerWidget({
- 'class': 'contentSlider'
- });
- this.contentSliderWrapper.addChild(this.contentSlider);
- this.outerContainer.addChild(this.contentSliderWrapper);
-
- this._renderThumbs();
- },
-
- _renderThumbs: function() {
- this.loadedImagesCount = 0;
- var index = 0;
- var uniqueYTVideoIds = [];
-
- array.forEach(this.items, lang.hitch(this, function(item) {
- if (this._srcIsYoutubeVideo(item.src)) {
- var videoId = this._getYoutubeUrlVideoId(item.src);
- if (uniqueYTVideoIds.indexOf(videoId) !== -1) {
- return;
- }
- uniqueYTVideoIds.push(videoId);
-
- var videoWrapper = domConstruct.create('div', {'class': 'galleryVideo'}, this.contentSlider.domNode);
- var div = domConstruct.create('div', {
- id: videoId
- }, videoWrapper);
-
- this.itemNodes.push(videoWrapper);
- } else {
- var imgWrapper = domConstruct.create('div', {}, this.contentSlider.domNode);
- var img = domConstruct.create('img', {
- src: item.src,
- index: index,
- 'class': 'carouselScreenshot',
- onload: lang.hitch(this, function() {
- this._updateHeighestImage(img);
- this.imagesLoaded();
- }),
- onclick: lang.hitch(this, function(evt) {
- this.togglePreviewSize(parseInt(evt.target.getAttribute('index')));
- })
- }, imgWrapper);
- this.itemNodes.push(imgWrapper);
- }
- index++;
- }));
- },
-
- _renderYoutubeVideos: function() {
- query('.galleryVideo', this.contentSlider.domNode).forEach(lang.hitch(this, function(videoWrapper) {
- var player;
- var videoId = videoWrapper.firstChild.id;
- player = new YT.Player(videoId, {
- height: this.itemHeight.toString(),
- width: 'auto',
- videoId: videoId,
- events: {
- 'onReady': onPlayerReady
- }
- });
- this.own(player);
- }));
- },
-
- _srcIsYoutubeVideo: function(src) {
- //taken from http://stackoverflow.com/questions/28735459/how-to-validate-youtube-url-in-client-side-in-text-box
- var p = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;
- if(src.match(p)){
- return true;
- }
- return false;
- },
-
- _getYoutubeUrlVideoId: function(src) {
- var p = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;
- return src.match(p)[1];
- },
-
- _setDefaultThumbsHeight: function() {
- if (!styles.getStyleSheet('defaultItemHeightImage')) {
- styles.insertCssRule('.contentSlider .carouselScreenshot', lang.replace('max-height: {0}px', [this.itemHeight]), 'defaultItemHeightImage');
- }
- if (!styles.getStyleSheet('defaultItemHeightWrapper')) {
- styles.insertCssRule('.contentSlider div', lang.replace('height: {0}px', [this.itemHeight]), 'defaultItemHeightWrapper');
- }
- },
-
- renderNavButtons: function() {
- this.leftButton = domConstruct.create('div', {
- 'class': 'carouselButton leftCarouselButton',
- onclick: lang.hitch(this, function() {
- this.showItem(this.shownItemIndex - 1);
- })
- }, this.contentSliderWrapper.domNode, 'before');
- domConstruct.create('div', {
- 'class': 'carouselButtonImage'
- }, this.leftButton);
-
- this.rightButton = domConstruct.create('div', {
- 'class': 'carouselButton rightCarouselButton',
- onclick: lang.hitch(this, function() {
- this.showItem(this.shownItemIndex + 1);
- })
- }, this.contentSliderWrapper.domNode, 'after');
- domConstruct.create('div', {
- 'class': 'carouselButtonImage'
- }, this.rightButton);
- },
-
- renderScaleButton: function() {
- this.scaleButton = domConstruct.create('div', {
- 'class': 'scaleButton dijitHidden',
- onclick: lang.hitch(this, function() {
- this.togglePreviewSize(this.shownItemIndex);
- })
- }, this.domNode);
- },
-
- imagesLoaded: function() {
- this.loadedImagesCount++;
- if (this.loadedImagesCount === this.itemNodes.length) {
- this.allItemsLoaded = true;
- this.heighestImg = this.heighestImg.cloneNode();
- domConstruct.place(this.heighestImg, this.domNode);
- domClass.add(this.heighestImg, 'dijitHidden');
- domClass.remove(this.scaleButton, 'dijitHidden');
-
- this._setDefaultThumbsHeight();
- this.resizeCarousel();
- this.calcDefaultSliderWidth();
- this.calcOffsets();
- }
- },
-
- _updateHeighestImage: function(img) {
- if (!this.heighestImg) {
- this.heighestImg = img;
- }
- if ((img.height > img.width) && (img.height > this.heighestImg.height)) {
- this.heighestImg = img;
- }
- },
-
- calcDefaultSliderWidth: function() {
- this.defaultSliderWidth = domGeometry.getMarginBox(this.contentSlider.domNode).w;
- this.defaultItemWidths = [];
- array.forEach(this.itemNodes, lang.hitch(this, function(itemNode) {
- this.defaultItemWidths.push(domGeometry.getMarginBox(itemNode).w);
- }));
- },
-
- calcOffsets: function() {
- this.offsets = [0];
- var maxOffset = Math.max(0, this.defaultSliderWidth - domGeometry.getMarginBox(this.contentSliderWrapper.domNode).w);
-
- for (var i = 1; i < this.itemNodes.length; i++) {
- var offset = 0;
- for (var c = 0; c < i; c++) {
- offset += this.defaultItemWidths[c];
- }
- if (offset >= maxOffset) {
- this.offsets.push(maxOffset);
- } else {
- this.offsets.push(offset);
- }
- }
- },
-
- resizeCarousel: function() {
- if (this.bigThumbnails) {
- this._resizeBigThumbnails();
- }
- var totalItemsWidth = 0;
- array.forEach(this.itemNodes, function(imgWrapper) {
- var imgWrapperWidth = domGeometry.getMarginBox(imgWrapper).w;
- totalItemsWidth += imgWrapperWidth;
- });
-
- var contentSliderHeight = domGeometry.getMarginBox(this.contentSlider.domNode).h;
- domStyle.set(this.outerContainer.domNode, 'height', contentSliderHeight + 'px');
-
- var availableWidth = domGeometry.getMarginBox(this.domNode).w;
-
- if (availableWidth >= totalItemsWidth) {
- domClass.add(this.leftButton, 'dijitHidden');
- domClass.add(this.rightButton, 'dijitHidden');
- domStyle.set(this.outerContainer.domNode, 'width', totalItemsWidth + 'px');
- domStyle.set(this.contentSliderWrapper.domNode, 'width', totalItemsWidth + 'px');
- } else {
- domClass.remove(this.leftButton, 'dijitHidden');
- domClass.remove(this.rightButton, 'dijitHidden');
-
- var widthForThumbs = availableWidth -
- (domGeometry.getMarginBox(this.leftButton).w + domGeometry.getMarginBox(this.rightButton).w);
- domStyle.set(this.outerContainer.domNode, 'width', availableWidth + 'px');
- domStyle.set(this.contentSliderWrapper.domNode, 'width', widthForThumbs + 'px');
- }
- this._toggleNavButtonsVisibility();
- },
-
- _resizeBigThumbnails: function(newIndex) {
- var maxHeight = dojo.window.getBox().h - 200;
- maxHeight = (maxHeight < this.itemHeight) ? this.itemHeight : maxHeight;
-
- var marginOfThumbs = domGeometry.getMarginExtents(this.itemNodes[0]).w;
- domClass.remove(this.leftButton, 'dijitHidden'); //make sure navButton is visible for sizing calculations
- domClass.remove(this.rightButton, 'dijitHidden');
- var maxWidth = domGeometry.getMarginBox(this.domNode).w - (domGeometry.getMarginBox(this.leftButton).w * 2) - marginOfThumbs;
- domStyle.set(this.outerContainer.domNode, 'width', '');
- domStyle.set(this.contentSliderWrapper.domNode, 'width', maxWidth + 'px');
-
- styles.disableStyleSheet('defaultItemHeightWrapper');
-
- domStyle.set(this.heighestImg, 'position', 'absolute');
- domStyle.set(this.heighestImg, 'right', '1000000px');
- domClass.toggle(this.heighestImg, 'dijitHidden');
-
-
- domStyle.set(this.heighestImg, 'max-height', maxHeight + 'px');
- domStyle.set(this.heighestImg, 'max-width', maxWidth + 'px');
- var heighestImgHeight = domGeometry.getMarginBox(this.heighestImg).h;
- domClass.toggle(this.heighestImg, 'dijitHidden');
-
- array.forEach(this.itemNodes, lang.hitch(this, function(imgWrapper) {
- domStyle.set(imgWrapper.firstChild, 'max-width', maxWidth + 'px');
- domStyle.set(imgWrapper.firstChild, 'max-height', maxHeight + 'px');
- domStyle.set(imgWrapper, 'height', heighestImgHeight + 'px');
- domStyle.set(imgWrapper, 'width', maxWidth + 'px');
- }));
- query('.galleryVideo', this.contentSlider.domNode).forEach(lang.hitch(this, function(videoWrapper) {
- domStyle.set(videoWrapper.firstChild, 'height', '100%');
- domStyle.set(videoWrapper.firstChild, 'width', '100%');
- }));
- domStyle.set(this.outerContainer.domNode, 'height', heighestImgHeight + 'px');
-
- var newOffset = newIndex * (maxWidth + marginOfThumbs);
- domStyle.set(this.contentSlider.domNode, 'left', (newOffset * (-1)) + 'px');
- },
-
- _toggleNavButtonsVisibility: function(newOffset) {
- var _contentSliderOffset;
- if (newOffset === undefined) {
- _contentSliderOffset = Math.abs(domStyle.get(this.contentSlider.domNode, 'left'));
- } else {
- _contentSliderOffset = newOffset;
- }
-
- var maxOffset = domGeometry.getMarginBox(this.contentSlider.domNode).w - domGeometry.getMarginBox(this.contentSliderWrapper.domNode).w;
-
- domClass.toggle(this.leftButton, 'disabled', (_contentSliderOffset === 0 || this.shownItemIndex === 0));
- domClass.toggle(this.rightButton, 'disabled', (_contentSliderOffset === maxOffset || this.shownItemIndex === this.items.length-1));
- },
-
- togglePreviewSize: function(newIndex) {
- if (this.bigThumbnails) {
- styles.enableStyleSheet('defaultItemHeightWrapper');
- var availableWidth = domGeometry.getMarginBox(this.domNode).w;
-
- if (availableWidth >= this.defaultSliderWidth) {
- domClass.add(this.leftButton, 'dijitHidden');
- domClass.add(this.rightButton, 'dijitHidden');
- domStyle.set(this.outerContainer.domNode, 'transition', 'height 0.5s, width 0.5s');
- domStyle.set(this.contentSliderWrapper.domNode, 'transition', 'height 0.5s, width 0.5s');
- domStyle.set(this.outerContainer.domNode, 'width', this.defaultSliderWidth + 'px');
- domStyle.set(this.contentSliderWrapper.domNode, 'width', this.defaultSliderWidth + 'px');
- }
-
-
- query('.carouselScreenshot', this.contentSlider.domNode).forEach(lang.hitch(this, function(imgNode) {
- domStyle.set(imgNode, 'max-height', '');
- domStyle.set(imgNode, 'max-width', '');
- domStyle.set(imgNode.parentNode, 'width', '');
- domStyle.set(imgNode.parentNode, 'height', '');
- }));
- query('.galleryVideo', this.contentSlider.domNode).forEach(lang.hitch(this, function(videoWrapper) {
- domStyle.set(videoWrapper, 'height', '');
- domStyle.set(videoWrapper, 'width', '');
- domStyle.set(videoWrapper.firstChild, 'height', '');
- domStyle.set(videoWrapper.firstChild, 'width', '');
- }));
- domStyle.set(this.outerContainer.domNode, 'height', this.itemHeight + 'px');
- domStyle.set(this.contentSlider.domNode, 'left', Math.abs(this.offsets[newIndex]) * (-1) + 'px');
-
- } else {
- domStyle.set(this.outerContainer.domNode, 'transition', '');
- domStyle.set(this.contentSliderWrapper.domNode, 'transition', '');
- this._resizeBigThumbnails(newIndex);
- }
- this.bigThumbnails = !this.bigThumbnails;
- domClass.toggle(this.scaleButton, 'minimize');
- this.shownItemIndex = newIndex;
-
- setTimeout(lang.hitch(this, function() {
- this.resizeCarousel();
- }), 600);
-
- //scroll to the top of the image
- //var scrollTarget = domGeometry.position(this.domNode, true).y - 50;
- //window.scrollTo(0, scrollTarget);
- },
-
- showItem: function(newIndex) {
- if (newIndex < 0 || newIndex >= this.itemNodes.length) {
- return;
- }
-
- var newOffset = 0;
-
- var neededOffset = 0;
- for (var i = 0; i < newIndex; i++) {
- var itemWidth = domGeometry.getMarginBox(this.itemNodes[i]).w;
- neededOffset += itemWidth;
- }
-
- var maxOffset = domGeometry.getMarginBox(this.contentSlider.domNode).w - domGeometry.getMarginBox(this.contentSliderWrapper.domNode).w;
- if (neededOffset >= maxOffset) {
- newOffset = maxOffset;
- } else {
- newOffset = neededOffset;
- }
-
- var oldOffset = Math.abs(domStyle.get(this.contentSlider.domNode, 'left'));
-
- //if the new index would not change the offset
- //take the next lower index till a new offset is found
- if (oldOffset === newOffset && newIndex < this.shownItemIndex) {
- this.showItem(newIndex -1);
- return;
- }
-
- this.shownItemIndex = newIndex;
- domStyle.set(this.contentSlider.domNode, 'left', (newOffset * (-1)) + 'px');
-
- this._toggleNavButtonsVisibility(newOffset);
- },
-
- _handleResize: function() {
- if (this._resizeDeferred && !this._resizeDeferred.isFulfilled()) {
- this._resizeDeferred.cancel();
- }
- this._resizeDeferred = tools.defer(lang.hitch(this, function() {
- this.resizeCarousel();
- this.calcOffsets();
- this.showItem(this.shownItemIndex);
- }), 200);
- this._resizeDeferred.otherwise(function() { /* prevent logging of exception */ });
- },
-
- startup: function() {
- this.inherited(arguments);
- this._registerAtParentOnShowEvents(lang.hitch(this, function() {
- if (this.defaultSliderWidth === 0 && !this.bigThumbnails) {
- this.calcDefaultSliderWidth();
- }
- if (this.allItemsLoaded) {
- this.resizeCarousel();
- this.showItem(this.shownItemIndex);
- }
- }));
- this.own(on(baseWin.doc, 'resize', lang.hitch(this, '_handleResize')));
- this.own(on(kernel.global, 'resize', lang.hitch(this, '_handleResize')));
- styles.enableStyleSheet('defaultItemHeightWrapper');
- }
- });
-});
Index: umc/js/appcenter/ThumbnailGallery.js
===================================================================
--- umc/js/appcenter/ThumbnailGallery.js (Revision 0)
+++ umc/js/appcenter/ThumbnailGallery.js (Arbeitskopie)
@@ -0,0 +1,1052 @@
+/*
+ * Copyright 2011-2016 Univention GmbH
+ *
+ * http://www.univention.de/
+ *
+ * All rights reserved.
+ *
+ * The source code of this program is made available
+ * under the terms of the GNU Affero General Public License version 3
+ * (GNU AGPL V3) as published by the Free Software Foundation.
+ *
+ * Binary versions of this program provided by Univention to you as
+ * well as other copyrighted, protected or trademarked materials like
+ * Logos, graphics, fonts, specific documentations and configurations,
+ * cryptographic keys etc. are subject to a license agreement between
+ * you and Univention and not subject to the GNU AGPL V3.
+ *
+ * In the case you use this program under the terms of the GNU AGPL V3,
+ * the program is provided in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public
+ * License with the Debian GNU/Linux or Univention distribution in file
+ * /usr/share/common-licenses/AGPL-3; if not, see
+ * .
+ */
+/*global define,console,require*/
+
+define([
+ "dojo/_base/declare",
+ "dojo/_base/array",
+ "dojo/_base/lang",
+ "dojo/_base/kernel",
+ "dojo/_base/window",
+ "dojo/on",
+ "dojo/query",
+ "dojo/dom-construct",
+ "dojo/dom-style",
+ "dojo/dom-geometry",
+ "dojo/dom-class",
+ "dojox/html/styles",
+ "umc/tools",
+ "umc/widgets/ContainerWidget"
+], function(declare, array, lang, kernel, baseWin, on, query, domConstruct, domStyle, domGeometry, domClass, styles, tools, ContainerWidget) {
+ return declare("umc.modules.appcenter.ThumbnailGallery", [ContainerWidget], {
+ baseClass: 'umcThumbnailGallery',
+
+ outerContainer: null,
+
+ //is the viewport for the contentSlider
+ contentSliderWrapper: null,
+
+ //the contentSlider contains all thumbs
+ //the offset of the contentSlider gets changed to show different thumbs
+ contentSlider: null,
+
+ //the current offset off the contentSlider as absolute value ignoring navButtons
+ //used for calculations
+ contentSliderOffset: null,
+
+ //the currently displayed offset as absolute value
+ //the navButtons lay on top of the contentSlider but the calculations ignore that fact. so the width of one or
+ //both navButtons have to be accounted after the fact
+ _visibleOffset: null,
+
+ //toggles thumb size
+ scaleButton: null,
+
+ //all ThumbNodes that are in the contentSlider
+ itemNodes: null,
+
+ //shownItemIndex: int
+ // The currently visible thumb (itemNodes[shownItemIndex]).
+ // In the small view the shownItemIndex is either the leftmost or rightmost thumb
+ // based on the last call of showPrevThumbs or showNextThumbs.
+ // In the big view it is the visible thumb.
+ shownItemIndex: null,
+
+ //the default height for gallery thumbs in the small view
+ defaultThumbHeight: null,
+
+ //heighestImg: {w: number, h: number}
+ // the natural width and height of the heighest img in the contentSlider
+ heighestImg: null,
+
+ //heighestImgRatio = heighestImg.w / heighestImg.h
+ // If there are only videos in the contentSlider
+ // the heighestImgRatio defaults to the _youtubeIframeRatio
+ // to determine the height for big Thumbnails
+ heighestImgRatio: null,
+
+ //used to check if the contentSliderWrapper is to small for showNextThumbs and showPrevThumbs to work correctly
+ //and change behaviour accordingly
+ widestDefaultThumbWidth: null,
+
+ //_youtubeIframeRatio: defaults to 16/9
+ _youtubeIframeRatio: null,
+
+ // items: Array
+ // array of received objects({src: })
+ // where is either an image or a youtube video url
+ items: null,
+
+ //preparedItems: Array
+ // array of objects derived from items.
+ // Duplicate youtube videos are removed and
+ // objects are marked as either 'img' or 'video'
+ preparedItems: null,
+
+ //allItemsLoaded: bool
+ allItemsLoaded: null,
+
+ _loadedThumbsCount: null,
+
+ //isBigThumbnails: bool
+ isBigThumbnails: null,
+
+ //the margin each thumb has on either side
+ _galleryThumbLeftRightMargin: null,
+ //the whole left and right margin for thumbs
+ _galleryThumbMarginExtents: null,
+
+ //naturalThumbDimensions: object{: {w: , h: }}
+ // stores the unscaled width and height of the images.
+ // defaults to w: 1600, h: 900 for videos
+ naturalThumbDimensions: null,
+
+ //defaultThumbWidths: object{: }
+ // The default width for all thumbs in the small view including this._galleryThumbMarginExtents
+ defaultThumbWidths: null,
+
+ //defaultThumbOffsets: array
+ // contains the offset for every thumb so it is the leftmost first visible thumb
+ defaultThumbOffsets: null,
+
+ //ytPlayers: Array
+ // array of youtube Player objects for every video in the Gallery
+ // ( https://developers.google.com/youtube/iframe_api_reference?hl=de#Loading_a_Video_Player )
+ // used to pause videos if they are no longer visible
+ ytPlayers: null,
+
+ //thumbIndexToVideoId: object
+ // maps the id of the videoThumb to its youtube video_id
+ thumbIndexToVideoId: null,
+ videoIdToThumbIndex: null,
+
+ playingVideos: null,
+
+ //_insertedCssRules: Array
+ // contains array with objects with the selector and declaration inserted via styles.insertCssRule.
+ // {selector: , declaration: }
+ _insertedCssRules: null,
+
+ _resizeDeferred: null,
+
+ _firstResizeInterval: null,
+
+ _stopFirstResize: null,
+
+ //_baseTransitionDuration: int in ms
+ _baseTransitionDuration: null,
+
+ postMixInProperties: function() {
+ this.preparedItems = [];
+ this.defaultThumbHeight = this.defaultThumbHeight || 200;
+ this.itemNodes = [];
+ this.contentSliderOffset = 0;
+ this._visibleOffset = 0;
+ this.shownItemIndex = 0;
+ this.allItemsLoaded = false;
+ this.isBigThumbnails = false;
+ this._youtubeIframeRatio = 16 / 9;
+ //if there are only youtube videos in the contentslider
+ //the height for big thumbnails is determined by the _youtubeIframeRatio
+ this.heighestImgRatio = this._youtubeIframeRatio;
+ this.heighestImg = {w: 0, h: 0};
+ this.widestDefaultThumbWidth = 0;
+ this._galleryThumbLeftRightMargin = 10;
+ this._galleryThumbMarginExtents = 2 * this._galleryThumbLeftRightMargin;
+ this.naturalThumbDimensions = {};
+ this.defaultThumbWidths = {};
+ this.ytPlayers = {};
+ this.thumbIndexToVideoId = {};
+ this.videoIdToThumbIndex = {};
+ this.playingVideos = {};
+ this._insertedCssRules = [];
+ this._totalWidthOfSmallThumbs = 0;
+ this._bigThumbDimensions = {};
+ this._smallMaxOffset = 0;
+ this._widthForThumbs = 0;
+ this.loaded = false;
+ this._smallViewHasNavButtons = false;
+ this._stopFirstResize = false;
+ this._baseTransitionDuration = 500;
+ },
+
+ postCreate: function() {
+ //check if youtube iframe api is already loaded
+ if (window.YT && window.YT.loaded) {
+ //stub
+ } else {
+ //load youtube iframe api
+ var tag = document.createElement('script');
+ tag = domConstruct.create('script', {
+ id: "youtubeAPI",
+ src: "https://www.youtube.com/iframe_api"
+ });
+ var firstScriptTag = document.getElementsByTagName('script')[0];
+ firstScriptTag.parentNode.insertBefore(tag, firstScriptTag);
+ }
+ },
+
+ buildRendering: function() {
+ this.inherited(arguments);
+
+ this._insertGalleryVideoDefaultDimensions();
+ this._insertGalleryThumbCssRules();
+ this._insertTransitionDurations();
+
+ //style needed while thumbs are getting loaded
+ domStyle.set(this.domNode, 'height', this.defaultThumbHeight + 'px');
+ domStyle.set(this.domNode, 'overflow', 'hidden');
+
+ this.galleryLoadingOverlay = domConstruct.create('div', {
+ 'class': 'galleryLoadingOverlay'
+ }, this.domNode);
+
+ this.outerContainer = new ContainerWidget({
+ 'class': 'galleryOuterContainer'
+ });
+ this.contentSliderWrapper = new ContainerWidget({
+ 'class': 'contentSliderWrapper'
+ });
+
+ this.outerContainer.addChild(this.contentSliderWrapper);
+ this.addChild(this.outerContainer);
+ this.own(this.outerContainer);
+
+ this.renderNavButtons();
+ this.renderScaleButton();
+ this.renderContentSlider();
+ },
+
+ renderNavButtons: function() {
+ //construct left and right navButtons
+ this.leftButton = domConstruct.create('div', {
+ 'class': 'galleryNavButton leftGalleryNavButton disabled'
+ }, this.contentSliderWrapper.domNode, 'before');
+ on(this.leftButton, 'click', lang.hitch(this, function() {
+ this.showPrevThumbs();
+ }));
+ domConstruct.create('div', {
+ 'class': 'galleryNavButtonImage'
+ }, this.leftButton);
+
+ this.rightButton = domConstruct.create('div', {
+ 'class': 'galleryNavButton rightGalleryNavButton disabled'
+ }, this.contentSliderWrapper.domNode, 'after');
+ on(this.rightButton, 'click', lang.hitch(this, function() {
+ this.showNextThumbs();
+ }));
+ domConstruct.create('div', {
+ 'class': 'galleryNavButtonImage'
+ }, this.rightButton);
+
+ //insert Css Rule
+ var selector = lang.replace('#{0} .galleryNavButton', [this.id]);
+ var declaration = lang.replace('height: {0}px; transition-duration: {1}ms', [this.defaultThumbHeight, this._baseTransitionDuration]);
+ styles.insertCssRule(selector, declaration);
+ this._insertedCssRules.push({selector: selector, declaration: declaration});
+ },
+
+ renderScaleButton: function() {
+ this.scaleButton = domConstruct.create('div', {
+ 'class': 'scaleButton dijitHidden',
+ onclick: lang.hitch(this, function() {
+ this.toggleThumbSize(this.shownItemIndex);
+ })
+ }, this.domNode);
+ },
+
+ renderContentSlider: function() {
+ this.contentSlider = new ContainerWidget({
+ 'class': 'contentSlider'
+ });
+
+ this.galleryThumbVerticalAlignHelper = domConstruct.create('div', {
+ 'class': 'galleryThumb verticalAlignHelper'
+ }, this.contentSlider.domNode);
+
+ this.contentSliderWrapper.addChild(this.contentSlider);
+
+ this._renderThumbs();
+ },
+
+ _renderThumbs: function() {
+ this._prepareItems();
+ this._loadedThumbsCount = 0;
+
+ array.forEach(this.preparedItems, lang.hitch(this, function(item, index) {
+ if (item.type === 'video') {
+ this._createVideoThumb(item, index);
+ } else {
+ this._createImgThumb(item, index);
+ }
+ }));
+ },
+
+ _prepareItems: function() {
+ var uniqueYTVideoIds = [];
+
+ array.forEach(this.items, lang.hitch(this, function(item, index) {
+ if (this._srcIsYoutubeVideo(item.src)) {
+ var videoId = this._getYoutubeUrlVideoId(item.src);
+ if (uniqueYTVideoIds.indexOf(videoId) !== -1) {
+ return;
+ }
+ uniqueYTVideoIds.push(videoId);
+ this.preparedItems.push({type: 'video', videoId: videoId});
+ } else {
+ this.preparedItems.push({type: 'img', src: item.src});
+ }
+ }));
+ },
+
+ _createVideoThumb: function(item, index) {
+ var videoId = item.videoId;
+
+ //get the thumbnail for the youtube video via the unique id
+ var ytVideoThumbnailURL = lang.replace('https://img.youtube.com/vi/{0}/hqdefault.jpg', [videoId]);
+
+ var galleryVideoWrapper = domConstruct.create('div', {
+ id: 'galleryThumb_' + index,
+ 'class': 'galleryThumb galleryVideoThumb galleryVideoWrapper'
+ }, this.contentSlider.domNode);
+
+ //show the thumbnail of the video with a playbutton
+ var thumbNailWrapper = domConstruct.create('div', {
+ 'class': 'galleryVideoThumbnailWrapper'
+ }, galleryVideoWrapper);
+
+ var thumbNail = domConstruct.create('div', {
+ 'class': 'galleryVideoThumbnail'
+ }, thumbNailWrapper);
+ var playButtonImg = domConstruct.create('div', {
+ 'class': 'galleryVideoPlayButtonIcon'
+ }, thumbNailWrapper);
+
+ //this div gets replaced by the youtube iframe
+ var galleryVideo = domConstruct.create('div', {
+ 'class': 'galleryVideo dijitHidden',
+ id: videoId
+ }, galleryVideoWrapper);
+
+ //set the thumbnail of the ytVideo as Background
+ var selector = lang.replace('#{0} #galleryThumb_{1} .galleryVideoThumbnail', [this.id, index]);
+ var declaration = lang.replace('background-image: url({0})', [ytVideoThumbnailURL]);
+ styles.insertCssRule(selector, declaration);
+ this._insertedCssRules.push({selector: selector, declaration: declaration});
+
+ //load youtube video
+ on(galleryVideoWrapper, 'click', lang.hitch(this, function() {
+ this.shownItemIndex = index;
+ if (window.YT && window.YT.loaded) {
+ this.showThumb(this.shownItemIndex);
+ domClass.add(galleryVideoWrapper, 'loaded');
+ domClass.remove(galleryVideo, 'dijitHidden');
+ domClass.add(playButtonImg, 'dijitHidden');
+ domClass.add(thumbNailWrapper, 'dijitHidden');
+ setTimeout(lang.hitch(this, function() {
+ this._renderYoutubeVideo(galleryVideo, index);
+ }), 0);
+ }
+ }));
+
+ this.naturalThumbDimensions[index] = {w: 1600, h: 900};
+ var w = Math.round(this.defaultThumbHeight * this._youtubeIframeRatio);
+ var h = this.defaultThumbHeight;
+ this.defaultThumbWidths[index] = w + this._galleryThumbMarginExtents;
+ this._updateWidestThumb(w);
+
+ this.itemNodes.push(galleryVideoWrapper);
+ item.domNode = galleryVideoWrapper;
+ this.thumbLoaded();
+ },
+
+ _createImgThumb: function(item, index) {
+ var imgUrl = item.src;
+ var galleryImgThumb = domConstruct.create('div', {
+ 'class': 'galleryThumb galleryImgThumb',
+ id: 'galleryThumb_' + index
+ }, this.contentSlider.domNode);
+
+ var img = domConstruct.create('img', {
+ src: imgUrl,
+ onload: lang.hitch(this, function() {
+ this._updateHeighestImage(img);
+ this.naturalThumbDimensions[index] = {w: img.naturalWidth, h: img.naturalHeight};
+
+ //calc the width and height for the thumb in the small view
+ var w = Math.round(this.defaultThumbHeight * (img.naturalWidth / img.naturalHeight));
+ var h = this.defaultThumbHeight;
+ this.defaultThumbWidths[index] = w + this._galleryThumbMarginExtents;
+ this._updateWidestThumb(w);
+
+ //insert img as background
+ var selector = lang.replace('#{0} #{1}', [this.id, galleryImgThumb.id]);
+ var declaration = lang.replace('width: {0}px; height: {1}px; background-image: url({2})', [w, h, img.src]);
+ styles.insertCssRule(selector, declaration);
+ this._insertedCssRules.push({selector: selector, declaration: declaration});
+
+ this.thumbLoaded();
+ }),
+ onerror: lang.hitch(this, function() {
+ //calc the width and height for the thumb in the small view
+ var w = Math.round(this.defaultThumbHeight * (3/4));
+ var h = this.defaultThumbHeight;
+ this.naturalThumbDimensions[index] = {w: w, h: h};
+ this.defaultThumbWidths[index] = w + this._galleryThumbMarginExtents;
+ this._updateWidestThumb(w);
+
+ //insert img as background
+ var selector = lang.replace('#{0} #{1}', [this.id, galleryImgThumb.id]);
+ var declaration = lang.replace('width: {0}px; height: {1}px;', [w, h]);
+ styles.insertCssRule(selector, declaration);
+ this._insertedCssRules.push({selector: selector, declaration: declaration});
+ domClass.add(galleryImgThumb, 'brokenImg');
+
+ this.thumbLoaded();
+ })
+ });
+
+ on(galleryImgThumb, 'click', lang.hitch(this, function() {
+ this.toggleThumbSize(index);
+ }));
+ this.itemNodes.push(galleryImgThumb);
+ item.domNode = galleryImgThumb;
+ },
+
+ _srcIsYoutubeVideo: function(src) {
+ //taken from http://stackoverflow.com/questions/28735459/how-to-validate-youtube-url-in-client-side-in-text-box
+ var p = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;
+ if(src.match(p)){
+ return true;
+ }
+ return false;
+ },
+
+ _getYoutubeUrlVideoId: function(src) {
+ var p = /^(?:https?:\/\/)?(?:www\.)?(?:youtu\.be\/|youtube\.com\/(?:embed\/|v\/|watch\?v=|watch\?.+&v=))((\w|-){11})(?:\S+)?$/;
+ return src.match(p)[1];
+ },
+
+ _insertGalleryVideoDefaultDimensions: function() {
+ //calc default width and height
+ var width = Math.round(this.defaultThumbHeight * this._youtubeIframeRatio);
+ var height = this.defaultThumbHeight;
+
+ //insert Css Rule
+ var selector = lang.replace('#{0}.{1} .contentSlider .galleryVideoWrapper', [this.id, this.baseClass]);
+ var declaration = lang.replace('width: {0}px; height: {1}px', [width, height]);
+ styles.insertCssRule(selector, declaration);
+ this._insertedCssRules.push({selector: selector, declaration: declaration});
+ },
+
+ _insertGalleryThumbCssRules: function() {
+ var selector = lang.replace('#{0} .contentSlider .galleryThumb', [this.id]);
+ var declaration = lang.replace('margin: 0 {0}px; transition-duration: {1}ms', [this._galleryThumbLeftRightMargin, this._baseTransitionDuration]);
+ styles.insertCssRule(selector, declaration);
+ this._insertedCssRules.push({ selector: selector, declaration: declaration });
+ },
+
+ _insertTransitionDurations: function() {
+ var selector;
+ var declaration;
+ //contentSlider
+ selector = lang.replace('#{0} .contentSlider', [this.id]);
+ declaration = lang.replace('transition-duration: {0}ms', [this._baseTransitionDuration]);
+ styles.insertCssRule(selector, declaration);
+ this._insertedCssRules.push({ selector: selector, declaration: declaration });
+ },
+
+ _renderYoutubeVideo: function(galleryVideo, index) {
+ var player;
+ var videoId = galleryVideo.id;
+ //add the youtube video via the youtube iframe api
+ player = new YT.Player(videoId, {
+ videoId: videoId,
+ events: {
+ 'onReady': lang.hitch(this, this.onPlayerReady),
+ 'onStateChange': lang.hitch(this, this.onPlayerStateChange)
+ }
+ });
+ this.own(player);
+
+ //safe a reference to the video id accessible through the thumbIndex and vice versa
+ this.thumbIndexToVideoId[index] = videoId;
+ this.videoIdToThumbIndex[videoId] = index;
+ //safe the youtube player object
+ this.ytPlayers[videoId] = {playerIndex: index};
+ },
+
+ onPlayerReady: function(evt) {
+ evt.target.playVideo();
+ var videoId = evt.target.getVideoData().video_id;
+ this.ytPlayers[videoId].player = evt.target;
+ },
+
+ onPlayerStateChange: function(evt) {
+ var videoId;
+ if (evt.data && evt.data === YT.PlayerState.PLAYING) {
+ //safe clicked video as playing video and center it
+ videoId = evt.target.getVideoData().video_id;
+ if (this.playingVideos[videoId]) {
+ return;
+ }
+ this.playingVideos[videoId] = evt.target;
+ var index = this.videoIdToThumbIndex[videoId];
+ if (index !== this.shownItemIndex) {
+ this.showThumb(index);
+ }
+ } else if (evt.data === YT.PlayerState.PAUSED || evt.data === YT.PlayerState.ENDED) {
+ //remove paused video from playing videos
+ videoId = evt.target.getVideoData().video_id;
+ delete this.playingVideos[videoId];
+ }
+ },
+
+ //checks if all divs in the contentSlider are ready
+ thumbLoaded: function() {
+ this._loadedThumbsCount++;
+ if (this._loadedThumbsCount === this.preparedItems.length) {
+ this.onAllThumbsLoaded();
+ }
+ },
+
+ onAllThumbsLoaded: function() {
+ this.allItemsLoaded = true;
+
+ //calc offsets for all thumbs in the small view
+ this.defaultThumbOffsets = [];
+ var offset = 0;
+ for (var i = 0; i < this.itemNodes.length; i++) {
+ this.defaultThumbOffsets.push(offset);
+ offset += this.defaultThumbWidths[i];
+ }
+
+ this._handleFirstResize();
+ },
+
+ //waits till the gallery is visible in the domTree
+ //can be cancelled with _stopFirstResize=true (added for use in TitlePane)
+ _handleFirstResize: function() {
+ //avoid the 200ms interval if possible
+ if (domGeometry.getMarginBox(this.domNode).w !== 0) {
+ this._handleResize();
+ } else {
+ this._firstResizeInterval = setInterval(lang.hitch(this, function() {
+ if (this._stopFirstResize) {
+ clearInterval(this._firstResizeInterval);
+ } else if (domGeometry.getMarginBox(this.domNode).w !== 0) {
+ clearInterval(this._firstResizeInterval);
+ this._handleResize();
+ }
+ }), 200);
+ }
+ },
+
+ _updateHeighestImage: function(img) {
+ if (img.naturalHeight > this.heighestImg.h) {
+ this.heighestImg.w = img.naturalWidth;
+ this.heighestImg.h = img.naturalHeight;
+ this.heighestImgRatio = img.naturalWidth / img.naturalHeight;
+ }
+ },
+
+ _updateWidestThumb: function(width) {
+ if (width > this.widestDefaultThumbWidth) {
+ this.widestDefaultThumbWidth = width;
+ }
+ },
+
+ resizeCarousel: function() {
+ if (domGeometry.getMarginBox(this.domNode).w === 0) {
+ return;
+ }
+
+ //calc all values needed for toggling thumbsize and showing items
+ this._galleryWidth = domGeometry.getMarginBox(this.domNode).w;
+ this._leftNavButtonWidth = domGeometry.getMarginBox(this.leftButton).w;
+ this._rightNavButtonWidth = domGeometry.getMarginBox(this.rightButton).w;
+ this._widthForThumbs = this._galleryWidth - (this._leftNavButtonWidth + this._rightNavButtonWidth);
+
+ this._totalWidthOfSmallThumbs = 0;
+ for (var i = 0; i < this.itemNodes.length; i++) {
+ thumbWidth = this.defaultThumbWidths[i];
+ this._totalWidthOfSmallThumbs += thumbWidth;
+ }
+
+ this._smallMaxOffset = Math.max(this._totalWidthOfSmallThumbs - this._widthForThumbs, 0);
+ this._bigThumbDimensions = this._getMaxWidthHeightForBigThumbs();
+ var _bigContentSliderWidth = this.itemNodes.length * (this._bigThumbDimensions.maxWidthForThumb + this._galleryThumbMarginExtents);
+ this._bigMaxOffset = _bigContentSliderWidth - this._widthForThumbs;
+ this._smallViewHasNavButtons = this._totalWidthOfSmallThumbs > this._galleryWidth;
+
+
+ if (this.isBigThumbnails) {
+ this._scrollToTopOfGalleryWidget();
+ }
+
+ //reshow the current thumb with new dimensions
+ setTimeout(lang.hitch(this, function() {
+ this.showThumb(this.shownItemIndex);
+ }), 0);
+ },
+
+ _toggleNavButtonsVisibility: function(newOffset) {
+ if (this.isBigThumbnails || this._smallViewHasNavButtons) {
+ domClass.toggle(this.leftButton, 'disabled', (this.contentSliderOffset === 0 || this.shownItemIndex === 0));
+ domClass.toggle(this.rightButton, 'disabled', ((this.isBigThumbnails && this.contentSliderOffset === this.defaultThumbOffsets[this.itemNodes.length-1]) || (!this.isBigThumbnails && this.contentSliderOffset === this._smallMaxOffset) || this.shownItemIndex === this.itemNodes.length - 1));
+ } else {
+ domClass.add(this.leftButton, 'disabled');
+ domClass.add(this.rightButton, 'disabled');
+ }
+ },
+
+ showThumb: function(thumbIndex) {
+ if (thumbIndex < 0 || thumbIndex >= this.itemNodes.length) {
+ return;
+ }
+
+ var newOffsetIgnoringNavButtons = 0;
+ var newOffsetWithNavButtons = 0;
+
+ if (this.isBigThumbnails) {
+ ////for big thumbnails
+ this._hideVideosTemporarily(this._baseTransitionDuration + 50);
+
+ var targetThumb = this.itemNodes[thumbIndex];
+
+ //revert previously enlarged thumb and enlarge new thumb
+ query('.galleryThumb.enlarged', this.contentSlider.domNode).style('width', '').style('height', '').removeClass('enlarged');
+ ////_width
+ domStyle.set(targetThumb, 'width', lang.replace('{0}px', [this._bigThumbDimensions.maxWidthForThumb]));
+ ////_height
+ var targetThumbIsVideo = domClass.contains(targetThumb, 'galleryVideoThumb');
+ var maxHeight = targetThumbIsVideo ? this._bigThumbDimensions.maxHeightForVideoThumb : this._bigThumbDimensions.maxHeightForImgThumb;
+ domClass.add(targetThumb, 'enlarged');
+ domStyle.set(targetThumb, 'height', maxHeight + 'px');
+
+ //get new offset
+ newOffsetIgnoringNavButtons = this.defaultThumbOffsets[thumbIndex];
+ newOffsetWithNavButtons = newOffsetIgnoringNavButtons - this._leftNavButtonWidth;
+ } else {
+ ////for small thumbnails
+ if (!this._smallViewHasNavButtons) {
+ newOffsetIgnoringNavButtons = -((this._widthForThumbs - this._totalWidthOfSmallThumbs) / 2) - domGeometry.getMarginBox(this.leftButton).w;
+ newOffsetWithNavButtons = newOffsetIgnoringNavButtons;
+ } else {
+ //offset for target thumb so that it is just visible
+ newOffsetIgnoringNavButtons = this.defaultThumbOffsets[thumbIndex];
+
+ //calc offset so that targetThumb is centered
+ var targetThumbWidth = this.defaultThumbWidths[thumbIndex];
+ var newOffsetCentered = newOffsetIgnoringNavButtons - ((this._widthForThumbs - targetThumbWidth) / 2);
+
+ //if the centered offset would show blank parts of the contentSlider revert to 0 or maxOffset
+ //if the centerd thumb would only be 50px away from 0 or maxOffset set them to 0 or maxOffset
+ if (newOffsetCentered <= (this._galleryThumbMarginExtents + 30) ) {
+ newOffsetIgnoringNavButtons = 0;
+ newOffsetWithNavButtons = newOffsetIgnoringNavButtons;
+ } else if (newOffsetCentered + (this._galleryThumbMarginExtents + 30) >= this._smallMaxOffset) {
+ newOffsetIgnoringNavButtons = this._smallMaxOffset;
+ newOffsetWithNavButtons = this._smallMaxOffset - this._leftNavButtonWidth - this._rightNavButtonWidth;
+ } else {
+ newOffsetIgnoringNavButtons = newOffsetCentered;
+ newOffsetWithNavButtons = newOffsetIgnoringNavButtons - this._leftNavButtonWidth;
+ }
+ }
+ }
+
+ //set new offset and thumbIndex
+ this.shownItemIndex = thumbIndex || 0;
+ domStyle.set(this.contentSlider.domNode, 'transform', lang.replace('translate3d({0}px, 0, 0)', [(newOffsetWithNavButtons * (-1))]));
+ domStyle.set(this.contentSlider.domNode, '-webkit-transform', lang.replace('translate3d({0}px, 0, 0)', [(newOffsetWithNavButtons * (-1))]));
+ this.contentSliderOffset = newOffsetIgnoringNavButtons;
+ this._visibleOffset = newOffsetWithNavButtons;
+
+ this._toggleNavButtonsVisibility();
+ },
+
+ showNextThumbs: function() {
+ maxOffset = this.isBigThumbnails ? this._bigMaxOffset : this._smallMaxOffset;
+ if (this.contentSliderOffset === maxOffset || this.shownItemIndex === this.itemNodes.length - 1) {
+ return;
+ }
+
+ //define functions
+ _calcCurrentFullyVisibleThumbs = lang.hitch(this, function() {
+ var lastFullyVisibleThumbIndex = 0;
+ var totalThumbsWidth = 0;
+
+ var tmpWidth = 0;
+ var _widthForThumbs = this.shownItemIndex === 0 ? this._widthForThumbs + this._leftNavButtonWidth : this._widthForThumbs;
+ for (var tmpIndex = 0; tmpIndex <= this.itemNodes.length -1; ++tmpIndex) {
+ tmpWidth += this.defaultThumbWidths[tmpIndex];
+
+ isCurrentThumbFullyVisible = (tmpWidth - this._galleryThumbLeftRightMargin) <= (_widthForThumbs + this.contentSliderOffset);
+ if (!isCurrentThumbFullyVisible) {
+ break;
+ }
+ totalThumbsWidth = tmpWidth;
+ lastFullyVisibleThumbIndex = tmpIndex;
+ }
+
+ return {
+ lastIndex: lastFullyVisibleThumbIndex,
+ width: totalThumbsWidth
+ };
+ });
+
+ _calcNewFullyVisibleThumbs = lang.hitch(this, function(currThumbs) {
+ var lastFullyVisibleThumbIndex = currThumbs.lastIndex + 1;
+ var totalThumbsWidth = 0;
+
+ var tmpWidth = 0;
+ var _widthForThumbs = this._widthForThumbs;
+ for (var tmpIndex = lastFullyVisibleThumbIndex; tmpIndex <= this.itemNodes.length -1; ++tmpIndex) {
+ tmpWidth += this.defaultThumbWidths[tmpIndex];
+
+ if (tmpIndex === this.itemNodes.length-1) {
+ _widthForThumbs += this._leftNavButtonWidth;
+ }
+ isCurrentThumbFullyVisible = tmpWidth < _widthForThumbs;
+ if (!isCurrentThumbFullyVisible) {
+ break;
+ }
+ totalThumbsWidth = tmpWidth;
+ lastFullyVisibleThumbIndex = tmpIndex;
+ }
+
+ return {
+ lastIndex: lastFullyVisibleThumbIndex,
+ width: totalThumbsWidth
+ };
+ });
+
+ _calcCenteringOffset = lang.hitch(this, function(currentThumbs, newThumbs) {
+ //calc new offset so that all new thumbs are centered
+ var offsetCentered = currentThumbs.width - ((this._widthForThumbs - newThumbs.width) / 2);
+
+ //make sure to not exceed the maxOffset
+ var offset = Math.min(offsetCentered, maxOffset);
+
+ return offset;
+ });
+
+
+ //start
+ this.pauseAllVideos();
+
+ if (this.isBigThumbnails) {
+ ////for big thumbs
+ this.showThumb(this.shownItemIndex + 1);
+ } else {
+ ////for small thumbs
+ var newOffsetIgnoringNavButtons = 0;
+ var newIndex;
+
+ //calc new offset and index
+ var isGalleryTooSmall = !this.isBigThumbnails && this.widestDefaultThumbWidth + this._galleryThumbMarginExtents >= this._widthForThumbs;
+ if (isGalleryTooSmall) {
+ newIndex = this.shownItemIndex + 1;
+ var thumbOffset = this.defaultThumbOffsets[newIndex];
+ newOffsetIgnoringNavButtons = Math.min(thumbOffset, this._smallMaxOffset);
+ } else {
+ var isCurrentThumbFullyVisible;
+ var currentFullyVisibleThumbs = _calcCurrentFullyVisibleThumbs();
+ var newFullyVisibleThumbs = _calcNewFullyVisibleThumbs(currentFullyVisibleThumbs);
+
+ newOffsetIgnoringNavButtons = _calcCenteringOffset(currentFullyVisibleThumbs, newFullyVisibleThumbs);
+ newIndex = newFullyVisibleThumbs.lastIndex;
+ }
+
+ //set the new offset and index
+ var newOffsetWithNavButtons = newIndex === this.itemNodes.length -1 ? newOffsetIgnoringNavButtons - ( this._leftNavButtonWidth * 2) : newOffsetIgnoringNavButtons - this._leftNavButtonWidth;
+ var _oldOffset = this._visibleOffset;
+
+ //set transition duration based on distance
+ this._setSliderTransitionDuration(newOffsetWithNavButtons, _oldOffset);
+
+ domStyle.set(this.contentSlider.domNode, 'transform', lang.replace('translate3d({0}px, 0, 0)', [newOffsetWithNavButtons * (-1)]));
+ domStyle.set(this.contentSlider.domNode, '-webkit-transform', lang.replace('translate3d({0}px, 0, 0)', [(newOffsetWithNavButtons * (-1))]));
+ this.contentSliderOffset = newOffsetIgnoringNavButtons;
+ this._visibleOffset = newOffsetWithNavButtons;
+ this.shownItemIndex = newIndex;
+
+ this._toggleNavButtonsVisibility();
+ }
+ },
+
+ showPrevThumbs: function() {
+ if (this.contentSliderOffset === 0 || this.shownItemIndex === 0) {
+ return;
+ }
+
+ //define functions
+ var _findFirstFullyVisibleThumb = lang.hitch(this, function() {
+ var totalWidth = 0;
+ for (var ithumb = 0; ithumb <= this.itemNodes.length -1; ++ithumb) {
+ totalWidth += this.defaultThumbWidths[ithumb];
+
+ var isThumbFullyVisible = totalWidth - this.contentSliderOffset >= this.defaultThumbWidths[ithumb] - this._galleryThumbMarginExtents;
+ if (isThumbFullyVisible) {
+ break;
+ }
+ }
+ return ithumb;
+ });
+
+ var _findNextFullyVisibleThumbs = lang.hitch(this, function(ifirstThumb) {
+ var firstFullyVisibleThumbIndex = ifirstThumb -1;
+ var totalThumbsWidth = 0;
+
+ var tmpWidth = 0;
+ var _widthForThumbs = this._widthForThumbs;
+ for (var ithumb = firstFullyVisibleThumbIndex; ithumb >= 0; --ithumb) {
+ tmpWidth += this.defaultThumbWidths[ithumb];
+
+ if (ithumb === 0) {
+ _widthForThumbs += this._leftNavButtonWidth;
+ }
+ isCurrentThumbFullyVisible = tmpWidth < _widthForThumbs;
+ if (!isCurrentThumbFullyVisible) {
+ break;
+ }
+ totalThumbsWidth = tmpWidth;
+ firstFullyVisibleThumbIndex = ithumb;
+ }
+ return {
+ width: totalThumbsWidth,
+ firstIndex: firstFullyVisibleThumbIndex
+ };
+ });
+
+ var _calcCenteringOffset = lang.hitch(this, function(newThumbs) {
+ var offsetUntilNewThumbs = this.defaultThumbOffsets[newThumbs.firstIndex];
+ var newOffsetCentered = offsetUntilNewThumbs - ((this._widthForThumbs - newThumbs.width) / 2);
+
+ //make sure to not go lower than 0
+ return Math.max(newOffsetCentered, 0);
+ });
+
+
+ //start
+ this.pauseAllVideos();
+
+ if (this.isBigThumbnails) {
+ this.showThumb(this.shownItemIndex - 1);
+ } else {
+ ////for small thumbs
+ var newOffsetIgnoringNavButtons;
+ var newIndex;
+
+ //calc new offset and index
+ var isGalleryTooSmall = !this.isBigThumbnails && this.widestDefaultThumbWidth + this._galleryThumbMarginExtents >= this._widthForThumbs;
+ if (isGalleryTooSmall) {
+ newIndex = this.shownItemIndex-1;
+ newOffsetIgnoringNavButtons = this.defaultThumbOffsets[newIndex];
+ } else {
+ var isCurrentThumbFullyVisible;
+ var currFirstFullyVisibleThumbIndex = _findFirstFullyVisibleThumb();
+ var newFullyVisibleThumbs = _findNextFullyVisibleThumbs(currFirstFullyVisibleThumbIndex);
+
+ newOffsetIgnoringNavButtons = _calcCenteringOffset(newFullyVisibleThumbs);
+ newIndex = newFullyVisibleThumbs.firstIndex;
+ }
+
+ //set the new offset and index
+ var newOffsetWithNavButtons = newIndex === 0 ? newOffsetIgnoringNavButtons : newOffsetIgnoringNavButtons - this._leftNavButtonWidth;
+ var _oldOffset = this._visibleOffset;
+
+ //set transition duration based on distance
+ this._setSliderTransitionDuration(newOffsetWithNavButtons, _oldOffset);
+
+ domStyle.set(this.contentSlider.domNode, 'transform', lang.replace('translate3d({0}px, 0, 0)', [(newOffsetWithNavButtons * (-1))]));
+ domStyle.set(this.contentSlider.domNode, '-webkit-transform', lang.replace('translate3d({0}px, 0, 0)', [(newOffsetWithNavButtons * (-1))]));
+ this.contentSliderOffset = newOffsetIgnoringNavButtons;
+ this._visibleOffset = newOffsetWithNavButtons;
+ this.shownItemIndex = newIndex;
+
+ this._toggleNavButtonsVisibility();
+ }
+ },
+
+ _setSliderTransitionDuration: function(newOffset, oldOffset) {
+ var offsetDistance = Math.abs(oldOffset - newOffset);
+ var isNewTransitionDuration = offsetDistance > this._baseTransitionDuration;
+ if (isNewTransitionDuration) {
+ domStyle.set(this.contentSlider.domNode, 'transition-duration', lang.replace('{0}ms', [offsetDistance]));
+ setTimeout(lang.hitch(this, function() {
+ domStyle.set(this.contentSlider.domNode, 'transition-duration', '');
+ }), offsetDistance);
+ }
+ },
+
+ _hideVideosTemporarily: function(duration) {
+ //hide loaded youtube iframes during resize for performance reasons
+ query('.galleryVideoThumb.loaded .galleryVideoThumbnailWrapper', this.contentSlider.domNode).removeClass('dijitHidden');
+ query('.galleryVideoThumb.loaded .galleryVideo', this.contentSlider.domNode).addClass('dijitHidden');
+ setTimeout(lang.hitch(this, function() {
+ query('.galleryVideoThumb.loaded .galleryVideo', this.contentSlider.domNode).removeClass('dijitHidden');
+ query('.galleryVideoThumb.loaded .galleryVideoThumbnailWrapper', this.contentSlider.domNode).addClass('dijitHidden');
+ }), duration);
+ },
+
+ toggleThumbSize: function(newIndex) {
+ newIndex = (newIndex === undefined) ? this.shownItemIndex : newIndex;
+
+ if (this.isBigThumbnails) {
+ this._toggleToSmallThumbs();
+ } else {
+ this._toggleToBigThumbs(newIndex);
+ }
+ domClass.toggle(this.scaleButton, 'minimize');
+
+ this.isBigThumbnails = !this.isBigThumbnails;
+ this.showThumb(newIndex);
+ },
+
+ _toggleToSmallThumbs: function() {
+ this._hideVideosTemporarily(this._baseTransitionDuration + 50);
+ //remove height and width for thumbs and navButtons
+ query('.galleryNavButton', this.outerContainer.domNode).style('height', '');
+ query('.galleryThumb', this.contentSlider.domNode).style('width', '').style('height', '');
+ },
+
+ _toggleToBigThumbs: function(newIndex) {
+ this.pauseAllVideos(newIndex);
+
+ this._scrollToTopOfGalleryWidget();
+
+ //enlarge navButtons and VerticalAlignHelperThumb
+ var maxHeight = Math.max(this._bigThumbDimensions.maxHeightForImgThumb, this._bigThumbDimensions.maxHeightForVideoThumb);
+ query('.galleryNavButton', this.outerContainer.domNode).style('height', lang.replace('{0}px', [maxHeight]));
+ domStyle.set(this.galleryThumbVerticalAlignHelper, 'height', lang.replace('{0}px', [maxHeight]));
+ },
+
+ _scrollToTopOfGalleryWidget: function() {
+ //##scroll to the top of the galleryWidget
+ var scrollTarget = domGeometry.position(this.domNode, true).y - 50;
+ var scrollInterval = setInterval(lang.hitch(this, function() {
+ window.scrollTo(0, scrollTarget);
+ }), 1);
+
+ //wait for the transition to finish
+ setTimeout(function() {
+ clearInterval(scrollInterval);
+ }, this._baseTransitionDuration + 100);
+ },
+
+ _getMaxWidthHeightForBigThumbs: function() {
+ //##get max width for a big thumb
+ var maxWidthForThumb = this._widthForThumbs - this._galleryThumbMarginExtents;
+
+ //##get the max height for a big thumb
+ var maxHeight = dojo.window.getBox().h - 100;
+ maxHeight = (maxHeight < this.defaultThumbHeight) ? this.defaultThumbHeight : maxHeight;
+ var maxHeightForImgThumb = Math.min(maxWidthForThumb / this.heighestImgRatio, maxHeight, this.heighestImg.h);
+ var maxHeightForVideoThumb = maxWidthForThumb / (this._youtubeIframeRatio);
+
+ return {
+ maxWidthForThumb: maxWidthForThumb,
+ maxHeightForImgThumb: maxHeightForImgThumb,
+ maxHeightForVideoThumb: maxHeightForVideoThumb
+ };
+ },
+
+ _handleResize: function() {
+ if (domGeometry.getMarginBox(this.domNode).w === 0) {
+ return;
+ }
+ if (this._resizeDeferred && !this._resizeDeferred.isFulfilled()) {
+ this._resizeDeferred.cancel();
+ }
+
+ this._resizeDeferred = tools.defer(lang.hitch(this, function() {
+ this.resizeCarousel();
+
+ this._removeLoadingScreen();
+ }), 200);
+
+ this._resizeDeferred.otherwise(function() { /* prevent logging of exception */ });
+ },
+
+ _removeLoadingScreen: function() {
+ if (!this.loaded) {
+ //fade the loadingAnimationOverlay out over given transition duration
+ var transitionDuration = 600;
+ domStyle.set(this.galleryLoadingOverlay, 'transition', lang.replace('opacity {0}ms', [transitionDuration]));
+ domClass.add(this.galleryLoadingOverlay, 'loaded');
+ domClass.add(this.contentSlider.domNode, 'noTransition');
+ domStyle.set(this.contentSlider.domNode, 'transform', lang.replace('translate3d({0}px, 0, 0)', [this._leftNavButtonWidth]));
+ domStyle.set(this.contentSlider.domNode, '-webkit-transform', lang.replace('translate3d({0}px, 0, 0)', [this._leftNavButtonWidth]));
+
+ //hide the loading overlay after transition duration
+ tools.defer(lang.hitch(this, function() {
+ domClass.add(this.galleryLoadingOverlay, 'dijitHidden');
+ domClass.remove(this.contentSlider.domNode, 'noTransition');
+ }), transitionDuration);
+
+ //remove styles needed for loading overlay
+ domStyle.set(this.domNode, 'height', '');
+ domStyle.set(this.domNode, 'overflow', '');
+
+ domClass.remove(this.scaleButton, 'dijitHidden');
+ this.loaded = true;
+ }
+ },
+
+ startup: function() {
+ this.inherited(arguments);
+ this.own(on(baseWin.doc, 'resize', lang.hitch(this, '_handleResize')));
+ this.own(on(kernel.global, 'resize', lang.hitch(this, '_handleResize')));
+ },
+
+ destroy: function() {
+ this.inherited(arguments);
+
+ //remove all inserted CssRules
+ array.forEach(this._insertedCssRules, function(rule) {
+ styles.removeCssRule(rule.selector, rule.declaration);
+ });
+ },
+
+ pauseAllVideos: function(pauseExceptionIndex) {
+ if (Object.keys(this.playingVideos).length === 0) {
+ return;
+ }
+
+ //pause all playing videos
+ //if a pauseExceptionIndex is given do not pause that video if it is playing
+ tools.forIn(this.playingVideos, lang.hitch(this, function(_videoId, player) {
+ if (this.videoIdToThumbIndex[_videoId] !== pauseExceptionIndex) {
+ player.pauseVideo();
+ }
+ }));
+ },
+ });
+});
Index: umc/js/appcenter/thumbnailError.svg
===================================================================
--- umc/js/appcenter/thumbnailError.svg (Revision 0)
+++ umc/js/appcenter/thumbnailError.svg (Arbeitskopie)
@@ -0,0 +1 @@
+
\ No newline at end of file
Index: umc/js/appcenter/videoPlayButton.svg
===================================================================
--- umc/js/appcenter/videoPlayButton.svg (Revision 0)
+++ umc/js/appcenter/videoPlayButton.svg (Arbeitskopie)
@@ -0,0 +1,63 @@
+
+
Index: umc/js/appcenter.styl
===================================================================
--- umc/js/appcenter.styl (Revision 67996)
+++ umc/js/appcenter.styl (Arbeitskopie)
@@ -445,61 +445,116 @@
@media screen and (max-width: 549px)
width: 100%
- .umcCarouselWidget
+ .umcThumbnailGallery
position: relative
+ line-height: 0
- .carouselOuterContainer
- margin: auto
+ .galleryLoadingOverlay
+ position: absolute
+ width: 100%
+ height: 100%
+ z-index: 1
+ background-image: url(../../dijit/themes/umc/images/standbyAnimation.svg)
+ background-repeat: no-repeat
+ background-position: center center
+ background-color: #f0f0f0
+ opacity: 1
+
+ &.loaded
+ opacity: 0
+ pointer-events: none
+
+ .galleryOuterContainer
position: relative
- transition: height 0.5s
.contentSliderWrapper
- float: left
+ display: inline-block
+
white-space: nowrap
overflow: hidden
- position: relative
- height: 100%
+ width: 100%
.contentSlider
- position: absolute
- left: 0
- transition: left 0.5s
+ display: inline-block
+ transform: translate3d(0, 0, 0)
+ transition-property: transform
- div
- margin: 0 10px
+ &.noTransition
+ transition: none
+
+ .galleryThumb
display: inline-block
- /*border: 2px solid darkgrey;*/
- /*box-sizing: content-box;*/
+ vertical-align: middle
+ transition-property: width, height
+ &.galleryImgThumb
+ background-size: contain
+ background-position: center center
+ background-repeat: no-repeat
+ cursor: pointer
- &.noTransition
- transition: none !important
+ &.brokenImg
+ width: 150px
+ box-shadow: inset 0px 0px 6px #5a5a5a
+ background: url("appcenter/thumbnailError.svg") no-repeat, #e6e6e6
+ background-position: center center
+ background-size: 30% auto
- .carouselScreenshot
- cursor: pointer
- display: block
- margin: auto
+ &.verticalAlignHelper
+ margin: 0 !important
+ height: 0
+
+ .galleryVideoThumb
position: relative
- top: 50%
- transform: translateY(-50%)
- -ms-transform: translateY(-50%)
- -webkit-transform: translateY(-50%)
- transition: max-height 0.5s, max-width 0.5s
- .carouselButton
- float: left
+ .galleryVideo
+ width: 100%
+ height: 100%
+ background-color: black
+ .galleryVideoThumbnailWrapper
+ width: 100%
+ height: 100%
+
+ .galleryVideoThumbnail
+ width: 100%
+ height: 100%
+ background-size: cover
+ background-position: center center
+ background-repeat: no-repeat
+ cursor: pointer
+
+ .galleryVideoPlayButtonIcon
+ width: 100%
+ height: 100%
+ background: url("appcenter/videoPlayButton.svg") no-repeat;
+ background-size: auto 30%
+ background-position: center center
+ position: absolute
+ top: 0
+
+ &:after
+ content: ''
+ height: 100%
+ display: inline-block
+ vertical-align: middle
+
+ .galleryNavButton
cursor: pointer
width: 40px
- height: 100%
opacity: 1
- transition: opacity 0.5s
+ transition-property: opacity, height
+ position: absolute
+ z-index: 1
+ background-color: #f0f0f0
&.disabled
opacity: 0
cursor: default
+ pointer-events: none
+ height: 0
- .carouselButtonImage
+ .galleryNavButtonImage
background-image: url("appcenter/carouselArrowRight.svg")
background-size: contain
background-repeat: no-repeat
@@ -511,13 +566,15 @@
&:hover
opacity: 1
- &.rightCarouselButton
+ &.rightGalleryNavButton
margin-left: 5px
+ right: 0
+ top: 0
- &.leftCarouselButton
+ &.leftGalleryNavButton
margin-right: 5px
- .carouselButtonImage
+ .galleryNavButtonImage
transform: rotateY(180deg)
-ms-transform: rotateY(180deg)
-webkit-transform: rotateY(180deg)