User:Yair rand/NavPopupsRestyled.js

// See Navigation Popups (Restyling and Enhancements), by Vibhabamba. // This is still probably quite buggy. // Licensed under GPLv2+, CC-BY-SA 3.0, GFDL, and anything else the WMF might find useful. mw.util.addCSS( " \ .NP2box { \ position: absolute; \  z-index: 5; \  background: #FFFFFF; \  border: 1px solid #AAA; \  box-shadow: rgba(0, 0, 0, 0.15) 3px 3px 3px; \  padding: 0; \  font-size: 12px; \  display: none; \  min-width: 300px; \  width: 300px; \ } \ .NP2box.is-tall { \  width: 450px; \ } \ .NP2box:before { \  content: ; \  position: absolute; \  border: 8px solid transparent; \  border-top: 0; \  border-bottom: 8px solid #AAA; \  top: -8px; \  left: 10px; \ } \ .NP2box:after { \  content: ; \  position: absolute; \  border: 7px solid transparent; \  border-top: 0; \  border-bottom: 7px solid #FFF; \  top: -7px; \  left: 11px; \ } \ .NP2box.flipped:before { \  left: initial; \  right: 10px; \ } \ .NP2box.flipped:after { \  left: initial; \  right: 11px; \ } \ .NP2box > div { \  margin-top: -9px; \  padding-top: 9px; \ } \ .NP2box > div > div { \  padding: 1.6em; \ } \ .NP2box > div > div + div { \ padding: 0.8em; \ margin: 0.8em; \ } \ .NP2watchicon { \ float: right; \ font-size: 20px; \ cursor: pointer; \ margin-top: -5px; \ } \ .NP2box > div > img.is-tall { \ float: right; \ margin-left: 1em; \ } \ .NP2box > div > img.is-not-tall { \ margin-left: 0; \ } \ .NP2redirect { \ color: #AAAAAA; \ font-style: italic; \ }"); $( document ).ready( function( $ ){ mw.messages.set( {    "NP2-readmore" : "Read more", // this and the four below allow use of GENDER:$1.    "NP2-watch" : "Add page to watchlist.",     "NP2-unwatch" : "Unwatch this page.",     "NP2-watched" : "Added to your watchlist",     "NP2-unwatched" : "Removed from your watchlist",     "NP2-edited-seconds" : "Last edited NaN $1 secondss ago.",     "NP2-edited-minutes" : "Last edited $1 minutes ago.",     "NP2-edited-hours" : "Last edited $1 hours ago.",     "NP2-edited-days" : "Last edited NaN $1 days agos.",     "NP2-edited-years" : "Last edited NaN $1 yearss ago.",    "NP2-redirects" : "redirects to"  } );  var loadDelay = 150, // number of ms before sending the request    displayDelay = 500, // minimum number of ms between sending request and displaying    closeDelay = 800, // time between exiting box and it disappearing    timer, currentLink, cache = {}, curRequest, $marker = $( " " ).css("vertical-align", "top"), api = new mw.Api, $box = $( " " ).addClass( "NP2box" ).on( {      mouseleave: leaveActive,      mouseenter: function {        setTimeout( function{ timer && clearTimeout( timer ); }, 16 ); // don't ask.     }    }).appendTo( document.body ); function closeBox { $( currentLink ).off( "mouseleave", leaveActive ); $box.hide; timer && clearTimeout( timer ); currentLink = timer = undefined; } function leaveActive { timer = setTimeout( closeBox, closeDelay ); } function leaveInactive { $( currentLink ).off( "mouseleave", leaveInactive ); clearTimeout( timer ); curRequest && curRequest.abort; currentLink = timer = curRequest = undefined; } $("#mw-content-text").on("mouseenter", "a", function( event ){    var $this = $( this ), href = this.href,       title = href && href.split("/wiki/")[ 1 ];    if( !title || $this.hasClass("extiw") || $this.hasClass("image") || $this.hasClass("new") || href.indexOf("?") !== -1 || href.indexOf( location.href.split("#")[ 0 ] + "#" ) === 0 ) {     return;    }    if( currentLink === this ) {      clearTimeout( timer );      return;    }    if( currentLink ) {      closeBox;    }    currentLink = this;    timer = setTimeout( function { function createBox { $marker.prependTo( $this ); var offset, sploot = cache[ href ], thumbnail = sploot.thumbnail, sOffset = $marker.offset, eOffset = $marker.appendTo( $this ).offset, tall = thumbnail && thumbnail.height > thumbnail.width, // todo: fix duplication. boxWidth = tall ? 450 : 300;       if( sOffset.left > eOffset.left ) { if( event.pageX > sOffset.left ) { // top half offset = sOffset; } else { // latter half offset = eOffset; offset.left += - 40; }         offset.top += $marker.height + 8; } else { // normal situations offset = sOffset; offset.top += $this.height + 8; offset.left += ( $this.width / 2 ) - ( 20 ); }       var flip = $( window ).width < boxWidth + offset.left; $marker.remove;

