User:Se4598/testpopups.js

From mediawiki.org

Note: After publishing, you may have to bypass your browser's cache to see the changes.

  • Firefox / Safari: Hold Shift while clicking Reload, or press either Ctrl-F5 or Ctrl-R (⌘-R on a Mac)
  • Google Chrome: Press Ctrl-Shift-R (⌘-Shift-R on a Mac)
  • Internet Explorer / Edge: Hold Ctrl while clicking Refresh, or press Ctrl-F5
  • Opera: Press Ctrl-F5.
mw.loader.using( [ 'ext.popups' ], function() { //wait for popups to be loaded
	// inject gerrit change 232236
	    mw.popups.render.render = function ( link, event ) {
       // This will happen when the mouse goes from the popup box back to the
       // anchor tag. In such a case, the timer to close the box is cleared.
       if (
           mw.popups.render.currentLink &&
           mw.popups.render.currentLink[ 0 ] === link[ 0 ]
       ) {
           if ( mw.popups.render.closeTimer ) {
               mw.popups.render.closeTimer.abort();
           }
           return;
       }
 
       // If the mouse moves to another link (we already check if its the same
       // link in the previous condition), then close the popup.
       if ( mw.popups.render.currentLink ) {
           mw.popups.render.closePopup();
       }
 
       // Ignore if its meant to call a function
       // TODO: Remove this when adding reference popups
       if ( link.attr( 'href' ) === '#' ) {
           return;
       }
 
       // Disable Navigration Popups
       mw.popups.disableNavPopup();
 
       mw.popups.render.currentLink = link;
       link.on( 'mouseleave blur', mw.popups.render.leaveInactive );
 
       if ( mw.popups.render.cache[ link.attr( 'href' ) ] ) {
           mw.popups.render.openTimer = mw.popups.render.wait( mw.popups.render.POPUP_DELAY )
               .done( function () {
                   mw.popups.render.openPopup( link, event );
               } );
       } else {
           // Wait for timer before making API queries and showing hovercard
           mw.popups.render.openTimer = mw.popups.render.wait( mw.popups.render.API_DELAY )
               .done( function () {
                   var cachePopup, key,
                       renderers = mw.popups.render.renderers;
 
                   // Check run the matcher method of all renderers to find the right one
                   for ( key in renderers ) {
                       if ( renderers.hasOwnProperty( key ) && key !== 'article' ) {
                           if ( !!renderers[ key ].matcher( link.attr( 'href' ) ) ) {
                               cachePopup = renderers[ key ].init( link );
                           }
                       }
                   }
 
                   // Use the article renderer if nothing else matches
                   if ( cachePopup === undefined ) {
                       cachePopup = mw.popups.render.renderers.article.init( link );
                   }
 
                   mw.popups.render.openTimer = mw.popups.render.wait( mw.popups.render.POPUP_DELAY - mw.popups.render.API_DELAY );
 
                   $.when( mw.popups.render.openTimer, cachePopup ).done( function () {
                       mw.popups.render.openPopup( link, event );
                   } );
               } );
       }
   };
	// ############# END of patched function
	
	
	    /**
    * @class mw.popups.render.artblue
    * @singleton
    */
   var artblue = {};
 
   /**
    * Size constants for popup images
    * @property SIZES
    */
   artblue.SIZES = {
       portraitImage: {
           h: 250, // Exact height
           w: 203 // Max width
       },
       landscapeImage: {
           h: 200, // Max height
           w: 300 // Exact Width
       },
       landscapePopupWidth: 450, // Exact width of a landscape popup
       portraitPopupWidth: 300, // Exact width of a portrait popup
       pokeySize: 8 // Height of the triangle used to point at the link
   };
 
 
   /**
    * Survey link, if any, for this renderer
    * @property surveyLink
    */
   artblue.surveyLink = mw.config.get( 'wgPopupsSurveyLink' );
 
   artblue.matcher =  function ( xxx ) {
   		console.log(xxx);
       return true;
   }
 
   /**
    * Send an API request and cache the jQuery element
    *
    * @param {jQuery} link
    * @return {jQuery.Promise}
    */
   artblue.init = function ( link ) {
       var href = link.attr( 'href' ),
           title = mw.popups.getTitle( href ),
           deferred = $.Deferred();
 
       if ( !title ) {
           return deferred.reject().promise();
       }
 
       mw.popups.render.currentRequest = mw.popups.api.get( {
           action: 'query',
           prop: 'extracts|pageimages|revisions',
           formatversion: 2,
           redirects: true,
           exintro: true,
           exsentences: 5,
           // there is an added geometric limit on .mwe-popups-extract
           // so that text does not overflow from the card
           explaintext: true,
           piprop: 'thumbnail',
           pithumbsize: 300,
           rvprop: 'timestamp',
           titles: title,
           smaxage: 300,
           maxage: 300,
           uselang: 'content'
       } );
 
       mw.popups.render.currentRequest.fail( deferred.reject );
       mw.popups.render.currentRequest.done( function ( re ) {
           mw.popups.render.currentRequest = undefined;
 
           if (
               !re.query ||
               !re.query.pages ||
               !re.query.pages[0].extract ||
               re.query.pages[0].extract === ''
           ) {
               deferred.reject();
               return;
           }
 
           mw.popups.render.cache[ href ] = {};
           mw.popups.render.cache[ href ].popup = artblue.createPopup( re.query.pages[0], href );
           mw.popups.render.cache[ href ].getOffset = artblue.getOffset;
           mw.popups.render.cache[ href ].getClasses = artblue.getClasses;
           mw.popups.render.cache[ href ].process = artblue.processPopup;
           deferred.resolve();
       } );
 
       return deferred.promise();
   };
 
   /**
    * Returns a thumbnail object based on the ratio of the image
    * Uses an SVG image where available to add the triangle/pokey
    * mask on the image. Crops and resizes the SVG image so that
    * is fits inside a rectangle of a particular size.
    *
    * @method createPopup
    * @param {Object} page Information about the linked page
    * @param {String} href
    * @return {jQuery}
    */
   artblue.createPopup = function ( page, href ) {
       var $div,
           $contentbox = $( '<a>' )
               .attr( 'href', href )
               .addClass( 'mwe-popups-extract' )
               .append(
                   'i am blue'
               ),
           thumbnail = page.thumbnail,
           tall = thumbnail && thumbnail.height > thumbnail.width,
           $thumbnail = artblue.createThumbnail( thumbnail, tall ),
           timestamp = new Date( page.revisions[0].timestamp ),
           timediff = new Date() - timestamp,
           oneDay = 1000 * 60 * 60 * 24,
           timestampclass = ( timediff < oneDay ) ?
               'mwe-popups-timestamp-recent' :
               'mwe-popups-timestamp-older',
           $settingsImage = $( '<a>' ).addClass( 'mwe-popups-icon mwe-popups-settings-icon' ),
           $surveyImage,
           $timestamp = $( '<div>' )
               .addClass( timestampclass )
               .append(
                   $( '<span>' ).text( mw.message( 'popups-last-edited',
                       moment( timestamp ).fromNow() ).text() ),
                   $settingsImage
               );
 
       if ( artblue.surveyLink ) {
           $surveyImage = $( '<a>' )
               .attr( 'href', artblue.surveyLink )
               .attr( 'target', '_blank' )
               .attr( 'title', mw.message( 'popups-send-feedback' ) )
               .addClass( 'mwe-popups-icon mwe-popups-survey-icon' );
           $timestamp.append( $surveyImage );
       }
 
 
       if ( $thumbnail.prop( 'tagName' ) !== 'SPAN' ) {
           $thumbnail = $( '<a>' )
               .addClass( 'mwe-popups-discreet' )
               .attr( 'href', href )
               .append( $thumbnail );
       } else {
           tall = thumbnail = undefined;
       }
 
       $div = $( '<div>' ).append( $thumbnail, $contentbox, $timestamp ).css( 'background-color', 'blue' );
 
       mw.popups.render.cache[ href ].settings = {
           'title': page.title,
           'tall': ( tall === undefined ) ? false : tall,
           'thumbnail': ( thumbnail === undefined ) ? false : thumbnail
       };
 
       return $div;
   };
 
   /**
    * Returns an array of elements to be appended after removing parentheses
    * and making the title in the extract bold.
    *
    * @method getProcessedElements
    * @param {String} extract Should be unescaped
    * @param {String} title Should be unescaped
    * @return {Array} of elements to appended
    */
   artblue.getProcessedElements = function ( extract, title ) {
       title = title.replace( /([.?*+^$[\]\\(){}|-])/g, '\\$1' ); // Escape RegExp elements
 
       var elements = [],
           regExp = new RegExp( '(^|\\s)(' + title + ')(|$)', 'ig' ),
           boldIdentifier = '<bi-' + Math.random() + '>',
           snip = '<snip-' + Math.random() + '>';
 
       // Remove text in parentheses along with the parentheses
       extract = artblue.removeParensFromText( extract );
       extract = extract.replace(/\s+/g, ' '); // Remove extra white spaces
 
       // Make title bold in the extract text
       // As the extract is html escaped there can be no such string in it
       // Also, the title is escaped of RegExp elements thus can't have "*"
       extract = extract.replace( regExp, '$1' + snip + boldIdentifier + '$2' + snip + '$3' );
       extract = extract.split( snip );
 
       $.each( extract, function ( index, part ) {
           if ( part.indexOf( boldIdentifier ) === 0 ) {
               elements.push( $( '<b>' ).text( part.substring( boldIdentifier.length ) ) );
           } else {
               elements.push( document.createTextNode( part ) );
           }
       } );
 
       return elements;
   };
 
   /**
    * Removes content in parentheses from a string.  Returns the original
    * string as is if the parentheses are unbalanced or out or order. Does not
    * remove extra spaces.
    *
    * @method removeParensFromText
    * @param {String} string
    * @return {String}
    */
   artblue.removeParensFromText = function ( string ) {
       var
           ch,
           newString = '',
           level = 0,
           i = 0;
 
       for( i; i < string.length; i++ ) {
           ch = string.charAt( i );
 
           if ( ch === ')' && level === 0  ) {
               return string;
           }
           if ( ch === '(' ) {
               level++;
               continue;
           } else if ( ch === ')' ) {
               level--;
               continue;
           }
           if ( level === 0 ) {
               // Remove leading spaces before brackets
               if ( ch === ' ' && string.charAt( i + 1 ) === '(' ) {
                   continue;
               }
               newString += ch;
           }
       }
 
       return ( level === 0 ) ? newString : string;
   };
 
   /**
    * Use createElementNS to create the svg:image tag as jQuery
    * uses createElement instead. Some browsers map the `image` tag
    * to `img` tag, thus an `svg:image` is required.
    *
    * @method createSVGTag
    * @param {String} tag
    * @return {Object}
    */
   artblue.createSVGTag = function ( tag ) {
       return document.createElementNS( 'http://www.w3.org/2000/svg', tag );
   };
 
   /**
    * Returns a thumbnail object based on the ratio of the image
    * Uses an SVG image where available to add the triangle/pokey
    * mask on the image. Crops and resizes the SVG image so that
    * is fits inside a rectangle of a particular size.
    *
    * @method createThumbnail
    * @param {Object} thumbnail
    * @param {boolean} tall
    * @return {Object} jQuery DOM element of the thumbnail
    */
   artblue.createThumbnail = function ( thumbnail, tall ) {
       var svg = mw.popups.supportsSVG;
 
       if (
           // No thumbnail
           !thumbnail ||
           // Image too small for landscape display
           ( !tall && thumbnail.width < artblue.SIZES.landscapeImage.w ) ||
           // Image too small for protrait display
           ( tall && thumbnail.height < artblue.SIZES.portraitImage.h ) ||
           // These characters in URL that could inject CSS and thus JS
           (
               thumbnail.source.indexOf( '\\' ) > -1 ||
               thumbnail.source.indexOf( '\'' ) > -1 ||
               thumbnail.source.indexOf( '\"' ) > -1
           )
       ) {
           return $( '<span>' );
       }
 
       if ( tall && svg ) {
           return artblue.createSvgImageThumbnail(
               'mwe-popups-is-not-tall',
               thumbnail.source,
               ( thumbnail.width > artblue.SIZES.portraitImage.w ) ?
                       ( ( thumbnail.width - artblue.SIZES.portraitImage.w ) / -2 ) :
                       ( artblue.SIZES.portraitImage.w - thumbnail.width ),
               ( thumbnail.height > artblue.SIZES.portraitImage.h ) ?
                       ( ( thumbnail.height - artblue.SIZES.portraitImage.h ) / -2 ) :
                       0,
               thumbnail.width,
               thumbnail.height,
               artblue.SIZES.portraitImage.w,
               artblue.SIZES.portraitImage.h
           );
       }
 
       if ( tall && !svg ) {
           return artblue.createImgThumbnail( 'mwe-popups-is-tall', thumbnail.source );
       }
 
       if ( !tall && svg ) {
           return artblue.createSvgImageThumbnail(
               'mwe-popups-is-not-tall',
               thumbnail.source,
               0,
               ( thumbnail.height > artblue.SIZES.landscapeImage.h ) ?
                       ( ( thumbnail.height - artblue.SIZES.landscapeImage.h ) / -2 ) :
                       0,
               thumbnail.width,
               thumbnail.height,
               artblue.SIZES.landscapeImage.w + 3,
               ( thumbnail.height > artblue.SIZES.landscapeImage.h ) ?
                       artblue.SIZES.landscapeImage.h :
                       thumbnail.height,
               'mwe-popups-mask'
           );
       }
 
       if ( !tall && !svg ) {
           return artblue.createImgThumbnail( 'mwe-popups-is-not-tall', thumbnail.source );
       }
   };
 
   /**
    * Returns the `svg:image` object for thumbnail
    *
    * @method createSvgImageThumbnail
    * @param {String} className
    * @param {String} url
    * @param {Number} x
    * @param {Number} y
    * @param {Number} thumbnailWidth
    * @param {Number} thumbnailHeight
    * @param {Number} width
    * @param {Number} height
    * @param {String} clipPath
    * @return {jQuery}
    */
   artblue.createSvgImageThumbnail = function (
       className, url, x, y, thumbnailWidth, thumbnailHeight, width, height, clipPath
   ) {
       var $thumbnailSVGImage, $thumbnail;
 
       $thumbnailSVGImage = $( artblue.createSVGTag( 'image' ) );
       $thumbnailSVGImage
           .addClass( className )
           .attr( {
               'xlink:href': url,
               x: x,
               y: y,
               width: thumbnailWidth,
               height: thumbnailHeight,
               'clip-path': 'url(#' + clipPath + ')'
           } );
 
       $thumbnail = $( '<svg>' )
           .attr( {
               xmlns: 'http://www.w3.org/2000/svg',
               width: width,
               height: height
           } )
           .append( $thumbnailSVGImage );
 
       return $thumbnail;
   };
 
   /**
    * Returns the `img` object for thumbnail
    *
    * @method createImgThumbnail
    * @param {String} className
    * @param {String} url
    * @return {jQuery}
    */
   artblue.createImgThumbnail = function ( className, url ) {
       return $( '<div>' )
           .addClass( className )
           .css( 'background-image', 'url(' + url + ')' );
   };
 
   /**
    * Positions the popup based on the mouse position and popup size
    * Default popup positioning is below and to the right of the mouse or link,
    * unless flippedX or flippedY is true. flippedX and flippedY are cached.
    *
    * @method getOffset
    * @param {jQuery} link
    * @param {Object} event
    * @return {Object} This can be passed to `.css()` to position the element
    */
   artblue.getOffset = function ( link, event ) {
       var
           href = link.attr( 'href' ),
           flippedX = false,
           flippedY = false,
           settings = mw.popups.render.cache[ href ].settings,
           offsetTop = ( event.pageY ) ? // If it was a mouse event
               // Position according to mouse
               event.pageY + 20 :
               // Position according to link position or size
               link.offset().top + link.height() + 9,
           clientTop = ( event.clientY ) ?
               event.clientY :
               offsetTop,
           offsetLeft = ( event.pageX ) ?
               event.pageX :
               link.offset().left;
 
       // X Flip
       if ( offsetLeft > ( $( window ).width() / 2 ) ) {
           offsetLeft += ( !event.pageX ) ? link.width() : 0;
           offsetLeft -= ( !settings.tall ) ?
               artblue.SIZES.portraitPopupWidth :
               artblue.SIZES.landscapePopupWidth;
           flippedX = true;
       }
 
       if ( event.pageX ) {
           offsetLeft += ( flippedX ) ? 20 : -20;
       }
 
       mw.popups.render.cache[ href ].settings.flippedX = flippedX;
 
       // Y Flip
       if ( clientTop > ( $( window ).height() / 2 ) ) {
           flippedY = true;
       }
 
       if ( event.pageY && flippedY ) {
           offsetTop += 30;
       }
 
       mw.popups.render.cache[ href ].settings.flippedY = flippedY;
 
       return {
           top: offsetTop + 'px',
           left: offsetLeft + 'px'
       };
   };
 
   /**
    * Returns an array of classes based on the size and setting of the popup
    *
    * @method getClassses
    * @param {jQuery} link
    * @return {Array} List of classes to applied to the parent `div`
    */
   artblue.getClasses = function ( link ) {
       var
           classes = [],
           cache = mw.popups.render.cache [ link.attr( 'href' ) ],
           tall = cache.settings.tall,
           thumbnail = cache.settings.thumbnail,
           flippedY = cache.settings.flippedY,
           flippedX = cache.settings.flippedX;
 
       if ( flippedY ) {
           classes.push( 'mwe-popups-fade-in-down' );
       } else {
           classes.push( 'mwe-popups-fade-in-up' );
       }
 
       if ( flippedY && flippedX ) {
           classes.push( 'flipped_x_y' );
       }
 
       if ( flippedY && !flippedX ) {
           classes.push( 'flipped_y' );
       }
 
       if ( flippedX && !flippedY ) {
           classes.push( 'flipped_x' );
       }
 
       if ( ( !thumbnail || tall ) && !flippedY ) {
           classes.push( 'mwe-popups-no-image-tri' );
       }
 
       if ( ( thumbnail && !tall ) && !flippedY ) {
           classes.push( 'mwe-popups-image-tri' );
       }
 
       if ( tall ) {
           classes.push( 'mwe-popups-is-tall' );
       } else {
           classes.push( 'mwe-popups-is-not-tall' );
       }
 
       return classes;
   };
 
   /**
    * Processed the popup div after it has been displayed
    * to correctly render the triangle/pokeys
    *
    * @method processPopups
    * @param {jQuery} link
    */
   artblue.processPopup = function ( link ) {
       var
           svg = mw.popups.supportsSVG,
           cache = mw.popups.render.cache [ link.attr( 'href' ) ],
           popup = mw.popups.$popup,
           tall = cache.settings.tall,
           thumbnail = cache.settings.thumbnail,
           flippedY = cache.settings.flippedY,
           flippedX = cache.settings.flippedX;
 
       popup.find( '.mwe-popups-settings-icon' ).click( function () {
           mw.popups.settings.open();
       } );
 
       if ( !flippedY && !tall && cache.settings.thumbnail.height < artblue.SIZES.landscapeImage.h ) {
           $( '.mwe-popups-extract').css(
               'margin-top',
               cache.settings.thumbnail.height - artblue.SIZES.pokeySize
           );
       }
 
       if ( !svg && flippedY && !tall ) {
           $( '.mwe-popups-extract' ).css( 'margin-top', '206px' );
       }
 
       if ( flippedY ) {
           popup.css( {
               top: popup.offset().top - ( popup.outerHeight() + 50 )
           } );
       }
 
       if ( flippedY && thumbnail && svg ) {
           mw.popups.$popup
               .find( 'image' )[ 0 ]
               .setAttribute( 'clip-path', '' );
       }
 
       if ( flippedY && flippedX && thumbnail && tall && svg ) {
           mw.popups.$popup
               .find( 'image' )[ 0 ]
               .setAttribute( 'clip-path', 'url(#mwe-popups-landscape-mask-flip)' );
       }
 
       if ( flippedX && !flippedY && thumbnail && !tall && svg ) {
           mw.popups.$popup
               .find( 'image' )[ 0 ]
               .setAttribute( 'clip-path', 'url(#mwe-popups-mask-flip)' );
       }
 
       if ( flippedX && !flippedY && thumbnail && tall && svg ) {
           mw.popups.$popup
               .removeClass( 'mwe-popups-no-image-tri' )
               .find( 'image' )[ 0 ]
               .setAttribute( 'clip-path', 'url(#mwe-popups-landscape-mask)' );
       }
   };
	
	
	// register custom renderer
	mw.popups.render.renderers.artblue = artblue;
});