MediaWiki:LinkPreview/code.js

/* */ /* smth like mw:Extension:Popups */ /* popup on link:hover */ /* maintainer: user:fngplg */ /* classes: main: npage-preview, image not found: npage-preview-noimage */ /* img:, text: */ (function wrapper ($){   var Settings = window.pPreview || {};    Settings.debug = Settings.debug !== undefined ? Settings.debug : false;    //show preview delay, ms    Settings.delay = Settings.delay !== undefined ? Settings.delay : 100;    Settings.tlen = Settings.tlen !== undefined ? Settings.tlen : 1000; //max text length    //cache size    Settings.csize = Settings.csize !== undefined ? Settings.csize : 100;    Settings.defimage = Settings.defimage !== undefined ? Settings.defimage : 'https://vignette2.wikia.nocookie.net/borderlands/images/0/05/Ajax.gif/revision/latest/scale-to-width-down/350?cb=20170626182120&path-prefix=ru'; //default image path    //no image found. class: npage-preview-noimage    Settings.noimage = Settings.noimage !== undefined ? Settings.noimage : 'https://vignette4.wikia.nocookie.net/borderlands/images/f/f5/%D0%97%D0%B0%D0%B3%D0%BB%D1%83%D1%88%D0%BA%D0%B0.png/revision/latest/scale-to-width-down/200?cb=20160122074659&path-prefix=ru'; //request to perform scaling Settings.scale = Settings.scale !== undefined ? Settings.scale : {r: '?', t: '/scale-to-width-down/350?'}; //container (#WikiaMainContent, #mw-content-text etc) Settings.dock = !!Settings.dock ? Settings.dock : '#mw-content-text'; Settings.RegExp = Settings.RegExp || {}; //regexps //images 2 ignore Settings.RegExp.iimages = Settings.RegExp.iimages || []; //pages 2 ignore Settings.RegExp.ipages = Settings.RegExp.ipages || []; //links 2 ignore Settings.RegExp.ilinks = Settings.RegExp.ilinks || []; //Settings.RegExp.hash = Settings.RegExp.hash || new RegExp('#.*'); Settings.RegExp.wiki = Settings.RegExp.wiki || new RegExp('\/wiki\/', 'i'); //delete tags Settings.RegExp.dtag = Settings.RegExp.dtag || new RegExp('<.*>','gm'); //preprocess data (remove scripts) Settings.RegExp.prep = Settings.RegExp.prep || new RegExp(' .*? ','igm'); var ncache = []; //{href, data} var loc = {lefts: 5, tops: 5}; //left: x, top: y, lefts: left-shift, clientx var currentEl = {}; //{href, ?data} //var api = new mw.Api; var apiUri = new mw.Uri({path: '/api.php'}); var thisPage = createUri(location).truepath; //exports Settings.wrapper = wrapper; Settings.context = this; Settings.f = {init: init, main: main, createuri: createUri, getpreview: ngetPreview, showpreview: nshowPreview, hidepreview: nhidePreview, cache: ncache, ignoreImage: nignoreImage, ignorepage: nignorePage, ignorelink: nignoreLink, cacheof: ncacheOf}; mw.loader.using(['mediawiki.util'], init);

function init { //should i ignore this page if (nignorePage(thisPage)) { mw.hook('wikipage.content').remove(main); if (Settings.debug) console.log('pp. ignore ', thisPage); return; }       //run once if (window.pPreview && window.pPreview.version) return; Settings.version = '1.0b'; window.pPreview = Settings; importArticles({type: "style", articles: ["u:dev:LinkPreview/code.css"]}); if (Settings.debug) { console.log('pp rmain'); Settings.cache = ncache; }       Settings.RegExp.ilinks.push(thisPage); // ignore this page Settings.RegExp.ilinks.push(new RegExp(apiUri.path)); //ignore unknown //ajaxrc support window.ajaxCallAgain = window.ajaxCallAgain || []; window.ajaxCallAgain.push(main); mw.hook('wikipage.content').add(main); //main; } //init function main { //main $(Settings.dock).find('a').each(function{           var el = $(this);            var ahref = el.attr('href');            if (Settings.debug) console.log('pp. main. el.h: ', ahref);           if (!ahref) return;            ahref = createUri(ahref);            if (Settings.debug) console.log('pp. main.uri: ', ahref);            if (ahref && (ahref.hostname === apiUri.host) && !nignoreLink(ahref.truepath)) { //internal link               el.hover(aHover, nhidePreview);            } // if internal link        }); //each a    } //main function createUri (href, base) { var h;       if (URL) { //if not ie actually h = new URL(href, base || apiUri.clone.extend({path:''}).toString); } else { try { h = new mw.Uri(href); h.pathname = h.path; h.hostname = h.host; } catch (e) { h = undefined; if (Settings.debug) console.log('pp.createUrl.e: ', e); }       } //if url if (h) { h.truepath = decodeURIComponent(h.pathname.replace(Settings.RegExp.wiki, '')); }       return h;    } //createUri function escapeRegExp(str) { return str.replace(/([.*+?^=!:${}|\[\]\/\\])/g, "\\$1"); } //escapeRegExp function aHover (ev) { //a hover handler ev.stopPropagation; currentEl.href = createUri($(ev.currentTarget).attr('href')).truepath; //if link determined be ignored if (nignoreLink(currentEl.href)) { return true; } //if ignore link //set coords loc.left = ev.pageX; loc.top = ev.pageY; loc.clientX = ev.clientX; loc.clientY = ev.clientY; if (Settings.debug) { console.log('pp.ahover ev:', ev, 'cel:', currentEl); }       setTimeout(ngetPreview.bind(this, ev), Settings.delay); return false; } //ahover function getObj (data, key) { //traverse through object tree var ret = [], r;       for (var k in data) { if (data[k] instanceof Object) { if (k === key) { ret.push(data[k]); }               r=getObj(data[k], key); if (r) ret=ret.concat(r); } //if obj } //for k in data return ret; } //getObj function getVal (data, key) { //travers through object tree var ret = [], r;       for (var k in data) { if (data[k] instanceof Object) { r=getVal(data[k], key); if (r) { ret=ret.concat(r); }           } else { if (k === key) { ret.push(data[k]); }           } //if obj } //for k in data return ret; } //getVal function hlpPreview (href, div, img, force) { //preview helper //load img and add to div var im, d;       im = $('img', div); if (img) { //let vignette do scale im.attr('src', Settings.scale ? img.replace(Settings.scale.r, Settings.scale.t) : img); } else { im.attr('src', Settings.noimage); im.addClass('npage-preview-noimage'); } //if img d = {href: href, data: div}; ncache.push(d); if (Settings.debug) window.pPreview.pdiv = d.data; nshowPreview(d.data, d.href, force); } //hlpPreview function ngetPreview (ev, forcepath) { var nhref = forcepath || createUri($(ev.currentTarget).attr('href')).truepath; //save bandwith if (Settings.debug) console.log('pp.gp href: ', nhref, ' curel.href: ', currentEl.href, nhref===currentEl.href); if (!forcepath && (nhref != currentEl.href)) return; var ndata = ncacheOf(nhref); if (Settings.debug) console.log('pp.gp x:'+loc.left+' y:'+loc.top); if (ndata) { if (Settings.debug) console.log('pp.gp show preview', ndata); nshowPreview(ndata.data, nhref, forcepath ? true : false); return false; } //if data //get data var apipage = apiUri.clone.extend({action: 'parse', page: nhref, section: 0,                   prop: 'images|text', format: 'json', disablepp: , redirects: }); if (Settings.debug) console.log('pp.gp apip: ', apipage.toString); $.getJSON(apipage).done(function(data){           //parse: {text: {*: text}, images: []}            if (!data.parse) {                if (Settings.debug) console.log('pp.gp apip. no valid data in', data);               Settings.RegExp.ilinks.push(nhref); //and ignore it                return this;            }            var img = data.parse.images.map(function(value, index){ if (nignoreImage(value)) { return false; } else { return value; }           }).filter(Boolean)[0];            //img = $(img);            var text = data.parse.text['*'];            if (Settings.debug) {                console.log('pp.gp apip img:', img, 'text:', {text: text});            }            if (!img && !text) {return this;}            //preprocess (cleanup)            text = text.replace(Settings.RegExp.prep, );            text = $(' ', {class: 'tmpdivclass', style: 'visibility:hidden;display:none;'}).html(text);            text.find('aside').prevAll.remove;            text.find('aside').remove;            //convert 2 text            text = text.text;            //text clean up            text = text ? text.replace(Settings.RegExp.dtag, ) : '';            text = text.trim.substr(0, Settings.tlen);            if (Settings.debug) {                Settings.pptext = text;                Settings.ppdata = data;                console.log('pp.gp img: ', img, ' text: ', {text: text}); }           var div = $(' ', {class: 'npage-preview'}); var d = {}, twrap, iwrap; if (text.length > 0) { twrap = $(' ', {text: text}); //wrap.text(text); div.append(twrap); } //if text iwrap = $(' ', {src: Settings.defimage}); div.prepend(iwrap); if (img) { //action=query&titles=file:.jpg&iiprop=url&prop=imageinfo&format=xml var im = 'file:' + img.trim; var apiimage = apiUri.clone.extend({action: 'query', redirects: '',                           titles: im, iiprop: 'url', prop: 'imageinfo', format: 'json'}); if (Settings.debug) { console.log('pp.gp apii: ', apiimage.toString); }               $.getJSON(apiimage.toString).done(function(data) {                    if (Settings.debug) {                        console.log('pp.gp apii done: ', data);                    }                    var im, d1;                    d1 = data.query;                    if (d1.redirects) {                        var imRed = getVal(getObj(d1, 'redirects'), 'to');                        if (Settings.debug) console.log('pp.gp img redir to ', imRed);                        if (imRed.length > 0) {                            imRed = imRed[0];                        } else {                            //no url found                            iwrap.attr('src', Settings.noimage);                            if (Settings.debug) console.log('pp.gp img redir.to not found in ', d1);                            return this;                        }                        var apiim = apiimage.clone.extend({titles: imRed}); //resolve redirect if (Settings.debug) { console.log('pp.gp resolv redir: ', apiim.toString); }                       $.getJSON(apiim.toString, function(data) {                            var im = getVal(getObj(data, 'pages'), 'url');                            if (im.length > 0) {                                im = im[0];                            } else {                                //no url found. again                                im = false;                            }                            hlpPreview(nhref, div, im, forcepath ? true : false);                       }); //getjson. resolve redirect } else { im = getVal(getObj(d1, 'imageinfo'), 'url'); if (im.length > 0) { im = im[0]; } else { im = false; }                       hlpPreview(nhref, div, im, forcepath ? true : false); } //if redirects return this; //should be promise. but well }).fail(function(obj, stat, err){ if (Settings.debug) { console.log('pp.gp img api fail', obj, stat, err); }                   hlpPreview(nhref, div, false, forcepath ? true : false); return this; });           } else { //no img                hlpPreview(nhref, div, false, forcepath ? true : false);           }//if img        });//get page data return false; } //getpreview function nshowPreview (data, target, force) { if (Settings.debug) console.log('pp.sp ', data, target, force); if (!force && (currentEl.href !== target)) return false; //other hover processing yet if (Settings.debug) { console.log('pp.sp data: ', data); }       $('body').append($(data));

//prehide data $(data).css({left: -10000, top: -10000}); $(data).show(200, function{ //;//fadeIn('fast');           if (loc.clientY > $(window).height / 2 ) {                loc.top -= $(data).height - loc.tops;            } else {                loc.top += loc.tops;            }//if top>window            if (loc.clientX > $(window).width / 2 ) {                loc.left -= $(data).width - loc.lefts;            } else {                loc.left += loc.lefts;            }//if left>window            //move preview to target location            $(data).css({ left: force ? $('body').scrollLeft : loc.left, top: force ? $('body').scrollTop : loc.top});       });//data.show.done } //showpreview function nhidePreview (data) { currentEl.href = ''; $('.npage-preview').remove; } //hidepreview function nignoreImage (name) { //true if image should be ignore //name = name.replace(/(file):/im, ''); //name = name.charAt(0).toUpperCase + name.slice(1); for (var i=0, len=Settings.RegExp.iimages.length; i<len; i++) { if (Settings.RegExp.iimages[i] instanceof RegExp) { if (Settings.RegExp.iimages[i].test(name)) return true; } else { if (name === Settings.RegExp.iimages[i]) return true; } //if regexp }       return false; } //nignoreimage function nignorePage (name) { //true if page should be ignore var a = Settings.RegExp.ipages; for (var i=0, len=a.length; i<len; i++) { if (a[i] instanceof RegExp) { if (a[i].test(name)) return true; } else { if (name === a[i]) return true; } //if regexp }       return false; } //nignorepage function nignoreLink (name) { //true if link should be ignore var a = Settings.RegExp.ilinks; for (var i=0, len=a.length; i Settings.csize) ncache = []; //clear cache for (var i=0, len=ncache.length; i<len; i++) { if (ncache[i].href === href) { if (Settings.debug) console.log('pp.cache found: ' + href + ' data: ', ncache[i].data); //window.ppcdata = ncache[i]; return ncache[i]; }       }        return null; } //ncacheof })(jQuery);