$box .children .detach .end [ tall ? "addClass" : "removeClass" ]( "is-tall" ) [ flip ? "addClass" : "removeClass" ]( "flipped" ) // needs a better class name. .css({ // avoid .empty to keep event handlers           top: offset.top,            left: offset.left - ( flip && boxWidth - 40 ),            "min-height": tall ? thumbnail.height : "initial"          }) .append( sploot.box ) .show; $this.off( "mouseleave", leaveInactive ).on( "mouseleave", leaveActive ); }     if( cache[ href ] ) { timer = setTimeout( createBox, displayDelay ); } else { curRequest = api.get( {         action: "query",           prop: "extracts|pageimages|revisions|info",          redirects: "true",          exintro: "true",          exsentences: 2,          explaintext: "true",          piprop: "thumbnail",          pithumbsize: 300,          rvprop: "timestamp",          inprop: "watched",          titles: decodeURI( title )        }, function( re ) {          curRequest = undefined;          var extract, timestamp, timetext, thumbnail, watched,             redirects = re.query.redirects;          for( var page in re.query.pages ) {            page = re.query.pages[ page ];          }          thumbnail = page.thumbnail;          timestamp = +new Date - +new Date( page.revisions[ 0 ].timestamp );          watched = "watched" in page;          extract = page.extract;          var tall = thumbnail && thumbnail.height > thumbnail.width;          function timediff( timestamp ) { function shrink( num ){ return timestamp = Math.floor( timestamp / num ); }           if( shrink( 1000 ) < 120 ) return mw.message( "NP2-edited-seconds", timestamp ); if( shrink( 60 ) < 120 ) return mw.message( "NP2-edited-minutes", timestamp ); if( shrink( 60 ) < 24 ) return mw.message( "NP2-edited-hours", timestamp ); if( shrink( 24 ) < 365 ) return mw.message( "NP2-edited-days", timestamp ); shrink( 365 ); return mw.message( "NP2-edited-years", timestamp ); }         timetext = timediff( timestamp ).text; var $watchicon, $bottommessage, $contentbox = $( " " ) .html( extract ) .append(              $( "" )                .attr({ href: href, target: "_blank" })                .text( mw.message( "NP2-readmore", mw.user ).text )             ); function updateWatchIcon { $watchicon.text( watched ? "★" : "☆" ).attr( "title",              mw.message( watched ? "NP2-unwatch" : "NP2-watch", mw.user ).text           ); }         if( redirects ) { $contentbox.prepend(              $( " " )                .addClass( "NP2redirect" )                .text( mw.message( "NP2-redirects" ).text ),              $( " " ).text( redirects[ 0 ].to )            ); }         cache[ href ] = { box: $( " " ).append(              thumbnail ? $( " " ).addClass( tall ? "is-tall" : "is-not-tall" ).attr( "src", thumbnail.source ).css( { width: thumbnail.width, height: thumbnail.height } ) : $( " " ),              $contentbox,              $( " " ).css( { color: timestamp < 1000 * 60 * 60 * 24 ? "#00AF89" : "#BBBBBB" } ).append( $watchicon = $( " " ).addClass("NP2watchicon").on( "click", function {                 var x = {                    format: "json",                    action: "watch",                     title: decodeURI( title ),                    token: mw.user.tokens.get('watchToken')                  };                  if( watched ){                     x.unwatch = true;                  }                  $.post( mw.util.wikiScript( 'api' ), x, function( re ) { // mw.notify( $( re.watch.message ) ); watched = !("unwatched" in re.watch); $bottommessage.text(                      mw.message( watched ? "NP2-watched" : "NP2-unwatched", mw.user ).text                   ).parent.css( "background-color", "#EEE" ); updateWatchIcon; $contentbox.add( $contentbox.prev ).animate( {                     opacity: 0.5                    }, 250 ).delay( 1000 ).animate( {                      opacity: 1                    }, 250, function {                      $bottommessage.text( timetext ).parent.css( "background-color", "#FFF" );                    } ); } );               } ),                 $bottommessage = $(" ").text( timetext ) )            ), thumbnail: thumbnail };         timer === undefined && createBox; updateWatchIcon; });       timer = setTimeout( function { timer = undefined; cache[ href ] && createBox; }, displayDelay );     }    }, loadDelay ); $this.on( "mouseleave", leaveInactive ); }); });