MediaWiki:Gadget-fastcci.js
Jump to navigation
Jump to search
Note: After saving, you have to bypass your browser's cache to see the changes. Internet Explorer: press Ctrl-F5, Mozilla: hold down Shift while clicking Reload (or press Ctrl-Shift-R), Opera/Konqueror: press F5, Safari: hold down Shift + Alt while clicking Reload, Chrome: hold down Shift while clicking Reload.
This user script seems to have a documentation page at MediaWiki:Gadget-fastcci and an accompanying .css page at MediaWiki:Gadget-fastcci.css. |
/**
* Add buttons to show all Featured pictures, Featured videos, Quality images, or Valued images
* in and below the current category.
* @author [[User:Dschwen]], 2014
*/
/* global mw, $ */
$( function () {
var $slider, $link, $controls, depthThreshold, request, modal,
// database backend url
serverList = [ '//fastcci1.wmflabs.org/', '//fastcci2.wmflabs.org/' ],
url = serverList[ Math.floor( Math.random() * serverList.length ) ],
base = '//upload.wikimedia.org/wikipedia/commons/thumb/',
badge = [
'e/e7/Cscr-featured.svg/24px-Cscr-featured.svg.png',
'a/a5/FV_invert_logo.png/24px-FV_invert_logo.png',
'8/8c/Quality_images_logo.svg/24px-Quality_images_logo.svg.png',
'd/d7/Valued_image_seal.svg/24px-Valued_image_seal.svg.png'
],
helpUrl = '//commons.wikimedia.org/wiki/Special:MyLanguage/Help:FastCCI',
helpIcon = '4/44/Help-browser.svg/24px-Help-browser.svg.png',
$e = $( '<div>' ).addClass( 'fastcci-results' ).on( 'click', '.fastcci-close', function () {
$e.empty();
tagLine( '' );
} ),
maxDepth = 0,
minDepth = Infinity,
// current namespace and action
ns = mw.config.get( 'wgNamespaceNumber' ),
namespaceIds = mw.config.get( 'wgNamespaceIds' ),
action = mw.config.get( 'wgAction' ),
// pageid of the current page
thisPageId = mw.config.get( 'wgArticleId' ),
uiLang = mw.config.get( 'wgUserLanguage' ),
$content = $( '#bodyContent, #mw-content-text' ).eq( 0 ),
$tagline = null,
i18nData = {
'Strong match': {
ar: 'تطابق قوی',
bn: 'জোরালো মিল',
ce: 'Къовламе цхьаьнадогӀуш',
cs: 'Bližší shoda',
de: 'Starke Übereinstimmung',
fa: 'تطابق قوی',
fr: 'Correspondance forte',
hr: 'Strogo podudaranje',
it: 'Corrispondenza forte',
mk: 'Строго совпаѓање',
ml: 'നന്നായി ചേർച്ചയുള്ളവ',
pt: 'Forte correspondência',
"pt-br": 'Forte correspondência',
ru: 'Строгое соответствие',
sv: 'Stark överensstämmelse',
zh: '强匹配'
},
'Weak match': {
ar: 'تطابق ضعيف',
bn: 'দুর্বল মিল',
ce: 'Ледара цхьаьнадогӀуш',
cs: 'Vzdálenější shoda',
de: 'Schwache Übereinstimmung',
fa: 'تطابق ضعیف',
fr: 'Correspondance faible',
hr: 'Približno podudaranje',
it: 'Corrispondenza debole',
mk: 'Благо совпаѓање',
ml: 'ചെറുതായി ചേർച്ചയുള്ളവ',
pt: 'Fraca correspondência',
"pt-br": 'Fraca correspondência',
ru: 'Слабое соответствие',
sv: 'Svag överensstämmelse',
zh: '弱匹配'
},
'No results.': {
ar: 'لا نتائج',
bn: 'কোন ফলাফল নেই',
ce: 'Хиламаш бац',
cs: 'Žádné výsledky.',
de: 'Keine Ergebnisse.',
es: 'Sin resultados.',
fa: 'هیچ نتیجه\u200cای.',
fr: 'Pas de résultats.',
hr: 'Nema rezultata.',
it: 'Nessun risultato.',
mk: 'Нема резултати.',
ml: 'ഫലങ്ങളൊന്നുമില്ല',
pt: 'Sem resultados.',
"pt-br": 'Sem resultados.',
ru: 'Нет результатов.',
sv: 'Inga resultat.',
tr: 'Sonuç yok.',
zh: '无结果。'
},
'Connecting...': {
ar: 'اتصال...',
bn: 'সংযোগ হচ্ছে...',
ce: 'Тасаялар...',
cs: 'Připojuji se...',
de: 'Verbinde ...',
es: 'Estableciendo conexión...',
fa: 'اتصال...',
fr: 'Connexion...',
hr: 'Povezujem se...',
it: 'Connessione...',
mk: 'Се поврзувам...',
ml: 'എടുക്കുന്നു...',
pt: 'A conectar...',
"pt-br": 'A conectar...',
ru: 'Соединение...',
sv: 'Ansluter...',
tr: 'Bağlanıyor...',
zh: '连接中...'
},
'Computing...': {
ar: 'حساب...',
bn: 'গণনা চলছে...',
ce: 'Таллам...',
cs: 'Pracuji...',
de: 'Arbeite ...',
es: 'Calculando...',
fa: 'محاسبه...',
fr: 'Calcul...',
hr: 'Računam...',
it: 'Calcolo...',
mk: 'Пресметувам...',
ml: 'കണക്കാക്കുന്നു...',
pt: 'A calcular...',
"pt-br": 'A calcular...',
ru: 'Анализ...',
sv: 'Beräknar...',
tr: 'Hesaplanıyor...',
zh: '计算中...'
},
'Digging through NUM files...': {
ar: 'جارِ البحث عن طريق ملفات NUM...',
bn: 'NUM ফাইলের মাধ্যমে সন্ধান...',
ce: 'Файлийн таллам, NUM терахьца ...',
cs: 'Prohledávám NUM souborů...',
de: 'Durchsuche NUM Dateien ...',
fa: 'استخراج از بین NUM پرونده...',
fr: 'Fouille dans NUM fichiers...',
hr: 'Pretražujem datoteke (NUM)...',
it: 'Ricerca nei file NUM...',
mk: 'Пребарувам по NUM податотеки...',
ml: 'NUM പ്രമാണങ്ങൾ പരിശോധിക്കുന്നു...',
pt: 'Procurando por ficheiros NUM...',
"pt-br": 'Procurando por ficheiros NUM...',
ru: 'Анализ файлов, числом NUM ...',
sv: 'Gräver genom NUM filer',
zh: '已查找了NUM个文件...'
},
'Waiting in line. NUM ahead of us.': {
ar: 'انتظار في الطابور. NUM تعمل حالياً.',
bn: 'লাইনে অপেক্ষারত। আমাদের NUM এগিয়ে',
ce: 'РогӀехь хьежар. Тхуна хьалхахь — NUM.',
cs: 'Čekáme na řadu, jsme NUM.',
de: 'Bitte warten, in der Warteschlange vor dir sind noch NUM Anfragen.',
fa: 'ایستادن در خط. NUM جلوی ما.',
fr: 'En attente. NUM requêtes devant nous.',
hr: 'Pričekajte u redu. Broj zahtjeva na čekanju: NUM.',
it: 'Attendi il tuo turno. Richieste NUM prima di noi.',
mk: 'Чекам во редица. Пред нас има NUM.',
ml: 'കാത്തിരിക്കുന്നു. NUM കൂടുതലാണ്.',
pt: 'Aguarde na fila. NUM à nossa frente.',
"pt-br": 'Aguarde na fila. NUM à nossa frente.',
ru: 'Ожидание в очереди. Перед нами — NUM.',
sv: 'Väntar i kö. NUM framför oss.',
zh: '队列中,前边有NUM个文件。'
},
'Advanced...': {
ar: 'متقدم...',
bn: 'উন্নত...',
ce: 'Хьалхадолу...',
cs: 'Pokročilé...',
de: 'Erweitert ...',
es: 'Avanzado...',
fa: 'پیشرفته...',
fr: 'Avancé...',
hr: 'Napredno...',
it: 'Avanzate...',
ml: 'വിപുലം...',
pt: 'Avançado...',
"pt-br": 'Avançado...',
ru: 'Продвигаемся...',
sv: 'Avancerat...',
tr: 'İlerliyor...',
zh: '高级...'
},
'Good pictures': {
ar: 'صور جيدة',
bn: 'ভালো চিত্রসমূহ',
ce: 'Дика суьрташ',
cs: 'Dobré obrázky',
de: 'Gute Bilder',
es: 'Imágenes buenas',
fa: 'تصاویر خوب',
fr: 'Bonnes images',
hr: 'Dobre slike',
it: 'Immagini belle',
mk: 'Добри слики',
ml: 'മികച്ച ചിത്രങ്ങൾ',
pt: 'Imagens boas',
"pt-br": 'Imagens boas',
ru: 'Хорошие изображения',
sv: 'Bra bilder',
tr: 'İyi resimler',
zh: '优秀图片'
},
'All images': {
ar: 'جميع الصور',
bn: 'সব চিত্রসমূহ',
ce: 'Массо суьрташ',
cs: 'Všechny obrázky',
de: 'Alle Bilder',
es: 'Todas las imágenes',
fa: 'همهٔ تصاویر',
fr: 'Toutes les images',
hr: 'Sve slike',
it: 'Tutte le immagini',
mk: 'Сите слики',
ml: 'എല്ലാ ചിത്രങ്ങളും',
pt: 'Todas as imagens',
"pt-br": 'Todas as imagens',
ru: 'Все изображения',
sv: 'Alla bilder',
tr: 'Tüm görseller',
zh: '全部图像'
},
'Featured pictures': {
ar: 'صور مختارة',
bn: 'নির্বাচিত চিত্রসমূহ',
ce: 'Хаьржина суьрташ',
cs: 'Nejlepší obrázky',
de: 'Exzellente Bilder',
es: 'Imágenes destacadas',
fa: 'تصاویر برگزیده',
fr: 'Images remarquables',
hr: 'Izabrane slike',
it: 'Immagini in vetrina',
mk: 'Избрани слики',
ml: 'തിരഞ്ഞെടുത്ത ചിത്രങ്ങൾ',
pt: 'Imagens em destaque',
"pt-br": 'Imagens em destaque',
ru: 'Избранные изображения',
sv: 'Utvalda bilder',
tr: 'Seçkin resimler',
zh: '特色图片'
},
'Featured videos': {
ar: 'مقاطع الفيديو المميزة',
bn: 'বৈশিষ্ট্যমূলক ভিডিওগুলি',
ce: 'истакнути видео снимци',
cs: 'Nejlepší videa',
de: 'Exzellente Videos',
es: 'Vídeos destacados',
fa: 'ویدئوهای برگزیده',
fr: 'Vidéo remarquables',
hi: 'विशेष रुप से प्रदर्शित वीडियो',
hr: 'istaknuti videozapisi',
it: 'Video in vetrina',
ja: '最高級の動画',
mk: 'најдобрите видеа',
ml: 'തിരഞ്ഞെടുത്ത വീഡിയോകൾ',
nds: 'Grootoordige Videos',
pl: 'Promowane filmy',
pt: 'Vídeos em Destaque',
"pt-br": 'Vídeos em Destaque',
ru: 'Избранные видео',
sv: 'Utvalda videoklipp',
tr: 'Seçkin videolar',
vi: 'video hay nhất',
zh: '精選視頻'
},
'Quality images': {
ar: 'صور الجودة',
bn: 'মানসম্মত চিত্রসমূহ',
ce: 'ЦӀена суьрташ',
cs: 'Kvalitní obrázky',
de: 'Qualitätsbilder',
es: 'Imágenes de calidad',
fa: 'تصاویر باکیفیت',
fr: 'Images de qualité',
hr: 'Kvalitetne slike',
it: 'Immagini di qualità',
mk: 'Квалитетни слики',
ml: 'മേന്മയേറിയ ചിത്രങ്ങൾ',
pt: 'Imagens de qualidade',
"pt-br": 'Imagens de qualidade',
ru: 'Качественные изображения',
sv: 'Kvalitetsbilder',
tr: 'Kaliteli görseller',
zh: '优质图像'
},
'Valued images': {
ar: 'صور قيمة',
bn: 'মূল্যবান চিত্রসমূহ',
ce: 'Мехала суьрташ',
cs: 'Hodnotné obrázky',
de: 'Wertvolle Bilder',
fa: 'تصاویر ارزشمند',
fr: 'Images de valeur',
hr: 'Cijenjene slike',
it: 'Immagini di valore',
mk: 'Ценети слики',
ml: 'മൂല്യമേറിയ ചിത്രങ്ങൾ',
pt: 'Imagens de valor',
"pt-br": 'Imagens de valor',
ru: 'Ценные иллюстрации',
sv: 'Värdefulla bilder',
tr: 'Değerli görseller',
zh: '最有价值图像'
},
'Find images': {
ar: 'البحث عن صور',
bn: 'চিত্রসমূহ খুঁজুন',
ce: 'Лаха сурт',
cs: 'Najít obrázky',
de: 'Finde Bilder',
es: 'Encontrar imágenes',
fa: 'یافتن تصاویر',
fr: 'Trouver les images',
hr: 'Pronađi slike',
it: 'Trova le immagini',
mk: 'Пронајди слики',
ml: 'ചിത്രങ്ങൾ എടുക്കുക:',
pt: 'Procurar imagens',
"pt-br": 'Procurar imagens',
ru: 'Поиск изображений',
sv: 'Hitta bilder',
tr: 'Görsel bul',
zh: '搜索图像'
},
'in this category': {
ar: 'في هذا التصنيف',
bn: 'এই বিষয়শ্রেণীতে',
ce: 'хӀокху категореш',
cs: 'v této kategorii',
de: 'aus dieser Kategorie',
es: 'en esta categoría',
fa: 'در این رده',
fr: 'dans cette catégorie',
hr: 'u ovoj kategoriji',
it: 'in questa categoria',
mk: 'во категоријава',
ml: 'ഈ വർഗ്ഗത്തിലെ',
pt: 'nesta categoria',
"pt-br": 'nesta categoria',
ru: 'в этой категории',
sv: 'i denna kategori',
tr: 'bu kategoride',
zh: '在本分类'
},
'and in': {
ar: 'أضف إلى',
bn: 'এবং এতে',
ce: 'кхин чохь',
cs: 'a zároveň v',
de: 'die auch sind in',
es: 'y en',
mk: 'и во',
fa: 'و در',
fr: 'et dans',
hr: 'kao i u',
it: 'e in',
ml: 'ഒപ്പം ഇതിലേയും',
pt: 'e em',
"pt-br": 'e em',
ru: 'и в',
sv: 'och i',
tr: 've burada',
zh: '且在'
},
'but not in': {
ar: 'لا تضع في',
bn: 'কিন্তু এতে নয়',
ce: 'амма чохь хӀума яц',
cs: 'ale ne v',
fa: 'ولی نه در',
de: 'die nicht sind in',
es: 'pero no en',
fr: 'mais pas dans',
hr: 'ali ne u',
it: 'ma non in',
mk: 'но не во',
ml: 'ഇതിൽ ഉള്ളത് വേണ്ട',
pt: 'mas não em',
"pt-br": 'mas não em',
ru: 'но не в',
sv: 'men inte i',
tr: 'burada olmayan',
zh: '但不在'
},
category: {
ar: 'تصنيف',
bn: 'বিষয়শ্রেণী',
ce: 'категори',
cs: 'kategorie',
de: 'Kategorie',
es: 'categoría',
fa: 'رده',
fr: 'catégorie',
hr: 'kategorija',
it: 'categoria',
mk: 'категорија',
ml: 'വർഗ്ഗം',
pt: 'categoria',
"pt-br": 'categoria',
ru: 'категория',
sv: 'kategori',
tr: 'kategori',
zh: '分类'
},
'In this category <b>and</b> in...': {
ar: 'في هذا التصنيف <b>و</b> في...',
bn: 'এই বিষয়শ্রেণীতে <b>এবং</b> এতে...',
ce: 'ХӀокху категореш чохь <b>кхин</b> чохь...',
cs: 'V této kategorii <b>a zároveň</b> v…',
de: 'In dieser Kategorie <b>und</b> in ...',
es: 'En esta categoría <b>y</b> en...',
fa: 'در این رده <b>و</b> در...',
fr: 'Dans cette catégorie <b>et</b> dans...',
hr: 'U ovoj kategoriji <b>i</b> u...',
it: 'In questa categoria <b>e</b> in...',
mk: 'во категоријава <b>и</b> во...',
ml: 'ഈ വർഗ്ഗത്തിലേയും <b>ഒപ്പം</b> ഇതിലേയും...',
pt: 'Nesta categoria <b>e</b> em...',
"pt-br": 'Nesta categoria <b>e</b> em...',
ru: 'В этой категории <b>и</b> в...',
sv: 'I denna kategori <b>och</b> i...',
tr: 'Bu kategoride <b>ve</b> şurada olanlar...',
zh: '在本分类<b>且</b>在...'
},
'In this category <b>but not</b> in...': {
ar: 'في هذا التصنيف <b>وليس</b> في...',
bn: 'এই বিষয়শ্রেণীতে <b>কিন্তু</b> এতে নয়...',
cs: 'V této kategorii, <b>ale ne</b> v…',
de: 'In dieser Kategorie, <b>aber nicht</b> in ...',
es: 'En esta categoría <b>pero no</b> en...',
fa: 'در این رده <b>ولی نه</b> در...',
fr: 'Dans cette catégorie <b>mais pas</b> dans...',
hr: 'U ovoj kategoriji <b>ali ne</b> u...',
it: 'In questa categoria <b>ma non</b> in...',
mk: 'во категоријава <b>но не</b> во...',
ml: 'ഈ വർഗ്ഗത്തിലേയും <b>പക്ഷേ</b> ഇതിലില്ലാത്തവയും...',
pt: 'Nesta categoria <b>mas não</b> em...',
"pt-br": 'Nesta categoria <b>mas não</b> em...',
ru: 'В этой категории, <b>но не</b> в...',
sv: 'I denna kategori <b>men inte</b> i...',
tr: 'Bu kategoride olan <b>ancak</b> şurada olmayanlar...',
zh: '在本分类<b>但不</b>在...'
},
'More...': {
ar: 'المزيد...',
bn: 'আরও...',
ce: 'Кхин...',
cs: 'Další…',
de: 'Weitere ...',
es: 'Más...',
fa: 'بیشتر...',
fr: 'Plus...',
hr: 'Više...',
it: 'Altro...',
mk: 'Повеќе...',
ml: 'കൂടുതൽ...',
pt: 'Mais...',
"pt-br": 'Mais...',
ru: 'Ещё...',
sv: 'Mer...',
tr: 'Daha fazla...',
zh: '更多...'
},
'About FastCCI...': {
ar: 'حول FastCCI...',
bn: 'FastCCI সম্পর্কে...',
cs: 'O FastCCI…',
ce: 'Цунах лаьцна FastCCI...',
de: 'Über FastCCI ...',
es: 'Acerca de FastCCI...',
fa: 'دربارهٔ FastCCI...',
hr: 'O FastCCIju',
it: 'A proposito di FastCCI...',
ml: 'FastCCI വിവരണം...',
pt: 'Acerca de FastCCI...',
"pt-br": 'Acerca de FastCCI...',
ru: 'Описание FastCCI...',
sv: 'Om FastCCI...',
tr: 'FastCCI hakkında...',
zh: '关于FastCCI...'
},
Ok: {
ar: 'موافق',
de: 'OK',
es: 'Aceptar',
hr: 'U redu',
it: 'Va bene',
pt: 'OK',
"pt-br": 'OK',
ru: 'OK',
sv: 'OK',
tr: 'Tamam',
zh: 'OK'
},
Cancel: {
ar: 'إلغاء',
de: 'Abbrechen',
es: 'Cancelar',
hr: 'Odustani',
it: 'Annulla',
pt: 'Cancelar',
"pt-br": 'Cancelar',
ru: 'Отмена',
sv: 'Avbryt',
tr: 'İptal',
zh: '取消'
}
};
// get a translated string
function i18n( key ) {
if ( !( key in i18nData ) || !( uiLang in i18nData[ key ] ) ) {
return key;
}
return i18nData[ key ][ uiLang ];
}
// request the fastcci db over HTTPS (no streaming)
function requestXHR( params, callback ) {
$.get( 'https:' + url, params )
.then( function ( data ) {
var i, res = data.split( '\n' );
for ( i = 0; i < res.length; ++i ) {
callback( res[ i ] );
}
} );
}
// request the fastcci db using a JS callback (no streaming, no CORS)
function requestJS( params, callback ) {
window.fastcciCallback = function ( res ) {
var i;
for ( i = 0; i < res.length; ++i ) {
callback( res[ i ] );
}
};
$.getScript( 'https:' + url + '?t=js&' + $.param( params ) );
}
// request the fastcci db over a WebSocket (streaming with progressive status updates)
function requestSocket( params, callback ) {
var ws = new WebSocket( 'wss:' + url + '?' + $.param( params ) );
// ws.onmessage = function(event) { setTimeout(function() {callback(event.data);}, 0); };
ws.onmessage = function ( event ) {
callback( event.data );
};
ws.onerror = function () {
// We should fall back to JS if the WS connection throws an error
// However current Chrome versions throw a non-fatal error (reserved bits)
// I'll need to fix this first before I can reenable the fallback :-/
// mw.notify('Still connecting...');
// request = requestJS;
// request(params, callback);
};
}
// determine request method (requestSocket > requestXHR > requestJS)
request = ( 'WebSocket' in window ) ? requestSocket : ( ( 'withCredentials' in new XMLHttpRequest() ) ? requestXHR : requestJS );
// process result by API call (res is a line returned by the server)
function processResult( res, ctx, callback, append ) {
var r = res.split( '|' ), t, l = r.length, i, pageids,
// get ID,depth, and tag lists
ids = Array( l ), depths = Array( l ), tags = Array( l ),
// return data
ret = append || [];
// build lists
for ( i = 0; i < l; ++i ) {
t = r[ i ].split( ',' );
ids[ i ] = t[ 0 ];
depths[ i ] = parseInt( t[ 1 ], 10 );
tags[ i ] = parseInt( t[ 2 ] || '0', 10 );
}
// pageid list for query
pageids = ids.join( '|' );
// query all IDs
$.get( mw.util.wikiScript( 'api' ), {
action: 'query',
pageids: pageids,
format: 'json',
utf8: true,
prop: 'imageinfo|info',
iiprop: 'size|user|sha1', inprop: 'url'
} )
.done( function ( data ) {
var j,
l = ids.length,
p = data.query.pages;
for ( j = 0; j < l; ++j ) {
if ( ids[ j ] in p ) {
p[ ids[ j ] ].fastcciDepth = depths[ j ];
p[ ids[ j ] ].fastcciTag = tags[ j ];
ret.push( p[ ids[ j ] ] );
} else {
ret.push( null );
}
}
callback( ret, ctx );
} );
}
// breadcrumbs (TODO: this breaks if the server returns two result lines. We need a reliable way to aggregate the results)
function breadCrumbs( txt ) {
var token = txt.split( ' ' );
if ( token.length !== 2 || token[ 0 ] !== 'RESULT' ) {
return;
}
processResult( token[ 1 ], null, function ( trail ) {
var l = trail.length, i, bc = [];
for ( i = 0; i < l; ++i ) {
if ( 'fullurl' in trail[ i ] && 'title' in trail[ i ] ) {
bc.push( '<a href="' + trail[ i ].fullurl + '">' + trail[ i ].title.replace( /^Category:/, '' ) + '</a>' );
} else {
bc.push( '???' );
}
}
$content.prepend( $( '<div>' ).addClass( 'fastcci-breadcrumbs' ).html( bc.join( ' → ' ) ) );
} );
}
// request wrapper that prepares the gallery
function fetchGallery( params ) {
var numResult = 0, dbAge = 0;
maxDepth = 0;
minDepth = Infinity;
// strength-of-match slider is moved, change result set
function slideMove( event, ui ) {
var i;
if ( ui.value === depthThreshold ) {
return;
}
depthThreshold = ui.value;
for ( i = minDepth; i <= maxDepth; ++i ) {
if ( i > depthThreshold ) {
$( '.fastcci-depth' + i ).hide();
} else {
$( '.fastcci-depth' + i ).show();
}
}
}
// append to result gallery
function addToGallery( txt ) {
var age,
token = txt.split( ' ' ),
d = 300;
// no results yet
if ( numResult === 0 ) {
switch ( token[ 0 ] ) {
case 'DONE':
$e.text( i18n( 'No results.' ) );
return;
case 'QUEUED':
$e.text( i18n( 'Waiting in line. NUM ahead of us.' ).replace( 'NUM', token[ 1 ] ) );
return;
case 'COMPUTE_START':
$e.text( i18n( 'Computing...' ) );
return;
case 'WORKING':
$e.text( i18n( 'Digging through NUM files...' ).replace( 'NUM', parseInt( token[ 1 ], 10 ) + parseInt( token[ 2 ], 10 ) ) );
return;
}
}
// are we done? add a ''More...'' button if applicable
switch ( token[ 0 ] ) {
case 'DONE':
// human readable database age
if ( dbAge < 60 ) {
age = dbAge + 's';
} else if ( dbAge < 3600 ) {
age = Math.round( dbAge / 60 ) + 'm';
} else if ( dbAge < 86400 ) {
age = Math.round( dbAge / 3600 ) + 'h';
} else {
age = Math.round( dbAge / 86400 ) + 'd';
}
$e.append( $( '<div>' ).addClass( 'fastcci-resultstatus' ).text( age ) );
// if we got the full amount of results show the button (TODO: look at OUTOF)
if ( numResult === params.s ) {
$e.append( $( '<button>' ).text( i18n( 'More...' ) ).button().on( 'click', function () {
var s = params.s || 200,
o = params.o || 0;
params.o = o + s;
window.scrollTo( 0, 0 );
fetchGallery( params );
} ) );
}
return;
case 'DBAGE':
dbAge = parseInt( token[ 1 ] || '0', 10 );
return;
}
// beyond this point ony process RESULT responses
if ( token.length < 2 || token[ 0 ] !== 'RESULT' ) {
return;
}
// show controls if results are coming in
if ( numResult === 0 ) {
$e.empty().append( $controls );
$link.attr( 'href', location.pathname + '?fastcci=' + encodeURIComponent( JSON.stringify( params ) ) );
depthThreshold = 1000;
$slider.slider( { change: slideMove, slide: slideMove, stop: slideMove, value: depthThreshold } );
}
// count the number of results received
numResult += token[ 1 ].split( '|' ).length;
processResult( token[ 1 ], $( '<span>' ).appendTo( $e ), function ( ids ) {
var j, ow, oh, w, h, p, i, t, depth, $div, path,
l = ids.length;
for ( j = 0; j < l; ++j ) {
p = ids[ j ];
if ( p === null || !( 'imageinfo' in p ) ) {
continue;
}
depth = p.fastcciDepth;
if ( depth > maxDepth ) {
maxDepth = depth;
}
if ( depth < minDepth ) {
minDepth = depth;
}
i = p.imageinfo[ 0 ];
ow = i.width;
oh = i.height;
if ( ow > oh ) {
w = Math.round( ow * d / oh );
h = d;
} else {
h = Math.round( oh * d / ow );
w = d;
}
// thumb.php only forks if the size requested is smaller than the full image!
t = encodeURIComponent( new mw.Title( p.title ).getMain() );
if ( Math.ceil( w ) >= ow ) {
w = ow;
h = oh;
path = '/wiki/Special:Redirect/file/' + t;
} else {
// console.log('//upload.wikimedia.org/wikipedia/commons/thumb/' + i.sha1.substr(0,1) + '/'+i.sha1.substr(0,2) + '/' + t + '/' + Math.ceil(w) + 'px-' + t);
path = '/w/index.php?title=Special:Redirect/file/' + t + '&width=' + Math.ceil( w );
}
$div = $( '<div>' )
.addClass( 'fastcci-image' )
.addClass( 'fastcci-depth' + depth )
.css( {
width: d + 'px',
height: d + 'px'
} )
.append(
$( '<a>' )
.attr( 'href', p.fullurl + '?fastcci_from=' + thisPageId + '&' + $.param( params ) )
.append(
$( '<img>' )
.attr( 'src', path )
.css( {
position: 'absolute',
left: Math.round( -( w - d ) / 2 ) + 'px',
top: Math.round( -( h - d ) / 2 ) + 'px'
} )
)
);
// add badge to thumb
if ( p.fastcciTag > 0 && p.fastcciTag <= 4 ) {
$( '<img>' ).addClass( 'fastcci-badge' ).attr( 'src', base + badge[ p.fastcciTag - 1 ] ).appendTo( $div );
}
$e.append( $div );
}
// set slider limits
$slider.slider( {
min: minDepth,
max: maxDepth,
value: Math.max( maxDepth, depthThreshold )
} );
} );
}
$e.empty().prependTo( $content ).text( i18n( 'Connecting...' ) );
request( params, addToGallery );
}
function tagLine( html ) {
if ( $tagline === null ) {
$tagline = $( '<span>' ).addClass( 'fastcci-tagline' ).appendTo( $( '#firstHeading>span' ).eq( 0 ) );
}
$tagline.html( html );
}
// show the modal dialog for advanced options
function showDialog( operation ) {
var text;
// fetch the page text of a soft redirect page and resolve the pageid of the redirect
function resolveRedirect( t ) {
modal.isRedirect[ t ] = true;
$.get( mw.util.wikiScript( 'index' ), { action: 'raw', title: 'Category:' + t }, undefined, 'text' )
.done( function ( data ) {
var m = /\{\{[Cc]ategory[_ ]redirect\|([Cc]ategory:|)([^}]+)\}\}/.exec( data );
if ( m !== null ) {
$.getJSON( mw.util.wikiScript( 'api' ), { action: 'query', format: 'json', titles: 'Category:' + m[ 2 ], indexpageids: true } )
.done( function ( data ) {
var i = parseInt( data.query.pageids[ 0 ], 10 );
if ( i >= 0 ) {
modal.isRedirect[ t ] = i;
validate();
}
} );
}
} );
}
// build the tagline for this operation
function getTagLine() {
var v = modal.$input.val();
switch ( modal.operation ) {
case 'and': return i18n( 'and in' ) + ' <i>' + v + '</i>';
case 'not': return i18n( 'but not in' ) + ' <i>' + v + '</i>';
}
return '';
}
// get pageId of currently typed category (or undefined)
function pageId() {
var v = modal.$input.val();
// is this an unresiolved redirect?
if ( v in modal.isRedirect ) {
if ( modal.isRedirect[ v ] === true ) {
return undefined;
} else {
return modal.isRedirect[ v ];
}
}
// page id in cache?
if ( v in modal.pageIds ) {
return modal.pageIds[ v ];
} else {
return undefined;
}
}
// validate current category
function validate() {
if ( pageId() !== undefined ) {
modal.$ok.button( 'enable' );
} else {
modal.$ok.button( 'disable' );
}
}
// send query and close dialog
function performOperation() {
var id = pageId();
if ( id !== undefined ) {
fetchGallery( { c1: thisPageId, c2: id, d1: 15, d2: 15, s: 200, a: modal.operation } );
tagLine( getTagLine() );
} else {
mw.notify( 'Error.' );
}
modal.$div.dialog( 'close' );
}
// build dialog on demand
if ( !modal ) {
modal = { cache: {}, pageIds: {}, isRedirect: {}, operation: null };
// dialog window
modal.$div = $( '<div>' );
// input widget
modal.$input = $( '<input>' ).attr( 'placeholder', i18n( 'category' ) ).appendTo( modal.$div ).autocomplete( {
minLength: 2,
source: function ( request, response ) {
var term = request.term.replace( / /g, '_' );
if ( term in modal.cache ) {
response( modal.cache[ term ] );
return;
}
$.getJSON( mw.util.wikiScript( 'api' ), {
action: 'query', format: 'json',
generator: 'allpages', gapprefix: term, gapnamespace: 14,
prop: 'templates', tltemplates: 'Template:Category_redirect'
}, function ( data ) {
var list = [], a, i, t;
if ( ( 'query' in data ) && ( 'pages' in data.query ) ) {
a = data.query.pages;
for ( i in a ) {
if ( Object.prototype.hasOwnProperty.call( a, i ) ) {
t = a[ i ].title.replace( /^Category:/, '' );
modal.pageIds[ t ] = i;
// check for soft redirects
if ( 'templates' in a[ i ] && !( t in modal.isRedirect ) ) {
resolveRedirect( t );
}
// add title to suggestion list
list.push( t );
}
}
modal.cache[ term ] = list;
}
validate();
response( list.sort() );
} );
},
change: validate,
select: function ( e, ui ) {
// manually set the input to the selected value and validate
modal.$input.val( ui.item.value );
validate();
e.preventDefault();
}
} )
.on( 'keyup', validate )
.on( 'keypress', function ( e ) {
if ( e.keyCode === $.ui.keyCode.ENTER && pageId() ) {
performOperation();
}
} );
// build dialog
modal.$div.dialog( {
autoOpen: false,
modal: true,
buttons: [
{ text: i18n( 'Ok' ), click: performOperation },
{ text: i18n( 'Cancel' ), click: function () { modal.$div.dialog( 'close' ); } }
]
} );
// Ok button
modal.$ok = $( 'button', modal.$div.parent() ).eq( 0 );
validate();
}
// customize text for the selected operation
text = {
and: [ 'In this category <b>and</b> in...', 'and in' ],
not: [ 'In this category <b>but not</b> in...', 'but not in' ]
};
modal.$div.dialog( 'option', 'title', i18n( text[ operation ][ 0 ] ) );
modal.operation = operation;
// show the dialog
modal.$div.dialog( 'open' );
}
// build category FP/QI/VI UI
function addCatUI() {
var $box = $( '#firstHeading' ),
$buttonset,
$menu = $( '<ul>' ).addClass( 'fastcci-menu' ),
$advanced = $( '<button>' ).text( i18n( 'Advanced...' ) )
.button( {
text: false,
icons: { primary: 'ui-icon-triangle-1-s' }
} )
.on( 'click', function ( e ) {
$menu.toggle();
e.stopPropagation();
} ),
width;
$buttonset = $( '<div>' ).addClass( 'fastcci-buttonset' )
.attr( 'lang', uiLang )
.append(
$( '<button>' )
.attr( 'title', i18n( 'Featured pictures' ) + ', ' + i18n( 'Featured videos' ) + ', ' + i18n( 'Quality images' ) + ', ' + i18n( 'Valued images' ) )
.append(
$( '<img>' ).attr( { id: 'fastcci-fqv1', src: base + badge[ 3 ] } ),
$( '<img>' ).attr( { id: 'fastcci-fqv2', src: base + badge[ 2 ] } ),
$( '<img>' ).attr( { id: 'fastcci-fqv3', src: base + badge[ 1 ] } ),
$( '<img>' ).attr( { id: 'fastcci-fqv4', src: base + badge[ 0 ] } ),
$( '<span>' ).attr( 'id', 'fastcci-buttontextwrapper' ).append( $( '<span>' ).attr( 'id', 'fastcci-buttontext' ).text( i18n( 'Good pictures' ) ) )
)
.on( 'click', function () {
fetchGallery( { c1: thisPageId, d1: 15, s: 200, a: 'fqv' } );
tagLine( i18n( 'Good pictures' ) );
} ),
$advanced
)
.buttonset()
.append( $menu )
.appendTo( $box );
width = $buttonset.outerWidth( true );
$menu.append(
$( '<li>' ).append( $( '<a>' ).attr( 'href', '#' )
.append(
$( '<span>' ).attr( 'id', 'fastcci-allimages' ).text( i18n( 'All images' ) )
)
.on( 'click', function () {
fetchGallery( { c1: thisPageId, d1: 15, s: 200, a: 'list' } );
tagLine( i18n( 'All images' ) );
} )
)
.css( 'margin-bottom', '0.5em' ), // menu separator
$( '<li>' ).append( $( '<a>' ).attr( 'href', '#' )
.append(
$( '<img>' ).addClass( 'fastcci-badges' ).attr( 'src', base + badge[ 0 ] ),
i18n( 'Featured pictures' )
)
.on( 'click', function () {
fetchGallery( { c1: thisPageId, c2: 3943817, d1: 15, d2: 0, s: 200 } );
tagLine( i18n( 'Featured pictures' ) );
} )
),
$( '<li>' ).append( $( '<a>' ).attr( 'href', '#' )
.append(
$( '<img>' ).addClass( 'fastcci-badges' ).attr( 'src', base + badge[ 1 ] ),
i18n( 'Featured videos' )
)
.on( 'click', function () {
fetchGallery( { c1: thisPageId, c2: 8460057, d1: 15, d2: 0, s: 200 } );
tagLine( i18n( 'Featured videos' ) );
} )
),
$( '<li>' ).append( $( '<a>' ).attr( 'href', '#' )
.append(
$( '<img>' ).addClass( 'fastcci-badges' ).attr( 'src', base + badge[ 2 ] ),
i18n( 'Quality images' )
)
.on( 'click', function () {
fetchGallery( { c1: thisPageId, c2: 3618826, d1: 15, d2: 0, s: 200 } );
tagLine( i18n( 'Quality images' ) );
} )
),
$( '<li>' ).append( $( '<a>' ).attr( 'href', '#' )
.append(
$( '<img>' ).addClass( 'fastcci-badges' ).attr( 'src', base + badge[ 3 ] ),
i18n( 'Valued images' )
)
.on( 'click', function () {
fetchGallery( { c1: thisPageId, c2: 4143367, d1: 15, d2: 0, s: 200 } );
tagLine( i18n( 'Valued images' ) );
} )
)
.css( 'margin-bottom', '0.5em' ), // menu separator
$( '<li>' ).append( $( '<a>' ).attr( 'href', '#' )
.append(
$( '<img>' ).addClass( 'fastcci-badges' ).attr( 'src', base + '4/45/Fastcci_intersect.svg/24px-Fastcci_intersect.svg.png' ),
i18n( 'In this category <b>and</b> in...' )
)
.on( 'click', function () {
showDialog( 'and' );
$menu.hide();
} )
),
$( '<li>' ).append( $( '<a>' ).attr( 'href', '#' )
.append(
$( '<img>' ).addClass( 'fastcci-badges' ).attr( 'src', base + 'd/d5/Fastcci_notin.svg/24px-Fastcci_notin.svg.png' ),
i18n( 'In this category <b>but not</b> in...' )
)
.on( 'click', function () {
showDialog( 'not' );
$menu.hide();
} )
)
.css( 'margin-bottom', '0.5em' ), // menu separator
$( '<li>' ).append( $( '<a>' ).attr( 'href', helpUrl ).attr( 'target', '_blank' )
.append(
$( '<img>' ).addClass( 'fastcci-badges' ).attr( 'src', base + helpIcon ),
i18n( 'About FastCCI...' )
)
.on( 'click', function ( e ) {
e.stopImmediatePropagation();
$menu.hide();
} )
)
)
.menu()
.position( {
my: 'top',
at: 'bottom',
of: $advanced,
within: $buttonset,
collide: 'none'
} )
.hide();
$slider = $( '<div>' ).addClass( 'fastcci-slider' );
$link = $( '<a>' ).addClass( 'fastcci-help' )
.append( $( '<img>' ).attr( 'src', '//upload.wikimedia.org/wikipedia/commons/f/fd/Link.png' ) );
$controls = $( '<div>' ).addClass( 'fastcci-controls' )
.append( i18n( 'Strong match' ) )
.append( $slider )
.append( i18n( 'Weak match' ) )
.append(
$( '<a>' ).addClass( 'fastcci-help' )
.attr( 'href', helpUrl )
.append( $( '<img>' ).attr( 'src', base + helpIcon ) )
)
.append( $link )
.append( $( '<img>' )
.attr( 'src', '//upload.wikimedia.org/wikipedia/commons/thumb/4/4c/Grey_close_x.svg/22px-Grey_close_x.svg.png' )
.addClass( 'fastcci-close' )
);
// remove button text if space is insufficient
function resize() {
var space = $( '#firstHeading' ).width() - $( '#firstHeading>span' ).width() - 10; // 10 px safety
if ( space < width ) {
$( '#fastcci-buttontext' ).hide();
} else {
$( '#fastcci-buttontext' ).show();
}
}
resize();
$( window ).on( 'resize', resize );
$( document ).on( 'click', function () {
$menu.hide();
} );
}
// add category UI
if ( ns === namespaceIds.category && action === 'view' ) {
mw.loader.using( [ 'jquery.ui'], addCatUI );
// process url parameters (allows linking to results)
var param, urlarg = mw.util.getParamValue( 'fastcci' );
if ( urlarg ) {
try {
param = JSON.parse( urlarg );
param.c1 = thisPageId; // make sure c1 is the current page to avoid surprises (like list pornstars on the kitten Category)
param.s = param.s || 200;
if ( param.s > 1000 ) {
// limit number of results
param.s = 1000;
}
mw.loader.using( 'jquery.ui', function () {
fetchGallery( param );
} );
} catch ( e ) {
mw.notify( 'FastCCI URL parameters invalid.' );
}
}
}
// display breadcrumbs on image page?
var from = mw.util.getParamValue( 'fastcci_from' );
if ( ns === namespaceIds.file && from ) {
request( { c1: parseInt( from, 10 ), c2: thisPageId, a: 'path' }, breadCrumbs );
}
// display second breadcrumb for intersections
var and_from = mw.util.getParamValue( 'c2' );
if ( ns === namespaceIds.file && from ) {
request( { c1: parseInt( and_from, 10 ), c2: thisPageId, a: 'path' }, breadCrumbs );
}
} );