{"version":3,"file":"mobile.mediaViewer.js","mappings":"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","sources":["webpack://mfModules/./src/mobile.mediaViewer/ImageCarousel.js","webpack://mfModules/./src/mobile.mediaViewer/ImageGateway.js","webpack://mfModules/./src/mobile.mediaViewer/LoadErrorMessage.js","webpack://mfModules/./src/mobile.mediaViewer/mobile.mediaViewer.js"],"sourcesContent":["const View = require( '../mobile.startup/View' ),\n\tutil = require( '../mobile.startup/util' ),\n\tIconButton = require( '../mobile.startup/IconButton' ),\n\ticons = require( '../mobile.startup/icons' ),\n\teventBus = require( '../mobile.startup/eventBusSingleton' ),\n\tButton = require( '../mobile.startup/Button' ),\n\tdetailsButton = new Button( {\n\t\tlabel: mw.msg( 'mobile-frontend-media-details' ),\n\t\tadditionalClassNames: 'button',\n\t\tprogressive: true\n\t} ),\n\tslideLeftButton = new IconButton( {\n\t\trotation: 90,\n\t\ticon: 'expand-invert',\n\t\tlabel: mw.msg( 'mobile-frontend-media-prev' )\n\t} ),\n\tslideRightButton = new IconButton( {\n\t\trotation: -90,\n\t\ticon: 'expand-invert',\n\t\tlabel: mw.msg( 'mobile-frontend-media-next' )\n\t} ),\n\tLoadErrorMessage = require( './LoadErrorMessage' ),\n\tImageGateway = require( './ImageGateway' ),\n\trouter = __non_webpack_require__( 'mediawiki.router' );\n\n/**\n * Displays images in full screen overlay\n *\n * @private\n */\nclass ImageCarousel extends View {\n\t/**\n\t * @param {Object} options Configuration options, see Overlay#defaults\n\t */\n\tconstructor( options ) {\n\t\tsuper(\n\t\t\tutil.extend(\n\t\t\t\t{\n\t\t\t\t\tclassName: 'image-carousel',\n\t\t\t\t\tevents: {\n\t\t\t\t\t\t'click .image-wrapper': 'onToggleDetails',\n\t\t\t\t\t\t// Click tracking for table of contents so we can see if people interact with it\n\t\t\t\t\t\t'click .slider-button': 'onSlide'\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t\toptions\n\t\t\t)\n\t\t);\n\t}\n\n\tinitialize( options ) {\n\t\tthis.gateway = options.gateway || new ImageGateway( {\n\t\t\tapi: options.api\n\t\t} );\n\t\tthis.router = options.router || router;\n\t\tthis.eventBus = options.eventBus;\n\t\tthis.hasLoadError = false;\n\t\tsuper.initialize( options );\n\t}\n\n\tget template() {\n\t\treturn util.template( `\n<button title=\"{{prevMsg}}\" class=\"prev slider-button\"></button>\n<div class=\"main\">\n\t<div class=\"image-wrapper\">\n\t\t<div class=\"image\"></div>\n\t</div>\n\t<!-- cancel button will go here -->\n\t<div class=\"image-details\">\n\t\t<!-- details button will go here -->\n\t\t<p class=\"truncated-text\">{{caption}}</p>\n\t\t<p class=\"license\"><a href=\"#\">{{licenseLinkMsg}}</a></p>\n\t</div>\n</div>\n<button title=\"{{nextMsg}}\" class=\"next slider-button\"></button>\n\t` );\n\t}\n\n\t/**\n\t * @mixes Overlay#defaults\n\t * @property {Object} defaults Default options hash.\n\t * @property {mw.Api} defaults.api instance of API to use\n\t * @property {string} defaults.licenseLinkMsg Link to license information in media viewer.\n\t * @property {string} defaults.prevMsg Title for \"prev\" button in media viewer.\n\t * @property {string} defaults.nextMsg Title for \"next\" button in media viewer.\n\t * @property {Thumbnail[]} defaults.thumbnails a list of thumbnails to browse\n\t */\n\tget defaults() {\n\t\treturn util.extend( {}, super.defaults, {\n\t\t\tlicenseLinkMsg: mw.msg( 'mobile-frontend-media-license-link' ),\n\t\t\tprevMsg: mw.msg( 'mobile-frontend-media-prev' ),\n\t\t\tnextMsg: mw.msg( 'mobile-frontend-media-next' ),\n\t\t\tthumbnails: []\n\t\t} );\n\t}\n\n\t/**\n\t * Event handler for slide event\n\t *\n\t * @param {jQuery.Event} ev\n\t */\n\tonSlide( ev ) {\n\t\tconst\n\t\t\tnextThumbnail = this.$el.find( ev.target ).closest( '.slider-button' ).data( 'thumbnail' ),\n\t\t\ttitle = nextThumbnail.options.filename;\n\n\t\tthis.router.navigateTo( null, {\n\t\t\tpath: '#/media/' + title,\n\t\t\tuseReplaceState: true\n\t\t} );\n\t\tthis.options.title = nextThumbnail.options.filename;\n\t\tconst newImageCarousel = new ImageCarousel( this.options );\n\t\tthis.$el.replaceWith( newImageCarousel.$el );\n\t\tthis.$el = newImageCarousel.$el;\n\t}\n\n\t/**\n\t * @inheritdoc\n\t */\n\tpreRender() {\n\t\tthis.options.thumbnails.forEach( ( thumbnail, i ) => {\n\t\t\tif ( thumbnail.getFileName() === this.options.title ) {\n\t\t\t\tthis.options.caption = thumbnail.getDescription();\n\t\t\t\tthis.galleryOffset = i;\n\t\t\t}\n\t\t} );\n\t}\n\n\t/**\n\t * Setup the next and previous images to enable the user to arrow through\n\t * all images in the set of images given in thumbs.\n\t *\n\t * @param {Array} thumbs A set of images, which are available\n\t * @private\n\t */\n\t_enableArrowImages( thumbs ) {\n\t\tconst offset = this.galleryOffset;\n\t\tlet lastThumb, nextThumb;\n\n\t\tif ( this.galleryOffset === undefined ) {\n\t\t\t// couldn't find a suitable matching thumbnail so make\n\t\t\t// next slide start at beginning and previous slide be end\n\t\t\tlastThumb = thumbs[thumbs.length - 1];\n\t\t\tnextThumb = thumbs[0];\n\t\t} else {\n\t\t\t// identify last thumbnail\n\t\t\tlastThumb = thumbs[ offset === 0 ? thumbs.length - 1 : offset - 1 ];\n\t\t\tnextThumb = thumbs[ offset === thumbs.length - 1 ? 0 : offset + 1 ];\n\t\t}\n\n\t\tthis.$el.find( '.prev' ).data( 'thumbnail', lastThumb );\n\t\tthis.$el.find( '.next' ).data( 'thumbnail', nextThumb );\n\t}\n\n\t/**\n\t * Disables the possibility to arrow through all images of the page.\n\t *\n\t * @private\n\t */\n\t_disableArrowImages() {\n\t\tthis.$el.find( '.prev, .next' ).remove();\n\t}\n\n\t/**\n\t * Handler for retry event which triggers when user tries to reload overlay\n\t * after a loading error.\n\t *\n\t * @private\n\t */\n\t_handleRetry() {\n\t\t// A hacky way to simulate a reload of the overlay\n\t\tthis.router.emit( 'hashchange' );\n\t}\n\n\t/**\n\t * @inheritdoc\n\t */\n\tpostRender() {\n\t\tlet\n\t\t\t$img;\n\t\tconst\n\t\t\t$el = this.$el,\n\t\t\t$spinner = icons.spinner().$el,\n\t\t\tthumbs = this.options.thumbnails || [];\n\n\t\t/**\n\t\t * Display media load failure message\n\t\t *\n\t\t * @method\n\t\t * @ignore\n\t\t */\n\t\tconst showLoadFailMsg = () => {\n\t\t\tthis.hasLoadError = true;\n\n\t\t\t$spinner.hide();\n\t\t\t// hide broken image if present\n\t\t\t$el.find( '.image img' ).hide();\n\n\t\t\t// show error message if not visible already\n\t\t\tif ( $el.find( '.load-fail-msg' ).length === 0 ) {\n\t\t\t\tnew LoadErrorMessage( { retryPath: this.router.getPath() } )\n\t\t\t\t\t.on( 'retry', () => this._handleRetry() )\n\t\t\t\t\t.prependTo( $el.find( '.image' ) );\n\t\t\t}\n\t\t};\n\n\t\t/**\n\t\t * Start image load transitions\n\t\t *\n\t\t * @method\n\t\t * @ignore\n\t\t */\n\t\tconst addImageLoadClass = () => {\n\t\t\t$img.addClass( 'image-loaded' );\n\t\t};\n\n\t\tif ( thumbs.length < 2 ) {\n\t\t\tthis._disableArrowImages();\n\t\t} else {\n\t\t\tthis._enableArrowImages( thumbs );\n\t\t}\n\n\t\tthis.$details = $el.find( '.image-details' );\n\t\t$el.find( '.image' ).append( $spinner );\n\n\t\tthis.$details.prepend( detailsButton.$el );\n\n\t\tthis.gateway.getThumb( this.options.title ).then( ( data ) => {\n\t\t\tlet author;\n\t\t\tconst url = data.descriptionurl + '#mw-jump-to-license';\n\n\t\t\t$spinner.hide();\n\n\t\t\tthis.thumbWidth = data.thumbwidth;\n\t\t\tthis.thumbHeight = data.thumbheight;\n\t\t\tthis.imgRatio = data.thumbwidth / data.thumbheight;\n\n\t\t\t// We need to explicitly specify document for context param as jQuery 3\n\t\t\t// will create a new document for the element if the context is\n\t\t\t// undefined. If element is appended to active document, event handlers\n\t\t\t// can fire in both the active document and new document which can cause\n\t\t\t// insidious bugs.\n\t\t\t// (https://api.jquery.com/jquery.parsehtml/#entry-longdesc)\n\t\t\t$img = this.parseHTML( '<img>', document );\n\n\t\t\t// Remove the loader when the image is loaded or display load fail\n\t\t\t// message on failure\n\t\t\t//\n\t\t\t// Error event handler must be attached before error occurs\n\t\t\t// (https://api.jquery.com/error/#entry-longdesc)\n\t\t\t//\n\t\t\t// For the load event, it is more unclear what happens cross-browser when\n\t\t\t// the image is loaded from cache. It seems that a .complete check is\n\t\t\t// needed if attaching the load event after setting the src.\n\t\t\t// (http://stackoverflow.com/questions/910727/jquery-event-for-images-loaded#comment10616132_1110094)\n\t\t\t//\n\t\t\t// However, perhaps .complete check is not needed if attaching load\n\t\t\t// event prior to setting the image src\n\t\t\t// (https://stackoverflow.com/questions/12354865/image-onload-event-and-browser-cache#answer-12355031)\n\t\t\t$img.on( 'load', addImageLoadClass ).on( 'error', showLoadFailMsg );\n\t\t\t$img.attr( 'src', data.thumburl ).attr( 'alt', this.options.caption );\n\t\t\t$el.find( '.image' ).append( $img );\n\n\t\t\tthis.$details.addClass( 'is-visible' );\n\t\t\tthis._positionImage();\n\t\t\t$el.find( '.image-details a' ).attr( 'href', url );\n\t\t\tif ( data.extmetadata ) {\n\t\t\t\t// Add license information\n\t\t\t\tif ( data.extmetadata.LicenseShortName ) {\n\t\t\t\t\t$el.find( '.license a' )\n\t\t\t\t\t\t.text( data.extmetadata.LicenseShortName.value )\n\t\t\t\t\t\t.attr( 'href', url );\n\t\t\t\t}\n\t\t\t\t// Add author information\n\t\t\t\tif ( data.extmetadata.Artist ) {\n\t\t\t\t\t// Strip any tags\n\t\t\t\t\tauthor = data.extmetadata.Artist.value.replace( /<.*?>/g, '' );\n\t\t\t\t\t$el.find( '.license' ).prepend( author + ' &bull; ' );\n\t\t\t\t}\n\t\t\t}\n\t\t\tthis.adjustDetails();\n\t\t}, () => {\n\t\t\t// retrieving image location failed so show load fail msg\n\t\t\tshowLoadFailMsg();\n\t\t} );\n\n\t\teventBus.on( 'resize:throttled', () => this._positionImage() );\n\t\tthis._positionImage();\n\t}\n\n\t/**\n\t * Event handler that toggles the details bar.\n\t *\n\t */\n\tonToggleDetails() {\n\t\tif ( !this.hasLoadError ) {\n\t\t\tthis.$el.find( '.cancel, .slider-button' ).toggle();\n\t\t\tthis.$details.toggle();\n\t\t\tthis._positionImage();\n\t\t}\n\t}\n\n\t/**\n\t * Fit the image into the window if its dimensions are bigger than the window dimensions.\n\t * Compare window width to height ratio to that of image width to height when setting\n\t * image width or height.\n\t *\n\t * @private\n\t */\n\t_positionImage() {\n\t\tconst $window = util.getWindow();\n\n\t\tthis.adjustDetails();\n\t\t// with a hidden details box we have a little bit more space, we just need to use it\n\t\t// TODO: Get visibility from the model\n\t\t// eslint-disable-next-line no-jquery/no-sizzle\n\t\tconst detailsHeight = !this.$details.is( ':visible' ) ? 0 : this.$details.outerHeight();\n\t\tconst windowWidth = $window.width();\n\t\tconst windowHeight = $window.height() - detailsHeight;\n\t\tconst windowRatio = windowWidth / windowHeight;\n\t\tconst $img = this.$el.find( 'img' );\n\n\t\tif ( this.imgRatio > windowRatio ) {\n\t\t\tif ( windowWidth < this.thumbWidth ) {\n\t\t\t\t$img.css( {\n\t\t\t\t\twidth: windowWidth,\n\t\t\t\t\theight: 'auto'\n\t\t\t\t} );\n\t\t\t}\n\t\t} else {\n\t\t\tif ( windowHeight < this.thumbHeight ) {\n\t\t\t\t$img.css( {\n\t\t\t\t\twidth: 'auto',\n\t\t\t\t\theight: windowHeight\n\t\t\t\t} );\n\t\t\t}\n\t\t}\n\n\t\tthis.$el.find( '.image-wrapper' ).css( 'bottom', detailsHeight );\n\t\tthis.$el.find( '.slider-button.prev' ).append( slideLeftButton.$el );\n\t\tthis.$el.find( '.slider-button.next' ).append( slideRightButton.$el );\n\t}\n\n\t/**\n\t * Function to adjust the height of details section to not more than 50% of window height.\n\t *\n\t */\n\tadjustDetails() {\n\t\tconst windowHeight = util.getWindow().height();\n\t\tif ( this.$el.find( '.image-details' ).height() > windowHeight * 0.50 ) {\n\t\t\tthis.$el.find( '.image-details' ).css( 'max-height', windowHeight * 0.50 );\n\t\t}\n\t}\n}\n\nmodule.exports = ImageCarousel;\n","const sizeBuckets = [ 320, 640, 800, 1024, 1280, 1920, 2560, 2880 ],\n\tactionParams = require( './../mobile.startup/actionParams' ),\n\tutil = require( './../mobile.startup/util' );\n\n/**\n * API for retrieving image thumbnails for a given page\n */\nclass ImageGateway {\n\t/**\n\t * @param {Object} options Configuration options\n\t * @param {mw.Api} options.api\n\t * @private\n\t */\n\tconstructor( options ) {\n\t\tthis._cache = {};\n\t\tthis.api = options.api;\n\t}\n\n\t/**\n\t * Get thumbnail via the API and cache it. Return the result from the cache if exists.\n\t *\n\t * @param {string} title Url of image\n\t * @return {jQuery.Deferred} with the image info\n\t */\n\tgetThumb( title ) {\n\t\tconst cachedThumb = this._cache[title],\n\t\t\t$window = util.getWindow(),\n\t\t\timageSizeMultiplier = ( window.devicePixelRatio && window.devicePixelRatio > 1 ) ?\n\t\t\t\twindow.devicePixelRatio : 1;\n\n\t\tif ( !cachedThumb ) {\n\t\t\tthis._cache[title] = this.api.get( actionParams( {\n\t\t\t\tprop: 'imageinfo',\n\t\t\t\ttitles: title,\n\t\t\t\tiiprop: [ 'url', 'extmetadata' ],\n\t\t\t\t// request an image devicePixelRatio times bigger than the reported screen size\n\t\t\t\t// for retina displays and zooming\n\t\t\t\tiiurlwidth: ImageGateway.findSizeBucket( $window.width() * imageSizeMultiplier ),\n\t\t\t\tiiurlheight: ImageGateway.findSizeBucket( $window.height() * imageSizeMultiplier )\n\t\t\t} ) ).then( ( resp ) => {\n\t\t\t\t// imageinfo is undefined for missing pages.\n\t\t\t\tif ( resp.query && resp.query.pages &&\n\t\t\t\t\tresp.query.pages[0] && resp.query.pages[0].imageinfo ) {\n\t\t\t\t\treturn resp.query.pages[0].imageinfo[0];\n\t\t\t\t}\n\t\t\t\tthrow new Error( 'The API failed to return any pages matching the titles.' );\n\t\t\t} );\n\t\t}\n\n\t\treturn this._cache[title];\n\t}\n\n\t/**\n\t * Gets the first size larger than or equal to the provided size\n\t *\n\t * @param {number} size\n\t * @return {number}\n\t */\n\tstatic findSizeBucket( size ) {\n\t\tlet i = 0;\n\t\twhile ( size > sizeBuckets[i] && i < sizeBuckets.length - 1 ) {\n\t\t\t++i;\n\t\t}\n\t\treturn sizeBuckets[i];\n\t}\n}\n\nmodule.exports = ImageGateway;\n","const util = require( './../mobile.startup/util' ),\n\ticons = require( './../mobile.startup/icons' ),\n\tView = require( './../mobile.startup/View' );\n\n/**\n * Shows the user a load failure message\n *\n * @fires LoadErrorMessage#retry\n */\nclass LoadErrorMessage extends View {\n\t/**\n\t * @param {Object} options Configuration options\n\t * @param {string} options.retryPath path of URL to try again\n\t * @private\n\t */\n\tconstructor( options ) {\n\t\tsuper(\n\t\t\t{ events: { 'click .load-fail-msg-link a': 'onRetry' } },\n\t\t\toptions\n\t\t);\n\t}\n\n\tget template() {\n\t\treturn util.template( `\n<div class=\"load-fail-msg\">\n  <div class=\"load-fail-msg-text\">{{msgToUser}}</div>\n  <div class=\"load-fail-msg-link\">\n    <a href=\"#\">{{retryTxt}}</a>\n  </div>\n</div>\n\t` );\n\t}\n\n\tget isTemplateMode() {\n\t\treturn true;\n\t}\n\n\t/**\n\t\t* @inheritdoc\n\t\t* @cfg {Object} defaults Default options hash.\n\t\t* @cfg {string} defaults.icon HTML of the alert icon\n\t\t* @cfg {string} defaults.msgToUser Message shown when media load fails\n\t\t* @cfg {string} defaults.retryTxt Text of retry link\n\t\t*/\n\tget defaults() {\n\t\treturn util.extend( {}, super.defaults, {\n\t\t\tmsgToUser: mw.msg( 'mobile-frontend-media-load-fail-message' ),\n\t\t\tretryTxt: mw.msg( 'mobile-frontend-media-load-fail-retry' )\n\t\t} );\n\t}\n\n\t/**\n\t * @inheritdoc\n\t */\n\tpostRender() {\n\t\tthis.$el.prepend( icons.error().$el );\n\t\tthis.$el.find( '.load-fail-msg-link a' ).attr( 'href', '#' + this.options.retryPath );\n\t}\n\n\t/**\n\t * Event handler for retry event\n\t *\n\t * @param {jQuery.Event} ev\n\t * @return {boolean} Returns false to prevent default behavior for links and\n\t * stop the event from propagating\n\t */\n\tonRetry() {\n\t\t/**\n\t\t * Triggered when retry button is clicked.\n\t\t *\n\t\t * @event LoadErrorMessage#retry\n\t\t */\n\t\tthis.emit( 'retry' );\n\n\t\treturn false;\n\t}\n}\n\nmodule.exports = LoadErrorMessage;\n","const m = require( '../mobile.startup/moduleLoaderSingleton' ),\n\tImageCarousel = require( './ImageCarousel' );\n\n// Needed for lazy loading ImageCarousel\nm.define( 'mobile.mediaViewer', {\n\tImageCarousel\n} );\n"],"names":["View","util","IconButton","icons","eventBus","detailsButton","label","mw","msg","additionalClassNames","progressive","slideLeftButton","rotation","icon","slideRightButton","LoadErrorMessage","ImageGateway","router","ImageCarousel","constructor","options","super","extend","className","events","initialize","this","gateway","api","hasLoadError","template","defaults","licenseLinkMsg","prevMsg","nextMsg","thumbnails","onSlide","ev","nextThumbnail","$el","find","target","closest","data","title","filename","navigateTo","path","useReplaceState","newImageCarousel","replaceWith","preRender","forEach","thumbnail","i","getFileName","caption","getDescription","galleryOffset","_enableArrowImages","thumbs","offset","lastThumb","nextThumb","undefined","length","_disableArrowImages","remove","_handleRetry","emit","postRender","$img","$spinner","spinner","showLoadFailMsg","hide","retryPath","getPath","on","prependTo","addImageLoadClass","addClass","$details","append","prepend","getThumb","then","author","url","descriptionurl","thumbWidth","thumbwidth","thumbHeight","thumbheight","imgRatio","parseHTML","document","attr","thumburl","_positionImage","extmetadata","LicenseShortName","text","value","Artist","replace","adjustDetails","onToggleDetails","toggle","$window","getWindow","detailsHeight","is","outerHeight","windowWidth","width","windowHeight","height","windowRatio","css","module","exports","sizeBuckets","actionParams","_cache","cachedThumb","imageSizeMultiplier","window","devicePixelRatio","get","prop","titles","iiprop","iiurlwidth","findSizeBucket","iiurlheight","resp","query","pages","imageinfo","Error","size","isTemplateMode","msgToUser","retryTxt","error","onRetry","m","define"],"sourceRoot":""}