The official discord link if you wish to join the discord: https://discord.gg/j5RKwCvAFu
Support the wiki on our official Ko-Fi page or Patreon page!
MediaWiki:Common.js: Difference between revisions
From The Codex
Line 215: | Line 215: | ||
}); | }); | ||
}(); | }(); | ||
/* <pre> */ | |||
/* smth like mw:Extension:Popups */ | |||
/* Based off of dev:LinkPreview, but without the Russian error image */ | |||
/* popup on link:hover */ | |||
/* maintainer: user:fngplg */ | |||
/* classes: main: npage-preview, image not found: npage-preview-noimage */ | |||
/* img: <img>, text: <div> */ | |||
(function wrapper ($) { | |||
var Settings = window.pPreview || {}, | |||
mwc = mw.config.get(['wgScriptPath', 'wgSassParams', 'wgArticlePath']); | |||
Settings.debug = $.getUrlVar('debug') || (Settings.debug !== undefined ? Settings.debug : false); | |||
// killswitch | |||
Settings.dontrun = $.getUrlVar('nolp'); | |||
if (Settings.dontrun) return; | |||
//default values | |||
var Defaults = { | |||
dock: '#mw-content-text, #article-comments', | |||
defimage: 'https://vignette.wikia.nocookie.net/borderlands/images/0/05/Ajax.gif/revision/latest/scale-to-width-down/350?cb=20170626182120&path-prefix=ru', | |||
noimage : 'https://vignette.wikia.nocookie.net/debatesjungle/images/8/88/Blanko.png/revision/latest?cb=20200510102636', | |||
};//defaults | |||
var pp = {}; | |||
pp.sync = []; //synchronization element | |||
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: mwc.wgScriptPath + '/api.php'}); | |||
//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, chkimagesrc: chkImageSrc, preprocess: preprocess, | |||
elvalidate: elValidate}; | |||
mw.loader.using(['mediawiki.util'], init); | |||
function log () { | |||
var a = [].slice.call(arguments); | |||
a.unshift('pp'); | |||
if (Settings.debug) console.log.apply(this, a); | |||
}//log | |||
pp.start = function (e) { | |||
//allows (true) processing for element e | |||
if (e) { | |||
if (pp.sync.indexOf(e) > -1) { | |||
return false; | |||
} | |||
} | |||
Settings.process = true; | |||
pp.sync.push(e || Settings.process); | |||
return true; | |||
};//start | |||
pp.stop = function (e) { | |||
hlpaHover(); | |||
var epos = pp.sync.indexOf(e); | |||
if (epos !== -1) { | |||
// remove e from sync array | |||
pp.sync.splice(epos, 1); | |||
} else { | |||
// remove something; stack presumed | |||
pp.sync.splice(0, 1); | |||
} | |||
if (pp.sync.length === 0) { | |||
Settings.process = false; | |||
} | |||
};//stop | |||
pp.cachedupl = function () { | |||
//check cache for href duplication | |||
var el = null; | |||
outer: | |||
for (var i = 0, len = ncache.length; i < len; i++) { | |||
for (var k = i + 1; k < len; k++) { | |||
if (ncache[i].href === ncache[k].href) { | |||
el = {v: ncache[i].href, i: i, k: k}; | |||
break outer; | |||
} | |||
}//k inner loop | |||
}//i outer loop | |||
if (el) { | |||
console.log('pp.cachedupl found', el.v, el.i, el.k); | |||
} | |||
};//cachedupl | |||
function init () { | |||
if (window.pPreview && window.pPreview.version) { | |||
log('init dbl run protection triggered'); | |||
return; | |||
} | |||
Settings.version = '1.61'; | |||
log('init vrsn:', Settings.version); | |||
//use api.v1/article/details | |||
Settings.apid = Settings.apid !== undefined ? Settings.apid : false; | |||
//show preview delay, ms | |||
Settings.delay = Settings.delay !== undefined ? Settings.delay : 100; | |||
//suppress hover events for x ms | |||
//Settings.throttling = timeout until x | |||
Settings.throttle = Settings.throttle !== undefined ? Settings.throttle : 100; | |||
Settings.throttling = false; | |||
Settings.process = false;//processing data | |||
Settings.tlen = Settings.tlen !== undefined ? Settings.tlen : 1000; //max text length | |||
//do not remove portable infobox on preprocess stage | |||
Settings.pibox = Settings.pibox !== undefined ? Settings.pibox : false; | |||
//cache size | |||
Settings.csize = Settings.csize !== undefined ? Settings.csize : 100; | |||
Settings.defimage = Settings.defimage !== undefined ? Settings.defimage : Defaults.defimage; //default image path | |||
//no image found. class: npage-preview-noimage | |||
Settings.noimage = Settings.noimage !== undefined ? Settings.noimage : Defaults.noimage; | |||
//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 : Defaults.dock; | |||
//parse whole page. debug purposes mainly | |||
Settings.wholepage = $.getUrlVar('wholepage') || (Settings.wholepage !== undefined ? Settings.wholepage : false); | |||
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 || []; | |||
//parents to ignore | |||
Settings.RegExp.iparents = Settings.RegExp.iparents || ['[id^=flytabs] .tabs']; | |||
//classes to ignore | |||
Settings.RegExp.iclasses = Settings.RegExp.iclasses || []; | |||
//content to process. non-exclusive inclusion | |||
Settings.RegExp.onlyinclude = Settings.RegExp.onlyinclude || []; | |||
//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 || []; | |||
//set len restriction for apid.abstract | |||
if (Settings.apid) { | |||
Settings.tlen = (Settings.tlen > 500) ? 500 : Settings.tlen; | |||
} | |||
//ensure #mw-content-text is processed | |||
Settings.fixContentHook = Settings.fixContentHook !== undefined ? Settings.fixContentHook : true; | |||
window.pPreview = Settings; | |||
var thisPage = (createUri(location) || {}).truepath; | |||
//should i ignore this page | |||
if (!thisPage || nignorePage(thisPage)) { | |||
mw.hook('wikipage.content').remove(main); | |||
log('ignore', thisPage); | |||
return; | |||
} | |||
//run once | |||
//dump sass params | |||
var sasses = ''; | |||
$.each(mwc.wgSassParams, function(k, v) { | |||
sasses = sasses + '--sass-' + k + ':' + v + ';\n'; | |||
});//each sassparam | |||
if (sasses.length) { | |||
sasses = ':root {\n' + sasses + '}'; | |||
mw.util.addCSS(sasses); | |||
} | |||
log('sasses', {sasses: sasses}); | |||
importArticle({type: 'style', article: 'u:dev:MediaWiki:LinkPreview.css'}); | |||
log('rmain'); | |||
if (Settings.debug) { | |||
Settings.cache = ncache; | |||
} | |||
Settings.RegExp.ilinks.push(thisPage); // ignore this page | |||
Settings.RegExp.ilinks.push(new RegExp(apiUri.path)); //ignore unknown | |||
var r; | |||
if (Settings.RegExp.prep instanceof RegExp) { | |||
r = Settings.RegExp.prep; | |||
Settings.RegExp.prep = [r]; | |||
}//if regexp.prep is regexp | |||
if (!(Settings.RegExp.prep instanceof Array)) { | |||
Settings.RegExp.prep = []; | |||
}//if regexp.prep is not array | |||
Settings.RegExp.prep.push(/<script>[\s\S]*?<\/script>/igm); | |||
Settings.RegExp.prep.push(/<ref>[\s\S]*?<\/ref>/igm); | |||
Settings.defimage = chkImageSrc(Settings.defimage) ? Settings.defimage : Defaults.defimage; | |||
Settings.noimage = chkImageSrc(Settings.noimage) ? Settings.noimage : Defaults.noimage; | |||
Settings.f.pp = pp; | |||
//ajaxrc support | |||
window.ajaxCallAgain = window.ajaxCallAgain || []; | |||
window.ajaxCallAgain.push(main); | |||
mw.hook('wikipage.content').add(main); | |||
mw.hook('ppreview.ready').fire(Settings); | |||
//main(); | |||
} //init | |||
function main ($cont) { | |||
//main | |||
log('main', $cont); | |||
if (Settings.fixContentHook && $cont && $cont.length) { | |||
Settings.fixContentHook = false; | |||
if ($cont.selector !== '#mw-content-text') { | |||
log('main fixcontent', $cont); | |||
main($('#mw-content-text')); | |||
} | |||
} | |||
var $content, arr = []; | |||
//gather dock sites to one array | |||
Settings.dock.split(',').forEach(function (v) { | |||
var $c = {}; | |||
if ($cont) { | |||
// if $cont belongs to dock container | |||
$c = ($cont.is(v) || $cont.parents(v).length) ? $cont : {}; | |||
} else { | |||
// get whole dock. if main() called w\o params | |||
$c = $(v); | |||
}// if $cont. instead of $cont ? .is || .len ? : : | |||
$.merge(arr, $c); | |||
});// each dock | |||
$content = $(arr); | |||
log('main.c:', $content); | |||
$content.find('a').each(function() { | |||
var $el = $(this); | |||
if (elValidate($el)) { //internal link | |||
//$el.hover(aHover, nhidePreview); | |||
$el.off('mouseenter.pp mouseleave.pp'); | |||
$el.on('mouseenter.pp', aHover); | |||
$el.on('mouseleave.pp', nhidePreview); | |||
} // if internal link | |||
}); //each a | |||
} //main | |||
function elValidate ($el) { | |||
//returns false if element should be ignored | |||
var ahref = $el.attr('href'), | |||
bstop = false; | |||
//log('elValidate. el.h:', ahref); | |||
if (!ahref) return false; | |||
ahref = createUri(ahref); | |||
//log('elValidate.uri:', ahref); | |||
if (!ahref || (ahref.hostname !== apiUri.host) || nignoreLink(ahref.truepath)) { | |||
return false; | |||
} | |||
//chk classes | |||
if ($.isArray(Settings.RegExp.iclasses)) { | |||
Settings.RegExp.iclasses.forEach(function(v) { | |||
if ($el.hasClass(v)) { | |||
log('elValidate classes', v, ahref.truepath); | |||
//Settings.RegExp.ilinks.push(ahref.truepath); | |||
bstop = true; | |||
} | |||
}); | |||
} | |||
//log('elValidate classes', bstop); | |||
if (bstop) return false; | |||
//chk parents | |||
if ($.isArray(Settings.RegExp.iparents)) { | |||
Settings.RegExp.iparents.forEach(function(v) { | |||
if ($el.parents(v).length) { | |||
log('elValidate parents', v, ahref.truepath); | |||
//Settings.RegExp.ilinks.push(ahref.truepath); | |||
bstop = true; | |||
} | |||
}); | |||
} | |||
//log('elValidate parents', bstop); | |||
if (bstop) return false; | |||
return true; | |||
}//elValidate | |||
function chkImageSrc (src) { | |||
//is src belongs to wikia | |||
if (!src) return false; | |||
var url; | |||
try { | |||
url = new mw.Uri(src); | |||
return (/(\.wikia\.(com|org)|\.fandom\.com|\.wikia\.nocookie\.net)$/.test(url.host)); | |||
} | |||
catch (e) { | |||
return false; | |||
} | |||
return false; | |||
}//chkimagesrc | |||
function preprocess (text) { | |||
//prep must be non-empty array (script removing at least, added in the init) | |||
if (!(Settings.RegExp.prep instanceof Array) || Settings.RegExp.prep.length < 1) return ''; | |||
var s = text, | |||
$s = $('<div>').html(s); | |||
//process exclusive items | |||
//must be done before trash tag processing. because of reasons | |||
if (Settings.RegExp.onlyinclude && (Settings.RegExp.onlyinclude instanceof Array)) { | |||
/* exclusive | |||
Settings.RegExp.onlyinclude.forEach(function (v) { | |||
var $v = $s.find(v); | |||
if ($v.length) $s = $v;//call it exclusive | |||
}); | |||
s = $s.html(); | |||
*/ | |||
/* non-exclusive set */ | |||
s = Settings.RegExp.onlyinclude.map(function(v) { | |||
var $v = $s.find(v); | |||
if ($v.length) { | |||
$s.remove(v); | |||
return $v.map(function() {return this.outerHTML}).toArray().join(); | |||
} else { | |||
return false; | |||
} | |||
}) | |||
.filter(Boolean).join() || s; | |||
}//if RegExp.onlyinclude | |||
Settings.RegExp.prep.forEach(function (v) { | |||
s = s.replace(v, ''); | |||
}); | |||
return s; | |||
}//preprocess | |||
function createUri (href, base) { | |||
var h; | |||
try { | |||
h = new mw.Uri(href.toString()); | |||
h.pathname = h.path; | |||
h.hostname = h.host; | |||
} catch (e) { | |||
h = undefined; | |||
log('createUrl.e', e); | |||
} | |||
if (h) { | |||
try { | |||
h.truepath = decodeURIComponent(h.pathname.replace(Settings.RegExp.wiki, '')); | |||
h.interwiki = h.path.split('/wiki/')[0]; | |||
h.islocal = mwc.wgArticlePath.split('/wiki/')[0] === h.interwiki; | |||
} | |||
catch (e) { | |||
h = undefined; | |||
log('createuri decode.e', e, h, String(h)); | |||
} | |||
} | |||
return h; | |||
} //createUri | |||
function escapeRegExp(str) { | |||
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); | |||
} //escapeRegExp | |||
function hlpaHover () { | |||
//aHover helper | |||
if (Settings.throttling) { | |||
clearTimeout(Settings.throttling); | |||
Settings.throttling = false; | |||
} | |||
}//hlpaHover | |||
function aHover (ev) { | |||
//a hover handler | |||
ev.stopPropagation(); | |||
log('ahover ', Settings.throttling, currentEl.href); | |||
//suppress some events | |||
if (Settings.throttling || Settings.process) { | |||
return false; | |||
} | |||
Settings.throttling = setTimeout(hlpaHover, Settings.throttle); | |||
var hel = createUri($(ev.currentTarget).attr('href')) || {}; | |||
//if link already in process | |||
if (hel && hel.truepath && currentEl.href == hel.truepath) { | |||
return false; | |||
} | |||
currentEl.href = hel.truepath; | |||
currentEl.islocal = hel.islocal; | |||
currentEl.interwiki = hel.interwiki; | |||
//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; | |||
log('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 (uri, div, img, force, withD) { | |||
//preview helper | |||
//load img and add to div | |||
var im, d; | |||
im = $('img', div); | |||
if (!Settings.apid && !withD) { | |||
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 | |||
}// if !apid | |||
d = {href: uri.truepath, data: div, uri: uri}; | |||
ncache.push(d); | |||
if (Settings.debug) window.pPreview.pdiv = d.data; | |||
nshowPreview(d.data, d.uri, force); | |||
pp.stop(d.href); | |||
} //hlpPreview | |||
function ngetPreview (ev, forcepath, withD) { | |||
var nuri = createUri($(ev.currentTarget).attr('href')) || {}; | |||
nuri.truepath = forcepath || nuri.truepath; | |||
if (!nuri || !nuri.truepath) { | |||
log('gp no href', ev, forcepath); | |||
return; | |||
} | |||
if (!pp.start(nuri.truepath)) { | |||
//this href already started to process | |||
log('gp suppressed dbl processing for', nuri); | |||
return; | |||
} | |||
//save bandwith | |||
log('gp uri: ', nuri, ' curel.href: ', currentEl.href, nuri.truepath === currentEl.href, 'd:', withD); | |||
// withd means fallback request, that should not be cancelled early | |||
if (!forcepath && !withD && (nuri.truepath != currentEl.href)) { | |||
pp.stop(nuri.truepath); | |||
return; | |||
} | |||
var ndata = ncacheOf(nuri.truepath); | |||
log('gp x:', loc.left, 'y:', loc.top); | |||
if (ndata) { | |||
log('gp show preview', ndata); | |||
nshowPreview(ndata.data, nuri, forcepath ? true : false); | |||
pp.stop(nuri.truepath); | |||
return false; | |||
} //if data | |||
//get data | |||
var apipage, | |||
iwrap = $('<img>', {src: Settings.defimage}), | |||
twrap = $('<div/>'), | |||
div = $('<div/>', {class: 'npage-preview'}); | |||
if (Settings.apid || withD) { | |||
apipage = new mw.Uri(nuri.interwiki + '/api/v1/Articles/Details'); | |||
apipage.extend({titles: nuri.truepath, abstract: Math.min(Settings.tlen, 500)}); | |||
log('gp apid', apipage); | |||
$.getJSON(apipage).done(function(data) { | |||
if (!data || data.error) { | |||
log('gp apid.error', nuri, data); | |||
Settings.RegExp.ilinks.push(nuri.truepath); //and ignore it | |||
pp.stop(nuri.truepath); | |||
return this; | |||
} | |||
var item = data.items[Object.keys(data.items)[0]]; | |||
if (!item) { | |||
log('gp apid.noitem', nuri, data); | |||
Settings.RegExp.ilinks.push(nuri.truepath); //and ignore it | |||
pp.stop(nuri.truepath); | |||
return this; | |||
} | |||
iwrap.attr('src', item.thumbnail || Settings.noimage); | |||
iwrap.addClass(item.thumbnail ? '' : 'npage-preview-noimage'); | |||
twrap.text(item.abstract); | |||
div.append(iwrap).append(twrap); | |||
hlpPreview(nuri, div, item.thumbnail, forcepath ? true : false, withD); | |||
return this; | |||
})// apid.done | |||
.fail(function(data) { | |||
log('gp apid.fail', nuri, data); | |||
Settings.RegExp.ilinks.push(nuri.truepath); //and ignore it | |||
pp.stop(nuri.truepath); | |||
return this; | |||
});// apid.fail | |||
return; | |||
} | |||
apipage = new mw.Uri({path: nuri.interwiki + '/api.php'}); | |||
apipage.extend({action: 'parse', page: nuri.truepath, | |||
prop: 'images|text', format: 'json', disablepp: '', redirects: ''}); | |||
if (!Settings.wholepage) apipage.extend({section: 0}); | |||
log('gp apip: ', apipage.toString()); | |||
$.getJSON(apipage).done(function(data) { | |||
//parse: {text: {*: text}, images: []} | |||
if (!data.parse) { | |||
log('gp apip. no valid data in', data); | |||
Settings.RegExp.ilinks.push(nuri.truepath); //and ignore it | |||
pp.stop(nuri.truepath); | |||
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['*']; | |||
log('gp apip img:', img, 'text:', {text: text}); | |||
if (!img && !text) { | |||
pp.stop(nuri.truepath); | |||
if (Settings.apid || withD) { | |||
Settings.RegExp.ilinks.push(nuri.truepath); //and ignore it | |||
return this; | |||
} else { | |||
// last try; via api.v1 | |||
return ngetPreview(ev, null, true); | |||
} | |||
} | |||
//preprocess (cleanup) | |||
text = preprocess(text); | |||
text = $('<div/>', {class: 'tmpdivclass', style: 'visibility:hidden;display:none;'}).html(text); | |||
if (!Settings.pibox) { //remove portable infobox | |||
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; | |||
log('gp img: ', img, ' text: ', {text: text}); | |||
} | |||
if (text.length > 0) { | |||
twrap.text(text); | |||
div.append(twrap); | |||
} //if text | |||
div.prepend(iwrap); | |||
if (img) { | |||
//action=query&titles=file:.jpg&iiprop=url&prop=imageinfo&format=xml | |||
var im = 'file:' + img.trim(); | |||
var apiimage = new mw.Uri({path: nuri.interwiki + '/api.php'}); | |||
apiimage.extend({action: 'query', redirects: '', | |||
titles: im, iiprop: 'url', prop: 'imageinfo', format: 'json'}); | |||
log('gp apii: ', apiimage.toString()); | |||
$.getJSON(apiimage.toString()).done(function(data) { | |||
log('gp apii done:', data); | |||
var im, d1; | |||
d1 = data.query; | |||
if (d1.redirects) { | |||
var imRed = getVal(getObj(d1, 'redirects'), 'to'); | |||
log('gp img redir to', imRed); | |||
if (imRed.length > 0) { | |||
imRed = imRed[0]; | |||
} else { | |||
//no url found | |||
iwrap.attr('src', Settings.noimage); | |||
log('gp img redir.to not found in', d1); | |||
return this; | |||
} | |||
var apiim = apiimage.clone().extend({titles: imRed}); | |||
//resolve redirect | |||
log('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(nuri, 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(nuri, div, im, forcepath ? true : false); | |||
} //if redirects | |||
return this; //should be promise. but well | |||
}).fail(function(obj, stat, err) { | |||
log('gp img api fail', obj, stat, err); | |||
hlpPreview(nuri, div, false, forcepath ? true : false); | |||
return this; | |||
});//img fail | |||
} else { //no img | |||
hlpPreview(nuri, div, false, forcepath ? true : false); | |||
}//if img | |||
})//get page data.done | |||
.fail(function(obj, stat, err){ | |||
log('pg get page data fail', obj, stat, err); | |||
pp.stop(nuri.truepath); | |||
});//get page data.fail | |||
//pp.stop(); | |||
return false; | |||
} //getpreview | |||
function nshowPreview (data, target, force) { | |||
log('sp', data, target, force); | |||
if (!force && (currentEl.href !== target.truepath)) { | |||
return false; //other hover processing yet | |||
} | |||
log('sp data:', data); | |||
//nhidePreview(); | |||
$('.npage-preview').remove(); //remove artefacts | |||
$('body').append($(data)); | |||
//prehide data | |||
$(data).css({left: -10000, top: -10000}); | |||
$(data).show(200, function() { //;//fadeIn('fast'); | |||
//reposition works well with pre-set fixed data bounds | |||
if ((loc.clientY + $(data).height()) > $(window).height()) { | |||
loc.top -= ($(data).height() + loc.tops); | |||
} else { | |||
loc.top += loc.tops; | |||
}//if top>window | |||
if ((loc.clientX + $(data).width()) > $(window).width()) { | |||
loc.left -= ($(data).width() + loc.lefts); | |||
} else { | |||
loc.left += loc.lefts; | |||
}//if left>window | |||
//move preview to target location | |||
log('sp loc', loc); | |||
loc.left = loc.left > 0 ? loc.left : 0; | |||
loc.top = loc.top > 0 ? loc.top : 0; | |||
$(data).css({ | |||
left: force ? $('body').scrollLeft() : loc.left, | |||
top: force ? $('body').scrollTop() : loc.top}); | |||
mw.hook('ppreview.show').fire(data); | |||
});//data.show.done | |||
} //showpreview | |||
function nhidePreview (data) { | |||
currentEl.href = ''; | |||
$('.npage-preview').remove(); | |||
//clear throttling | |||
hlpaHover(); | |||
} //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 < 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; | |||
} //nignorelink | |||
function ncacheOf (href) { | |||
//returns cached obj or null | |||
if (ncache.length > Settings.csize) ncache = []; //clear cache | |||
for (var i = 0, len = ncache.length; i < len; i++) { | |||
if (ncache[i].href === href) { | |||
log('cache found:', href, 'data:', ncache[i].data); | |||
//window.ppcdata = ncache[i]; | |||
return ncache[i]; | |||
} | |||
} | |||
return null; | |||
} //ncacheof | |||
})(jQuery); |
Revision as of 00:02, 17 May 2020
/* Any JavaScript here will be loaded for all users on every page load. */
window.AutoEditDropdownConfig = {
expandedAreaContribute: true,
expandedAreaEdit: false
};
window.ajaxRefresh = 60000;
window.ajaxPages = [
'Special:WikiActivity',
'Special:RecentChanges',
'Special:Contributions',
'Special:Log',
'Special:Log/move',
'Special:AbuseLog',
'Special:NewFiles',
'Special:NewPages',
'Special:Watchlist',
'Special:Statistics',
'Special:ListFiles',
'Category:Speedy_deletion_candidates',
'Category:Speedy_move_candidates'
];
window.AjaxRCRefreshText = 'Auto-refresh';
window.AjaxRCRefreshHoverText = 'Automatically refresh the page';
window.UserTagsJS = {
modules: {},
tags: {
// group: { associated tag data },
founder: {
u: 'Founder',
order: 1
},
bureaucrat: {
u: 'Bureaucrat',
order: 2
},
'former-bureaucrat': {
u: 'Retired Bureaucrat',
order: 3
},
sysop: {
u: 'Administrator',
order: 4
},
'former-sysop': {
u: 'Retired Administrator',
order: 5
},
'bot-global': {
u: 'Global Bot',
order: 7
},
bot: {
u: 'Bot',
order: 8
},
'content-moderator': {
u: 'Content Moderator',
order: 10
},
'former-content-moderator': {
u: 'Retired Content Moderator',
order: 11
},
threadmoderator: {
u: 'Discussion Moderator',
order: 13
},
'former-threadmoderator': {
u: 'Retired Discussion Moderator',
order: 14
},
chatmoderator: {
u: 'Chat Moderator',
order: 16
},
'former-chatmoderator': {
u: 'Retired Chat Moderator',
order: 17
},
rollback: {
u: 'Rollback',
order: 19
},
'former-rollback': {
u: 'Former Rollback',
order: 20
},
calc: {
u: 'Calc Group',
order: 22
},
'former-calc': {
u: 'Retired Calc Group',
order: 23
},
guru: {
u: 'Guru',
order: 40
},
'js-helper': {
u: 'JS Helper',
order: 51
},
'css-helper': {
u: 'CSS Helper',
order: 52
},
'image-helper': {
u: 'Image Helper',
order: 53
},
'template-helper': {
u: 'Templates Helper',
order: 54
},
'human-resources': {
u: 'Human Resources',
order: 55
},
'former-human-resources': {
u: 'Retired Human Resources',
order: 56
},
consultant: {
u: 'Consultant',
order: 100
},
bannedfromchat: {
u: 'Banned from Chat',
order: 500
},
inactive: {
u: 'Inactive',
order: 1 / 0
}
}
};
UserTagsJS.modules.inactive = {
days: 30,
// namespaces: [0, 'Talk', 'User', 'User talk', 'Forum'], // Only articles, talk pages, user pages, user talk pages or forums edits count, other Wikia namespace edits don't count
zeroIsInactive: true // 0 edits = inactive
};
UserTagsJS.modules.nonuser = (mediaWiki.config.get('skin') === 'monobook');
UserTagsJS.modules.autoconfirmed = true; // Switch on Autoconfirmed User check
UserTagsJS.modules.newuser = {
computation: function(days, edits) {
// If the expression is true then they will be marked as a new user
// If the expression is false then they won't.
// In this instance, newuser is removed as soon as the user gets 30
// edits, OR as soon as they have been present for 10 days, whichever
// happens first.
return days < 10 && edits < 30;
}
};
// NOTE: bannedfromchat displays in Oasis but is not a user-identity group so must be checked manually
UserTagsJS.modules.mwGroups = [
'bureaucrat',
'sysop',
'bot',
'bot-global',
'content-moderator',
'threadmoderator',
'chatmoderator',
'patroller',
'rollback',
'bannedfromchat'
];
UserTagsJS.modules.custom = {
'Paralysis of Lenuysis': ['former-content-moderator'],
'Sera EX': ['former-content-moderator'],
'Hykuu': ['former-content-moderator'],
'Emperoer but better': ['former-threadmoderator'],
'Game Master Battler': ['former-threadmoderator'],
'HyperNepsy': ['former-threadmoderator'],
'Sayo Yasuda': ['former-sysop'],
'LukaSolosYourVerse': ['former-sysop'],
'Just Shinza': ['former-sysop'],
'DarkNeon1994': ['former-bureaucrat'],
'Twilly18': ['former-bureaucrat'],
};
UserTagsJS.modules.metafilter = {
// Remove inactive from all bureaucrats, sysops, bots, global bots, staff, wikia utility and vstf
'inactive': [
'sysop',
'bureaucrat',
'bot',
'bot-global',
'staff',
'util',
'vstf'
],
'sysop': ['bot', 'bot-global'], // Remove "Administrator" tag from bots and global bots
'content-moderator': ['bureaucrat', 'sysop'], // Remove "Content Moderator" tag from bureaucrats and administrators
'threadmoderator': ['bureaucrat', 'sysop'], // Remove "Discussions Moderator" tag from bureaucrats and administrators
'chatmoderator': ['bureaucrat', 'sysop'], // Remove "Chat Moderator" tag from bureaucrats and administrators
'rollback': ['bureaucrat', 'sysop'] // Remove "Rollback" tag from bureaucrats and administrators
};
// UserTagsJS.modules.stopblocked = true; //Enabled by default
//Manually turn off by changing true -> false
// Username script //
(function () {
if (wgUserName !== null) $('span.insertusername').text(wgUserName);
})();
// Bureaucrat promotion warning message //
!function() {
if (wgCanonicalSpecialPageName !== 'Userrights') return;
$('#mw-content-text').on('change', '#wpGroup-bureaucrat', function() {
if ($('#wpGroup-bureaucrat').attr('checked') && !confirm('Do you truly want to appoint a bureaucrat?')) $('#wpGroup-bureaucrat').attr('checked', null);
});
}();
/* <pre> */
/* smth like mw:Extension:Popups */
/* Based off of dev:LinkPreview, but without the Russian error image */
/* popup on link:hover */
/* maintainer: user:fngplg */
/* classes: main: npage-preview, image not found: npage-preview-noimage */
/* img: <img>, text: <div> */
(function wrapper ($) {
var Settings = window.pPreview || {},
mwc = mw.config.get(['wgScriptPath', 'wgSassParams', 'wgArticlePath']);
Settings.debug = $.getUrlVar('debug') || (Settings.debug !== undefined ? Settings.debug : false);
// killswitch
Settings.dontrun = $.getUrlVar('nolp');
if (Settings.dontrun) return;
//default values
var Defaults = {
dock: '#mw-content-text, #article-comments',
defimage: 'https://vignette.wikia.nocookie.net/borderlands/images/0/05/Ajax.gif/revision/latest/scale-to-width-down/350?cb=20170626182120&path-prefix=ru',
noimage : 'https://vignette.wikia.nocookie.net/debatesjungle/images/8/88/Blanko.png/revision/latest?cb=20200510102636',
};//defaults
var pp = {};
pp.sync = []; //synchronization element
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: mwc.wgScriptPath + '/api.php'});
//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, chkimagesrc: chkImageSrc, preprocess: preprocess,
elvalidate: elValidate};
mw.loader.using(['mediawiki.util'], init);
function log () {
var a = [].slice.call(arguments);
a.unshift('pp');
if (Settings.debug) console.log.apply(this, a);
}//log
pp.start = function (e) {
//allows (true) processing for element e
if (e) {
if (pp.sync.indexOf(e) > -1) {
return false;
}
}
Settings.process = true;
pp.sync.push(e || Settings.process);
return true;
};//start
pp.stop = function (e) {
hlpaHover();
var epos = pp.sync.indexOf(e);
if (epos !== -1) {
// remove e from sync array
pp.sync.splice(epos, 1);
} else {
// remove something; stack presumed
pp.sync.splice(0, 1);
}
if (pp.sync.length === 0) {
Settings.process = false;
}
};//stop
pp.cachedupl = function () {
//check cache for href duplication
var el = null;
outer:
for (var i = 0, len = ncache.length; i < len; i++) {
for (var k = i + 1; k < len; k++) {
if (ncache[i].href === ncache[k].href) {
el = {v: ncache[i].href, i: i, k: k};
break outer;
}
}//k inner loop
}//i outer loop
if (el) {
console.log('pp.cachedupl found', el.v, el.i, el.k);
}
};//cachedupl
function init () {
if (window.pPreview && window.pPreview.version) {
log('init dbl run protection triggered');
return;
}
Settings.version = '1.61';
log('init vrsn:', Settings.version);
//use api.v1/article/details
Settings.apid = Settings.apid !== undefined ? Settings.apid : false;
//show preview delay, ms
Settings.delay = Settings.delay !== undefined ? Settings.delay : 100;
//suppress hover events for x ms
//Settings.throttling = timeout until x
Settings.throttle = Settings.throttle !== undefined ? Settings.throttle : 100;
Settings.throttling = false;
Settings.process = false;//processing data
Settings.tlen = Settings.tlen !== undefined ? Settings.tlen : 1000; //max text length
//do not remove portable infobox on preprocess stage
Settings.pibox = Settings.pibox !== undefined ? Settings.pibox : false;
//cache size
Settings.csize = Settings.csize !== undefined ? Settings.csize : 100;
Settings.defimage = Settings.defimage !== undefined ? Settings.defimage : Defaults.defimage; //default image path
//no image found. class: npage-preview-noimage
Settings.noimage = Settings.noimage !== undefined ? Settings.noimage : Defaults.noimage;
//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 : Defaults.dock;
//parse whole page. debug purposes mainly
Settings.wholepage = $.getUrlVar('wholepage') || (Settings.wholepage !== undefined ? Settings.wholepage : false);
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 || [];
//parents to ignore
Settings.RegExp.iparents = Settings.RegExp.iparents || ['[id^=flytabs] .tabs'];
//classes to ignore
Settings.RegExp.iclasses = Settings.RegExp.iclasses || [];
//content to process. non-exclusive inclusion
Settings.RegExp.onlyinclude = Settings.RegExp.onlyinclude || [];
//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 || [];
//set len restriction for apid.abstract
if (Settings.apid) {
Settings.tlen = (Settings.tlen > 500) ? 500 : Settings.tlen;
}
//ensure #mw-content-text is processed
Settings.fixContentHook = Settings.fixContentHook !== undefined ? Settings.fixContentHook : true;
window.pPreview = Settings;
var thisPage = (createUri(location) || {}).truepath;
//should i ignore this page
if (!thisPage || nignorePage(thisPage)) {
mw.hook('wikipage.content').remove(main);
log('ignore', thisPage);
return;
}
//run once
//dump sass params
var sasses = '';
$.each(mwc.wgSassParams, function(k, v) {
sasses = sasses + '--sass-' + k + ':' + v + ';\n';
});//each sassparam
if (sasses.length) {
sasses = ':root {\n' + sasses + '}';
mw.util.addCSS(sasses);
}
log('sasses', {sasses: sasses});
importArticle({type: 'style', article: 'u:dev:MediaWiki:LinkPreview.css'});
log('rmain');
if (Settings.debug) {
Settings.cache = ncache;
}
Settings.RegExp.ilinks.push(thisPage); // ignore this page
Settings.RegExp.ilinks.push(new RegExp(apiUri.path)); //ignore unknown
var r;
if (Settings.RegExp.prep instanceof RegExp) {
r = Settings.RegExp.prep;
Settings.RegExp.prep = [r];
}//if regexp.prep is regexp
if (!(Settings.RegExp.prep instanceof Array)) {
Settings.RegExp.prep = [];
}//if regexp.prep is not array
Settings.RegExp.prep.push(/<script>[\s\S]*?<\/script>/igm);
Settings.RegExp.prep.push(/<ref>[\s\S]*?<\/ref>/igm);
Settings.defimage = chkImageSrc(Settings.defimage) ? Settings.defimage : Defaults.defimage;
Settings.noimage = chkImageSrc(Settings.noimage) ? Settings.noimage : Defaults.noimage;
Settings.f.pp = pp;
//ajaxrc support
window.ajaxCallAgain = window.ajaxCallAgain || [];
window.ajaxCallAgain.push(main);
mw.hook('wikipage.content').add(main);
mw.hook('ppreview.ready').fire(Settings);
//main();
} //init
function main ($cont) {
//main
log('main', $cont);
if (Settings.fixContentHook && $cont && $cont.length) {
Settings.fixContentHook = false;
if ($cont.selector !== '#mw-content-text') {
log('main fixcontent', $cont);
main($('#mw-content-text'));
}
}
var $content, arr = [];
//gather dock sites to one array
Settings.dock.split(',').forEach(function (v) {
var $c = {};
if ($cont) {
// if $cont belongs to dock container
$c = ($cont.is(v) || $cont.parents(v).length) ? $cont : {};
} else {
// get whole dock. if main() called w\o params
$c = $(v);
}// if $cont. instead of $cont ? .is || .len ? : :
$.merge(arr, $c);
});// each dock
$content = $(arr);
log('main.c:', $content);
$content.find('a').each(function() {
var $el = $(this);
if (elValidate($el)) { //internal link
//$el.hover(aHover, nhidePreview);
$el.off('mouseenter.pp mouseleave.pp');
$el.on('mouseenter.pp', aHover);
$el.on('mouseleave.pp', nhidePreview);
} // if internal link
}); //each a
} //main
function elValidate ($el) {
//returns false if element should be ignored
var ahref = $el.attr('href'),
bstop = false;
//log('elValidate. el.h:', ahref);
if (!ahref) return false;
ahref = createUri(ahref);
//log('elValidate.uri:', ahref);
if (!ahref || (ahref.hostname !== apiUri.host) || nignoreLink(ahref.truepath)) {
return false;
}
//chk classes
if ($.isArray(Settings.RegExp.iclasses)) {
Settings.RegExp.iclasses.forEach(function(v) {
if ($el.hasClass(v)) {
log('elValidate classes', v, ahref.truepath);
//Settings.RegExp.ilinks.push(ahref.truepath);
bstop = true;
}
});
}
//log('elValidate classes', bstop);
if (bstop) return false;
//chk parents
if ($.isArray(Settings.RegExp.iparents)) {
Settings.RegExp.iparents.forEach(function(v) {
if ($el.parents(v).length) {
log('elValidate parents', v, ahref.truepath);
//Settings.RegExp.ilinks.push(ahref.truepath);
bstop = true;
}
});
}
//log('elValidate parents', bstop);
if (bstop) return false;
return true;
}//elValidate
function chkImageSrc (src) {
//is src belongs to wikia
if (!src) return false;
var url;
try {
url = new mw.Uri(src);
return (/(\.wikia\.(com|org)|\.fandom\.com|\.wikia\.nocookie\.net)$/.test(url.host));
}
catch (e) {
return false;
}
return false;
}//chkimagesrc
function preprocess (text) {
//prep must be non-empty array (script removing at least, added in the init)
if (!(Settings.RegExp.prep instanceof Array) || Settings.RegExp.prep.length < 1) return '';
var s = text,
$s = $('<div>').html(s);
//process exclusive items
//must be done before trash tag processing. because of reasons
if (Settings.RegExp.onlyinclude && (Settings.RegExp.onlyinclude instanceof Array)) {
/* exclusive
Settings.RegExp.onlyinclude.forEach(function (v) {
var $v = $s.find(v);
if ($v.length) $s = $v;//call it exclusive
});
s = $s.html();
*/
/* non-exclusive set */
s = Settings.RegExp.onlyinclude.map(function(v) {
var $v = $s.find(v);
if ($v.length) {
$s.remove(v);
return $v.map(function() {return this.outerHTML}).toArray().join();
} else {
return false;
}
})
.filter(Boolean).join() || s;
}//if RegExp.onlyinclude
Settings.RegExp.prep.forEach(function (v) {
s = s.replace(v, '');
});
return s;
}//preprocess
function createUri (href, base) {
var h;
try {
h = new mw.Uri(href.toString());
h.pathname = h.path;
h.hostname = h.host;
} catch (e) {
h = undefined;
log('createUrl.e', e);
}
if (h) {
try {
h.truepath = decodeURIComponent(h.pathname.replace(Settings.RegExp.wiki, ''));
h.interwiki = h.path.split('/wiki/')[0];
h.islocal = mwc.wgArticlePath.split('/wiki/')[0] === h.interwiki;
}
catch (e) {
h = undefined;
log('createuri decode.e', e, h, String(h));
}
}
return h;
} //createUri
function escapeRegExp(str) {
return str.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
} //escapeRegExp
function hlpaHover () {
//aHover helper
if (Settings.throttling) {
clearTimeout(Settings.throttling);
Settings.throttling = false;
}
}//hlpaHover
function aHover (ev) {
//a hover handler
ev.stopPropagation();
log('ahover ', Settings.throttling, currentEl.href);
//suppress some events
if (Settings.throttling || Settings.process) {
return false;
}
Settings.throttling = setTimeout(hlpaHover, Settings.throttle);
var hel = createUri($(ev.currentTarget).attr('href')) || {};
//if link already in process
if (hel && hel.truepath && currentEl.href == hel.truepath) {
return false;
}
currentEl.href = hel.truepath;
currentEl.islocal = hel.islocal;
currentEl.interwiki = hel.interwiki;
//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;
log('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 (uri, div, img, force, withD) {
//preview helper
//load img and add to div
var im, d;
im = $('img', div);
if (!Settings.apid && !withD) {
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
}// if !apid
d = {href: uri.truepath, data: div, uri: uri};
ncache.push(d);
if (Settings.debug) window.pPreview.pdiv = d.data;
nshowPreview(d.data, d.uri, force);
pp.stop(d.href);
} //hlpPreview
function ngetPreview (ev, forcepath, withD) {
var nuri = createUri($(ev.currentTarget).attr('href')) || {};
nuri.truepath = forcepath || nuri.truepath;
if (!nuri || !nuri.truepath) {
log('gp no href', ev, forcepath);
return;
}
if (!pp.start(nuri.truepath)) {
//this href already started to process
log('gp suppressed dbl processing for', nuri);
return;
}
//save bandwith
log('gp uri: ', nuri, ' curel.href: ', currentEl.href, nuri.truepath === currentEl.href, 'd:', withD);
// withd means fallback request, that should not be cancelled early
if (!forcepath && !withD && (nuri.truepath != currentEl.href)) {
pp.stop(nuri.truepath);
return;
}
var ndata = ncacheOf(nuri.truepath);
log('gp x:', loc.left, 'y:', loc.top);
if (ndata) {
log('gp show preview', ndata);
nshowPreview(ndata.data, nuri, forcepath ? true : false);
pp.stop(nuri.truepath);
return false;
} //if data
//get data
var apipage,
iwrap = $('<img>', {src: Settings.defimage}),
twrap = $('<div/>'),
div = $('<div/>', {class: 'npage-preview'});
if (Settings.apid || withD) {
apipage = new mw.Uri(nuri.interwiki + '/api/v1/Articles/Details');
apipage.extend({titles: nuri.truepath, abstract: Math.min(Settings.tlen, 500)});
log('gp apid', apipage);
$.getJSON(apipage).done(function(data) {
if (!data || data.error) {
log('gp apid.error', nuri, data);
Settings.RegExp.ilinks.push(nuri.truepath); //and ignore it
pp.stop(nuri.truepath);
return this;
}
var item = data.items[Object.keys(data.items)[0]];
if (!item) {
log('gp apid.noitem', nuri, data);
Settings.RegExp.ilinks.push(nuri.truepath); //and ignore it
pp.stop(nuri.truepath);
return this;
}
iwrap.attr('src', item.thumbnail || Settings.noimage);
iwrap.addClass(item.thumbnail ? '' : 'npage-preview-noimage');
twrap.text(item.abstract);
div.append(iwrap).append(twrap);
hlpPreview(nuri, div, item.thumbnail, forcepath ? true : false, withD);
return this;
})// apid.done
.fail(function(data) {
log('gp apid.fail', nuri, data);
Settings.RegExp.ilinks.push(nuri.truepath); //and ignore it
pp.stop(nuri.truepath);
return this;
});// apid.fail
return;
}
apipage = new mw.Uri({path: nuri.interwiki + '/api.php'});
apipage.extend({action: 'parse', page: nuri.truepath,
prop: 'images|text', format: 'json', disablepp: '', redirects: ''});
if (!Settings.wholepage) apipage.extend({section: 0});
log('gp apip: ', apipage.toString());
$.getJSON(apipage).done(function(data) {
//parse: {text: {*: text}, images: []}
if (!data.parse) {
log('gp apip. no valid data in', data);
Settings.RegExp.ilinks.push(nuri.truepath); //and ignore it
pp.stop(nuri.truepath);
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['*'];
log('gp apip img:', img, 'text:', {text: text});
if (!img && !text) {
pp.stop(nuri.truepath);
if (Settings.apid || withD) {
Settings.RegExp.ilinks.push(nuri.truepath); //and ignore it
return this;
} else {
// last try; via api.v1
return ngetPreview(ev, null, true);
}
}
//preprocess (cleanup)
text = preprocess(text);
text = $('<div/>', {class: 'tmpdivclass', style: 'visibility:hidden;display:none;'}).html(text);
if (!Settings.pibox) { //remove portable infobox
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;
log('gp img: ', img, ' text: ', {text: text});
}
if (text.length > 0) {
twrap.text(text);
div.append(twrap);
} //if text
div.prepend(iwrap);
if (img) {
//action=query&titles=file:.jpg&iiprop=url&prop=imageinfo&format=xml
var im = 'file:' + img.trim();
var apiimage = new mw.Uri({path: nuri.interwiki + '/api.php'});
apiimage.extend({action: 'query', redirects: '',
titles: im, iiprop: 'url', prop: 'imageinfo', format: 'json'});
log('gp apii: ', apiimage.toString());
$.getJSON(apiimage.toString()).done(function(data) {
log('gp apii done:', data);
var im, d1;
d1 = data.query;
if (d1.redirects) {
var imRed = getVal(getObj(d1, 'redirects'), 'to');
log('gp img redir to', imRed);
if (imRed.length > 0) {
imRed = imRed[0];
} else {
//no url found
iwrap.attr('src', Settings.noimage);
log('gp img redir.to not found in', d1);
return this;
}
var apiim = apiimage.clone().extend({titles: imRed});
//resolve redirect
log('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(nuri, 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(nuri, div, im, forcepath ? true : false);
} //if redirects
return this; //should be promise. but well
}).fail(function(obj, stat, err) {
log('gp img api fail', obj, stat, err);
hlpPreview(nuri, div, false, forcepath ? true : false);
return this;
});//img fail
} else { //no img
hlpPreview(nuri, div, false, forcepath ? true : false);
}//if img
})//get page data.done
.fail(function(obj, stat, err){
log('pg get page data fail', obj, stat, err);
pp.stop(nuri.truepath);
});//get page data.fail
//pp.stop();
return false;
} //getpreview
function nshowPreview (data, target, force) {
log('sp', data, target, force);
if (!force && (currentEl.href !== target.truepath)) {
return false; //other hover processing yet
}
log('sp data:', data);
//nhidePreview();
$('.npage-preview').remove(); //remove artefacts
$('body').append($(data));
//prehide data
$(data).css({left: -10000, top: -10000});
$(data).show(200, function() { //;//fadeIn('fast');
//reposition works well with pre-set fixed data bounds
if ((loc.clientY + $(data).height()) > $(window).height()) {
loc.top -= ($(data).height() + loc.tops);
} else {
loc.top += loc.tops;
}//if top>window
if ((loc.clientX + $(data).width()) > $(window).width()) {
loc.left -= ($(data).width() + loc.lefts);
} else {
loc.left += loc.lefts;
}//if left>window
//move preview to target location
log('sp loc', loc);
loc.left = loc.left > 0 ? loc.left : 0;
loc.top = loc.top > 0 ? loc.top : 0;
$(data).css({
left: force ? $('body').scrollLeft() : loc.left,
top: force ? $('body').scrollTop() : loc.top});
mw.hook('ppreview.show').fire(data);
});//data.show.done
} //showpreview
function nhidePreview (data) {
currentEl.href = '';
$('.npage-preview').remove();
//clear throttling
hlpaHover();
} //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 < 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;
} //nignorelink
function ncacheOf (href) {
//returns cached obj or null
if (ncache.length > Settings.csize) ncache = []; //clear cache
for (var i = 0, len = ncache.length; i < len; i++) {
if (ncache[i].href === href) {
log('cache found:', href, 'data:', ncache[i].data);
//window.ppcdata = ncache[i];
return ncache[i];
}
}
return null;
} //ncacheof
})(jQuery);