{"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","webpack://mfModules.[name]/./src/mobile.startup/mediaViewer/overlay.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","call","extend","className","events","click .image-wrapper","click .slider-button","hideOnExitClick","template","get","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","hasLoadError","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","prop","titles","iiprop","iiurlwidth","iiurlheight","resp","query","pages","imageinfo","Error","_findSizeBucket","click .load-fail-msg-link a","isTemplateMode","icon","toHtmlString","msgToUser","retryTxt","onRetry","m","overlay","define","promisedView","Overlay","make","cancelButton","cancel","Promise","all","using"],"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: 'arrow-invert'\n\t} ),\n\tslideRightButton = new Icon( {\n\t\trotation: -90,\n\t\tname: 'arrow-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 * @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\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\thideOnExitClick: false,\n\t/**\n\t * @memberof ImageCarousel\n\t * @instance\n\t */\n\ttemplate: mw.template.get( 'mobile.mediaViewer', 'ImageCarousel.hogan' ),\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 * @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 * @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 * @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 * @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 * @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 * @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 * @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 * @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\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 * @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 * @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 * @class ImageGateway\n *\n * @param {Object} options Configuration options\n * @property {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 * @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\tIcon = require( './../mobile.startup/Icon' ),\n\tView = require( './../mobile.startup/View' );\n\n/**\n * Shows the user a load failure message\n * @class LoadErrorMessage\n * @extends View\n * @fires LoadErrorMessage#retry\n *\n * @param {Object} options Configuration options\n */\nfunction LoadErrorMessage( options ) {\n\tif ( !options.retryPath ) {\n\t\tthrow new Error( '\\'retryPath\\' must be set in options param. Received: ' + options.retryPath );\n\t}\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: mw.template.get( 'mobile.mediaViewer', 'LoadErrorMessage.hogan' ),\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\ticon: new Icon( {\n\t\t\tname: 'alert-invert',\n\t\t\tadditionalClassNames: 'load-fail-msg-icon'\n\t\t} ).toHtmlString(),\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.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 * @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 * @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\toverlay = require( '../mobile.startup/mediaViewer/overlay' ),\n\tImageCarousel = require( './ImageCarousel' );\n\n/**\n * @deprecated\n * @param {Object} options\n * @return {Overlay}\n */\nfunction ImageOverlay( options ) {\n\treturn overlay( options );\n}\n\n// Expose for Minerva\nm.define( 'mobile.mediaViewer/ImageOverlay', ImageOverlay );\nm.define( 'mobile.mediaViewer', {\n\tImageCarousel\n} );\n","var m = require( '../moduleLoaderSingleton' ),\n\tpromisedView = require( '../promisedView' ),\n\tutil = require( '../util' ),\n\ticons = require( '../icons' ),\n\tOverlay = require( '../Overlay' );\n\n/**\n * Produce an overlay for talk page\n * @param {Object} options\n * @return {Overlay}\n */\nfunction mediaViewerOverlay( options ) {\n\tvar overlay = Overlay.make(\n\t\t{\n\t\t\tcancelButton: icons.cancel( 'gray' ).toHtmlString(),\n\t\t\tclassName: 'overlay media-viewer'\n\t\t},\n\t\tpromisedView(\n\t\t\tutil.Promise.all( [\n\t\t\t\tmw.loader.using( 'mobile.mediaViewer' )\n\t\t\t] ).then( function () {\n\t\t\t\tvar ImageCarousel = m.require( 'mobile.mediaViewer' ).ImageCarousel;\n\t\t\t\treturn new ImageCarousel( options );\n\t\t\t} )\n\t\t)\n\t);\n\n\treturn overlay;\n}\n\nmodule.exports = mediaViewerOverlay;\n"],"sourceRoot":""}