wikimedia/mediawiki-extensions-MobileFrontend

View on GitHub
resources/dist/mobile.mediaViewer.js.map.json

Summary

Maintainability
Test Coverage
{"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\tmfExtend = require( '../mobile.startup/mfExtend' ),\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 * @class ImageCarousel\n * @extends module:mobile.startup/View\n * @param {Object} options Configuration options, see Overlay#defaults\n * @private\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 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\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 {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\tdefaults: util.extend( {}, View.prototype.defaults, {\n\t\tlicenseLinkMsg: mw.msg( 'mobile-frontend-media-license-link' ),\n\t\tprevMsg: mw.msg( 'mobile-frontend-media-prev' ),\n\t\tnextMsg: mw.msg( 'mobile-frontend-media-next' ),\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\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\t/**\n\t * @inheritdoc\n\t * @memberof ImageCarousel\n\t * @instance\n\t */\n\tpreRender: function () {\n\t\tconst 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\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\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\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\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\tlet author;\n\t\t\tconst 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\teventBus.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\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 * @memberof ImageCarousel\n\t * @instance\n\t */\n\tadjustDetails: function () {\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 * 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\tlet 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 * @param {Object} options Configuration options\n * @param {mw.Api} options.api\n * @private\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\tconst 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","const 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 * @extends module:mobile.startup/View\n * @fires LoadErrorMessage#retry\n *\n * @param {Object} options Configuration options\n * @param {string} options.retryPath path of URL to try again\n * @private\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","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","require","util","mfExtend","IconButton","icons","eventBus","detailsButton","label","mw","msg","additionalClassNames","progressive","slideLeftButton","rotation","icon","slideRightButton","LoadErrorMessage","ImageGateway","router","__non_webpack_require__","ImageCarousel","options","this","gateway","api","hasLoadError","call","extend","className","events","template","defaults","prototype","licenseLinkMsg","prevMsg","nextMsg","thumbnails","onSlide","ev","nextThumbnail","$el","find","target","closest","data","title","filename","navigateTo","path","useReplaceState","newImageCarousel","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","$window","getWindow","detailsHeight","is","outerHeight","windowWidth","width","windowHeight","height","windowRatio","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"],"sourceRoot":""}