{"version":3,"sources":["webpack://mfModules.[name]/./src/mobile.mediaViewer/ImageCarousel.js","webpack://mfModules.[name]/./src/mobile.mediaViewer/ImageGateway.js","webpack://mfModules.[name]/./src/mobile.mediaViewer/LoadErrorMessage.js","webpack://mfModules.[name]/./src/mobile.mediaViewer/mobile.mediaViewer.js"],"names":["View","require","util","mfExtend","Icon","icons","detailsButton","label","mw","msg","additionalClassNames","progressive","slideLeftButton","rotation","name","slideRightButton","LoadErrorMessage","ImageGateway","router","loader","ImageCarousel","options","this","gateway","api","eventBus","hasLoadError","call","extend","className","events","template","defaults","prototype","licenseLinkMsg","thumbnails","onSlide","ev","newImageCarousel","nextThumbnail","$el","find","target","closest","data","title","filename","navigateTo","path","useReplaceState","replaceWith","preRender","self","forEach","thumbnail","i","getFileName","caption","getDescription","galleryOffset","_enableArrowImages","thumbs","lastThumb","nextThumb","offset","undefined","length","_disableArrowImages","remove","_handleRetry","emit","postRender","$img","$spinner","spinner","showLoadFailMsg","hide","retryPath","getPath","on","bind","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","detailsHeight","windowWidth","windowHeight","windowRatio","$window","getWindow","is","outerHeight","width","height","css","module","exports","sizeBuckets","actionParams","findSizeBucket","size","_cache","cachedThumb","imageSizeMultiplier","window","devicePixelRatio","get","prop","titles","iiprop","iiurlwidth","iiurlheight","resp","query","pages","imageinfo","Error","_findSizeBucket","isTemplateMode","msgToUser","retryTxt","error","onRetry","m","define"],"mappings":"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","file":"mobile.mediaViewer.js","sourcesContent":["var View = require( '../mobile.startup/View' ),\n\tutil = require( '../mobile.startup/util' ),\n\tmfExtend = require( '../mobile.startup/mfExtend' ),\n\tIcon = require( '../mobile.startup/Icon' ),\n\ticons = require( '../mobile.startup/icons' ),\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 Icon( {\n\t\trotation: 90,\n\t\tname: 'expand-invert'\n\t} ),\n\tslideRightButton = new Icon( {\n\t\trotation: -90,\n\t\tname: 'expand-invert'\n\t} ),\n\tLoadErrorMessage = require( './LoadErrorMessage' ),\n\tImageGateway = require( './ImageGateway' ),\n\t// FIXME: mw.loader.require is a private function but there's no other way to get hold of\n\t// this right now using require will cause webpack to resolve it\n\t// Can be rewritten to mw.router when https://gerrit.wikimedia.org/r/#/c/mediawiki/core/+/482732 has been merged\n\trouter = mw.loader.require( 'mediawiki.router' );\n\n/**\n * Displays images in full screen overlay\n *\n * @class ImageCarousel\n * @extends View\n * @param {Object} options Configuration options\n * @param {OO.EventEmitter} options.eventBus Object used to listen for resize:throttled events\n */\nfunction ImageCarousel( options ) {\n\tthis.gateway = options.gateway || new ImageGateway( {\n\t\tapi: options.api\n\t} );\n\tthis.router = options.router || router;\n\tthis.eventBus = options.eventBus;\n\tthis.hasLoadError = false;\n\n\tView.call(\n\t\tthis,\n\t\tutil.extend(\n\t\t\t{\n\t\t\t\tclassName: 'image-carousel',\n\t\t\t\tevents: {\n\t\t\t\t\t'click .image-wrapper': 'onToggleDetails',\n\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'click .slider-button': 'onSlide'\n\t\t\t\t}\n\t\t\t},\n\t\t\toptions\n\t\t)\n\t);\n}\n\nmfExtend( ImageCarousel, View, {\n\t/**\n\t * @memberof ImageCarousel\n\t * @instance\n\t */\n\ttemplate: util.template( `\n<button 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 class=\"next slider-button\"></button>\n\t` ),\n\n\t/**\n\t * @memberof ImageCarousel\n\t * @instance\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 {Thumbnail[]} defaults.thumbnails a list of thumbnails to browse\n\t */\n\tdefaults: util.extend( {}, View.prototype.defaults, {\n\t\tlicenseLinkMsg: mw.msg( 'mobile-frontend-media-license-link' ),\n\t\tthumbnails: []\n\t} ),\n\t/**\n\t * Event handler for slide event\n\t *\n\t * @memberof ImageCarousel\n\t * @instance\n\t * @param {jQuery.Event} ev\n\t */\n\tonSlide: function ( ev ) {\n\t\tvar\n\t\t\tnewImageCarousel,\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\tnewImageCarousel = new ImageCarousel( this.options );\n\t\tthis.$el.replaceWith( newImageCarousel.$el );\n\t\tthis.$el = newImageCarousel.$el;\n\t},\n\t/**\n\t * @inheritdoc\n\t * @memberof ImageCarousel\n\t * @instance\n\t */\n\tpreRender: function () {\n\t\tvar self = this;\n\t\tthis.options.thumbnails.forEach( function ( thumbnail, i ) {\n\t\t\tif ( thumbnail.getFileName() === self.options.title ) {\n\t\t\t\tself.options.caption = thumbnail.getDescription();\n\t\t\t\tself.galleryOffset = i;\n\t\t\t}\n\t\t} );\n\t},\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 * @memberof ImageCarousel\n\t * @instance\n\t * @param {Array} thumbs A set of images, which are available\n\t * @private\n\t */\n\t_enableArrowImages: function ( thumbs ) {\n\t\tvar offset = this.galleryOffset,\n\t\t\tlastThumb, 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\t/**\n\t * Disables the possibility to arrow through all images of the page.\n\t *\n\t * @memberof ImageCarousel\n\t * @instance\n\t * @private\n\t */\n\t_disableArrowImages: function () {\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 * @memberof ImageCarousel\n\t * @instance\n\t * @private\n\t */\n\t_handleRetry: function () {\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 * @memberof ImageCarousel\n\t * @instance\n\t */\n\tpostRender: function () {\n\t\tvar\n\t\t\t$img,\n\t\t\t$el = this.$el,\n\t\t\t$spinner = icons.spinner().$el,\n\t\t\tthumbs = this.options.thumbnails || [],\n\t\t\tself = this;\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\tfunction showLoadFailMsg() {\n\t\t\tself.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: self.router.getPath() } )\n\t\t\t\t\t.on( 'retry', self._handleRetry.bind( self ) )\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\tfunction 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( self.options.title ).then( function ( data ) {\n\t\t\tvar author, url = data.descriptionurl + '#mw-jump-to-license';\n\n\t\t\t$spinner.hide();\n\n\t\t\tself.thumbWidth = data.thumbwidth;\n\t\t\tself.thumbHeight = data.thumbheight;\n\t\t\tself.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 = self.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', self.options.caption );\n\t\t\t$el.find( '.image' ).append( $img );\n\n\t\t\tself.$details.addClass( 'is-visible' );\n\t\t\tself._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\tself.adjustDetails();\n\t\t}, function () {\n\t\t\t// retrieving image location failed so show load fail msg\n\t\t\tshowLoadFailMsg();\n\t\t} );\n\n\t\tthis.eventBus.on( 'resize:throttled', this._positionImage.bind( this ) );\n\t\tthis._positionImage();\n\t},\n\n\t/**\n\t * Event handler that toggles the details bar.\n\t *\n\t * @memberof ImageCarousel\n\t * @instance\n\t */\n\tonToggleDetails: function () {\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\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 * @memberof ImageCarousel\n\t * @instance\n\t * @private\n\t */\n\t_positionImage: function () {\n\t\tvar detailsHeight, windowWidth, windowHeight, windowRatio, $img,\n\t\t\t$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\tdetailsHeight = !this.$details.is( ':visible' ) ? 0 : this.$details.outerHeight();\n\t\twindowWidth = $window.width();\n\t\twindowHeight = $window.height() - detailsHeight;\n\t\twindowRatio = windowWidth / windowHeight;\n\t\t$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 * @memberof ImageCarousel\n\t * @instance\n\t */\n\tadjustDetails: function () {\n\t\tvar 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","var sizeBuckets = [ 320, 640, 800, 1024, 1280, 1920, 2560, 2880 ],\n\tactionParams = require( './../mobile.startup/actionParams' ),\n\tutil = require( './../mobile.startup/util' );\n\n/**\n * Gets the first size larger than or equal to the provided size\n *\n * @memberof ImageGateway\n * @param {number} size\n * @return {number}\n */\nfunction findSizeBucket( size ) {\n\tvar i = 0;\n\twhile ( size > sizeBuckets[i] && i < sizeBuckets.length - 1 ) {\n\t\t++i;\n\t}\n\treturn sizeBuckets[i];\n}\n\n/**\n * API for retrieving image thumbnails for a given page\n *\n * @class ImageGateway\n *\n * @param {Object} options Configuration options\n * @cfg {mw.Api} options.api\n */\nfunction ImageGateway( options ) {\n\tthis._cache = {};\n\tthis.api = options.api;\n}\n/**\n * Get thumbnail via the API and cache it. Return the result from the cache if exists.\n *\n * @param {string} title Url of image\n * @return {jQuery.Deferred} with the image info\n */\nImageGateway.prototype.getThumb = function ( title ) {\n\tvar cachedThumb = this._cache[title],\n\t\t$window = util.getWindow(),\n\t\timageSizeMultiplier = ( window.devicePixelRatio && window.devicePixelRatio > 1 ) ?\n\t\t\twindow.devicePixelRatio : 1;\n\n\tif ( !cachedThumb ) {\n\t\tthis._cache[title] = this.api.get( actionParams( {\n\t\t\tprop: 'imageinfo',\n\t\t\ttitles: title,\n\t\t\tiiprop: [ 'url', 'extmetadata' ],\n\t\t\t// request an image devicePixelRatio times bigger than the reported screen size\n\t\t\t// for retina displays and zooming\n\t\t\tiiurlwidth: findSizeBucket( $window.width() * imageSizeMultiplier ),\n\t\t\tiiurlheight: findSizeBucket( $window.height() * imageSizeMultiplier )\n\t\t} ) ).then( function ( resp ) {\n\t\t\t// imageinfo is undefined for missing pages.\n\t\t\tif ( resp.query && resp.query.pages &&\n\t\t\t\tresp.query.pages[0] && resp.query.pages[0].imageinfo ) {\n\t\t\t\treturn resp.query.pages[0].imageinfo[0];\n\t\t\t}\n\t\t\tthrow new Error( 'The API failed to return any pages matching the titles.' );\n\t\t} );\n\t}\n\n\treturn this._cache[title];\n};\n\nImageGateway._findSizeBucket = findSizeBucket;\nmodule.exports = ImageGateway;\n","var util = require( './../mobile.startup/util' ),\n\tmfExtend = require( './../mobile.startup/mfExtend' ),\n\ticons = require( './../mobile.startup/icons' ),\n\tView = require( './../mobile.startup/View' );\n\n/**\n * Shows the user a load failure message\n *\n * @class LoadErrorMessage\n * @extends View\n * @fires LoadErrorMessage#retry\n *\n * @param {Object} options Configuration options\n * @param {string} options.retryPath path of URL to try again\n */\nfunction LoadErrorMessage( options ) {\n\tView.call(\n\t\tthis,\n\t\t{ events: { 'click .load-fail-msg-link a': 'onRetry' } },\n\t\toptions\n\t);\n}\n\nmfExtend( LoadErrorMessage, View, {\n\ttemplate: 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\tisTemplateMode: true,\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* @memberof LoadErrorMessage\n\t\t* @instance\n\t\t*/\n\tdefaults: util.extend( {}, LoadErrorMessage.prototype.defaults, {\n\t\tmsgToUser: mw.msg( 'mobile-frontend-media-load-fail-message' ),\n\t\tretryTxt: mw.msg( 'mobile-frontend-media-load-fail-retry' )\n\t} ),\n\n\t/**\n\t * @inheritdoc\n\t * @memberof LoadErrorMessage\n\t * @instance\n\t */\n\tpostRender: function () {\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 * @memberof LoadErrorMessage\n\t * @instance\n\t */\n\tonRetry: function () {\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","var m = require( '../mobile.startup/moduleLoaderSingleton' ),\n\tImageCarousel = require( './ImageCarousel' );\n\n// Needed for lazy loading ImageCarousel\nm.define( 'mobile.mediaViewer', {\n\tImageCarousel\n} );\n"],"sourceRoot":""}