注意:在发布之后,您可能需要清除浏览器缓存才能看到所作出的变更的影响。
- Firefox或Safari:按住Shift的同时单击刷新,或按Ctrl-F5或Ctrl-R(Mac为⌘-R)
- Google Chrome:按Ctrl-Shift-R(Mac为⌘-Shift-R)
- Internet Explorer或Edge:按住Ctrl的同时单击刷新,或按Ctrl-F5
- Opera:按 Ctrl-F5。
/*global mw, jQuery, GallerySlide, alert, prompt */ /*jshint bitwise: false, laxbreak: true, browser: true, onevar: false, nomen: false, smarttabs: true */ /** * This is a derivative work of: */ /** * jQuery Galleriffic plugin * * Copyright (c) 2008 Trent Foley (http://trentacular.com) * Licensed under the MIT License: * http://www.opensource.org/licenses/mit-license.php * * Much thanks to primary contributer Ponticlaro (http://www.ponticlaro.com) * */ /** * Rewritten for commons by [[User:DieBuche]], * additional features by [[User:Rillke]] * jshint valid */ (function($) { if (typeof window.GallerySlide !== 'undefined' || mw.config.get('wgNamespaceNumber') < 0) { return; } // Declare global variable so that it is no longer undefined, // will be populated later from the document ready hook window.GallerySlide = null; // Globally keep track of all images by their unique hash. Each item is an image data object. var isCategory = (mw.config.get('wgNamespaceNumber') === 14); var isRtl = $('body').hasClass('rtl'); var allImages = {}; var imageCounter = 0; // Galleriffic static class $.galleriffic = { version: '2.2', // Strips invalid characters and any leading # characters normalizeHash: function(hash) { return hash.replace('#', ''); }, getImage: function(hash) { if (!hash) { return; } hash = $.galleriffic.normalizeHash(hash); return allImages[hash]; }, // Global function that looks up an image by its hash and displays the image. // Returns false when an image is not found for the specified hash. // @param {String} hash This is the unique hash value assigned to an image. gotoImage: function(hash) { var imageData = $.galleriffic.getImage(hash); if (!imageData) { return false; } var gallery = imageData.gallery; gallery.gotoImage(imageData); return true; } }; var i18n; var i18nStore = { bn: { delayInsertBtn: 'মিলিসেকেন্ডে বিলম্ব নির্ধারন করুন', delayInsert: 'একটি নতুন চিত্রের জন্য কত মিলিসেকেন্ড অপেক্ষা করতে চান?', delayInvalid: 'অকার্যকর ইনপুট। শুধুমাত্র 1500 থেকে বড় সংখ্যা গৃহীত হয়।', playLinkText: 'চালান', pauseLinkText: 'বিরতি', prevLinkText: 'পূর্ববর্তী', nextLinkText: 'পরবর্তী', hideText: 'স্লাইডশো বন্ধ করুন', continueKeyHowTo: 'অবিরত-চাবি - আপনি যদি পরে এই অবস্থান থেকে আরম্ভ করতে চান তাহলে, আপনি এই চাবি সংরক্ষণ বা লিঙ্ক বুকমার্ক করতে পারেন।', continueKeyInsert: 'দয়া করে অবিরত-চাবি প্রবেশ করান। প্রভাব দেখতে আপনাকে স্লাইডশোতে এগিয়ে যেতে হবে।', continueKeyInsertBtn: 'অবিরত-চাবি প্রবেশ করান', continueKeyInvalid: 'অকার্যকর চাবি।', licenseLabel: 'উপলভ্য লাইসেন্স: ', uploaderLabel: 'আপলোডকারী ', helpLinkTitle: 'এই সরঞ্জামের জন্য সাহায্য এবং নথিপত্র', descriptionLoadText: 'বিবরণ লোড হচ্ছে ...' }, ca: { delayInsertBtn: 'Canvia el temps de demora', delayInsert: 'Demora en milisegons entre cada imatge', delayInvalid: 'Entrada no vàlida. Només s’accepten números superiors a 1500.', playLinkText: 'Reprodueix', pauseLinkText: 'Pausa', prevLinkText: 'Anterior', nextLinkText: 'Següent', hideText: 'Tanca la presentació', continueKeyHowTo: 'Clau de represa - Podeu desar aquesta clau o afegir l’enllaç en les adreces d’interès si més endavant voleu començar des d’aquesta posició.', continueKeyInsert: 'Afegiu la clau de represa. Heu d’anar endavant en la presentació per constatar el canvi.', continueKeyInsertBtn: 'Afegeix la clau de represa', continueKeyInvalid: 'Clau invàlida.', licenseLabel: 'Llicències disponibles: ', uploaderLabel: 'Usuari ', helpLinkTitle: 'Ajuda i documentació d’aquesta eina', descriptionLoadText: 'Carregant la descripció ...' }, cs: { delayInsertBtn: 'Nastavit prodlevu v ms', delayInsert: 'Kolik milisekund se má čekat na další obrázek?', delayInvalid: 'Chybné zadání. Povolena jsou pouze čísla větší než 1500.', playLinkText: 'Spustit', pauseLinkText: 'Pozastavit', prevLinkText: 'Předchozí', nextLinkText: 'Následující', hideText: 'Zavřít prezentaci', continueKeyHowTo: 'Klíč pro pokračování – můžete si uschovat tento klíč nebo si do oblíbených uložit odkaz, pokud budete později pokračovat od této pozice.', continueKeyInsert: 'Vložte prosím klíč pro pokračování. Abyste viděli výsledek, musíte pak v prezentaci přejít na další snímek.', continueKeyInsertBtn: 'Vložte klíč pro pokračování', continueKeyInvalid: 'Neplatný klíč.', licenseLabel: 'Dostupné licence: ', uploaderLabel: 'Načetl(a) ', helpLinkTitle: 'Nápověda a dokumentace k tomuto nástroji', descriptionLoadText: 'Načítá se popis' }, en: { delayInsertBtn: 'Set delay in ms', delayInsert: 'How many ms to wait for a new image?', delayInvalid: 'Invalid input. Only numbers greater than 1500 are accepted.', playLinkText: 'Play', pauseLinkText: 'Pause', prevLinkText: 'Previous', nextLinkText: 'Next', hideText: 'Close slideshow', continueKeyHowTo: 'Continue Key - You can save this key or bookmark the link if you\'d like to start at this position later.', continueKeyInsert: 'Please insert the continue-key. You have to go forward in the slideshow to see effect.', continueKeyInsertBtn: 'Insert continue-key', continueKeyInvalid: 'Invalid key.', licenseLabel: 'Available Licenses: ', uploaderLabel: 'Uploader ', helpLinkTitle: 'Help and documentation for this tool', descriptionLoadText: 'Loading description ...' }, es: { delayInsertBtn: 'Fijar el atraso en milisegundos', delayInsert: '¿Cuántos milisegundos queres esperar hasta que aparezca la nueva imagen?', delayInvalid: 'Entrada no válida. Solo se aceptan los números mayores que 1500.', playLinkText: 'Reproducir', pauseLinkText: 'Pausar', prevLinkText: 'Anterior', nextLinkText: 'Siguiente', hideText: 'Cerrar la presentación', continueKeyHowTo: 'Clave de continuación: Puedes guardar esta clave o añadir el enlace a los favoritos si quieres empezar la presentación desde este punto en otro momento.', continueKeyInsert: 'Agregue la clave para reiniciar. Tienes que seguir la presentación para comprobar los cambios.', continueKeyInsertBtn: 'Introducir una clave de continuación', continueKeyInvalid: 'Clave no válida.', licenseLabel: 'Licencias disponibles: ', uploaderLabel: 'Persona que subió la foto', helpLinkTitle: 'Ayuda y documentación de esta herramienta', descriptionLoadText: 'Cargando la descripción...' }, fa: { delayInsertBtn: 'تنظیم تأخیر در میلی\u200cثانیه', delayInsert: 'چقدر میلی\u200cثانیه تأخبر برای تصویر جدید؟', delayInvalid: 'ورودی نادرست. فقط اعداد بزرگتر از ۱۵۰۰ قبول هستند.', playLinkText: 'پخش', pauseLinkText: 'توقف', prevLinkText: 'قبلی', nextLinkText: 'بعدی', hideText: 'بستن اسلایدشو', continueKeyHowTo: 'کلید ادامه - شما می\u200cتوانید این کلید را ذخیره یا بوک\u200cمارک کنید پیوندش را اگر می\u200cخواهید بعداً از این مکان شروع کنید.', continueKeyInsert: 'لطفاً کلید ادامه را فشار دهید. شما باید در اسلایدشو جلو بروید یا اثرش را ببینید.', continueKeyInsertBtn: 'قراردادن کلید ادامه', continueKeyInvalid: 'کلید نادرست.', licenseLabel: 'مجوزهای موجود: ', uploaderLabel: 'بارگذار ', helpLinkTitle: 'کمک و مستندات این ابزار', descriptionLoadText: 'بارگیری توضیحات ...' }, fr: { delayInsertBtn: 'Définir l’intervalle de temps', delayInsert: 'Combien de millisecondes entre chaque image ?', delayInvalid: 'Entrée invalide. Seuls les nombres supérieurs à 1500 sont acceptés.', playLinkText: 'Lire', pauseLinkText: 'Pause', prevLinkText: 'Précédent', nextLinkText: 'Suivant', hideText: 'Quitter le diaporama', continueKeyHowTo: 'Clé de départ - Vous pouvez sauvegarder cette clé ou mettre en marque-page le lien si vous souhaitez commencer à cette position plus tard.', continueKeyInsert: 'Veuillez insérer la clé de départ. Vous devez faire défiler le diaporama pour constater un changement.', continueKeyInsertBtn: 'Insérer la clé de départ', continueKeyInvalid: 'Clé invalide', licenseLabel: 'Licences disponibles : ', uploaderLabel: 'La personne qui a téléversé le fichier sur le serveur ', helpLinkTitle: 'Aide et documentation de cet outil', descriptionLoadText: 'Chargement de la description ...' }, gl: { delayInsertBtn: 'Fixar o atraso en milisegundos', delayInsert: 'Cantos milisegundos queres agardar ata que apareza unha nova imaxe?', delayInvalid: 'Entrada non válida. Só se aceptan os números maiores que 1500.', playLinkText: 'Reproducir', pauseLinkText: 'Pausar', prevLinkText: 'Anterior', nextLinkText: 'Seguinte', hideText: 'Pechar a presentación', continueKeyHowTo: 'Clave de continuación: Podes gardar esta clave ou engadir a ligazón aos marcadores se queres comezar a presentación desde este punto noutro momento.', continueKeyInsert: 'Escribe a clave de continuación. Tes que avanzar na presentación para comprobar os cambios.', continueKeyInsertBtn: 'Inserir unha clave de continuación', continueKeyInvalid: 'Clave non válida.', licenseLabel: 'Licenzas dispoñibles: ', uploaderLabel: 'Persoa que cargou a imaxe ', helpLinkTitle: 'Axuda e documentación desta ferramenta', descriptionLoadText: 'Cargando a descrición...' }, hr: { delayInsertBtn: 'Kašnjenje u milisekundama', delayInsert: 'Čekanje u milisekundama na novu sliku.', delayInvalid: 'Krivi unos. Broj mora biti veći od 1500.', playLinkText: 'Pokreni', pauseLinkText: 'Stanka', prevLinkText: 'Prethodna', nextLinkText: 'Sljedeća', hideText: 'Zatvori prezentaciju', continueKeyHowTo: 'Ključ za nastavak: možete ga spremiti ili zabilježiti poveznicu da biste naknadno nastavili prezentaciju od ove slike.', continueKeyInsert: 'Unesite ključ za nastavak. Da biste vidjeli promjenu, morate nastaviti s prezentacijom.', continueKeyInsertBtn: 'Unesite ključ za nastavak', continueKeyInvalid: 'Nevaljali ključ.', licenseLabel: 'Dostupne licencije: ', uploaderLabel: 'Postavio ', helpLinkTitle: 'Pomoć i dokumentacija ovog alata', descriptionLoadText: 'Učitavanje opisa ...' }, mk: { delayInsertBtn: 'Задај задршка во милисек.', delayInsert: 'Колку милисек. да се чека за нова слика?', delayInvalid: 'Погрешен внос. Се допуштаат само броеви поголеми од 1500.', playLinkText: 'Пушти', pauseLinkText: 'Запри', prevLinkText: 'Претходна', nextLinkText: 'Следна', hideText: 'Затвори', continueKeyHowTo: 'Клуч за продолжување — Можете да го зачувате овој клуч или да ја ставите врската во одбележани доколку сакате подоцна да продолжите од ова исто место.', continueKeyInsert: 'Внесете го клучот за продолжување. Ќе треба да отидете напред во сликоредот за да го видите ефектот.', continueKeyInsertBtn: 'Внесете клуч за продолжување', continueKeyInvalid: 'Погрешен клуч.', licenseLabel: 'Расположиви лиценци: ', uploaderLabel: 'Подигач ', helpLinkTitle: 'Помош и документација за алаткава', descriptionLoadText: 'Го вчитувам описот ...' }, ml: { delayInsertBtn: 'കാലതാമസം മില്ലിസെക്കന്റിൽ', delayInsert: 'പുതിയ ചിത്രത്തിനായി എത്ര മില്ലിസെക്കന്റുകൾ കാത്തിരിക്കേണ്ടതുണ്ട്?', delayInvalid: 'നൽകിയ വില അസാധുവാണ്. 1500-നു മുകളിലുള്ള സംഖ്യകൾ മാത്രമേ സ്വീകരിക്കാനാകൂ.', playLinkText: 'പ്രവർത്തിപ്പിക്കുക', pauseLinkText: 'ഇടയ്ക്കുനിർത്തുക', prevLinkText: 'മുൻപത്തേത്', nextLinkText: 'അടുത്തത്', hideText: 'സ്ലൈഡ്ഷോ അടയ്ക്കുക', continueKeyHowTo: 'തുടർച്ചാ ചാവി - ഈ സ്ഥാനത്തുനിന്ന് പിന്നീട് തുടങ്ങണമെന്നുണ്ടെങ്കിൽ താങ്കൾക്ക് ഈ ചാവി സൂക്ഷിച്ചുവെയ്ക്കുകയോ, ഈ കണ്ണി ബുക്ക്\u200cമാർക്ക് ചെയ്യുകയോ ചെയ്യാവുന്നതാണ്.', continueKeyInsert: 'ദയവായി തുടർച്ചാ-ചാവി നൽകുക. ഫലത്തിനായി സ്ലൈഡ്\u200cഷോയിൽ മുന്നോട്ട് പോകേണ്ടതാണ്.', continueKeyInsertBtn: 'തുടർച്ചാ-ചാവി നൽകുക', continueKeyInvalid: 'ചാവി അസാധുവാണ്.', licenseLabel: 'ലഭ്യമായ അനുമതികൾ: ', uploaderLabel: 'അപ്\u200cലോഡ് ചെയ്തയാൾ ', helpLinkTitle: 'ഈ ഉപകരണത്തിനുള്ള സഹായവും വിവരണവും', descriptionLoadText: 'വിവരണം ശേഖരിക്കുന്നു...' }, ms: { delayInsertBtn: 'Tetapkan selang masa dalam milisaat', delayInsert: 'Berapa milisaat perlu ditunggu untuk ke imej baru?', delayInvalid: 'Input tidak sah. Hanya nombor lebih daripada 1500 diterima.', playLinkText: 'Main', pauseLinkText: 'Jeda', prevLinkText: 'Sebelumnya', nextLinkText: 'Selepasnya', hideText: 'Tutup tayangan slaid', continueKeyHowTo: 'Kekunci Sambungan - Anda boleh menyimpan kekunci ini atau tandakan pautan jika anda ingin bermula pada kedudukan ini nanti.', continueKeyInsert: 'Sila masukkan kekunci untuk bermula. Anda mesti menatal melalui tayangan slaid untuk melihat perubahan.', continueKeyInsertBtn: 'Masukkan kekunci', continueKeyInvalid: 'Kekunci tidak sah.', licenseLabel: 'Lesen yang disediakan: ', uploaderLabel: 'Pemuat naik ', helpLinkTitle: 'Bantuan dan pendokumenan alatan ini', descriptionLoadText: 'Penerangan sedang dimuatkan ...' }, pl: { delayInsertBtn: 'Ustaw opóźnienie w ms', delayInsert: 'Ile milisekund czekać przed pokazaniem następnej grafiki?', delayInvalid: 'Nieprawidłowe opóźnienie. Tylko liczby większe niż 1500 są akceptowane.', playLinkText: 'Odtwórz', pauseLinkText: 'Pauza', prevLinkText: 'Wstecz', nextLinkText: 'Dalej', hideText: 'Zamknij pokaz slajdów', continueKeyHowTo: 'Klucz wznowienia - możesz zapisać ten klucz lub dodać go do zakładek, jeśli chcesz później wznowić przeglądanie od obecnej pozycji.', continueKeyInsert: 'Proszę wpisać klucz wznowienia. Aby ujrzeć efekt, musisz przejść do następnej grafiki.', continueKeyInsertBtn: 'Wpisz klucz wznowienia', continueKeyInvalid: 'Nieprawidłowy klucz.', licenseLabel: 'Dostępne licencje: ', uploaderLabel: 'Przesłane przez ', helpLinkTitle: 'Pomoc i dokumentacja dla tego narzędzia', descriptionLoadText: 'Pobieranie opisu ...' }, pt: { delayInsertBtn: 'Alterar o tempo de atraso', delayInsert: 'Atraso em milissegundos entre cada imagem', delayInvalid: 'Entrada inválida. Somente números maiores que 1500 são aceitos.', playLinkText: 'Reproduzir', pauseLinkText: 'Pausa', prevLinkText: 'Anterior', nextLinkText: 'Seguinte', hideText: 'Fechar a apresentação', continueKeyHowTo: 'Continuação de Chave - Você pode salvar esta chave e adicionar a ligação para os seus favoritos, se mais tarde quiser começar a partir dessa posição.', continueKeyInsert: 'Adicione a chave para reiniciar. Você tem que continuar a apresentação para verificar as alterações.', continueKeyInsertBtn: 'Insira a chave', continueKeyInvalid: 'Chave inválida.', licenseLabel: 'Licenças disponíveis: ', uploaderLabel: 'Utilizador(a) ', helpLinkTitle: 'Ajuda e documentação desta ferramenta', descriptionLoadText: 'A carregar descrição ...' }, 'pt-br': { delayInsertBtn: 'Alterar o tempo de atraso', delayInsert: 'Atraso em milissegundos entre cada imagem', delayInvalid: 'Entrada inválida. São aceitos somente números maiores que 1500.', playLinkText: 'Reproduzir', pauseLinkText: 'Pausa', prevLinkText: 'Anterior', nextLinkText: 'Seguinte', hideText: 'Fechar a apresentação', continueKeyHowTo: 'Chave de Continuação - Você pode salvar esta chave e adicionar o link aos seus favoritos, se mais tarde quiser iniciar a partir dessa posição.', continueKeyInsert: 'Adicione a chave para reiniciar. Você tem que continuar a apresentação para verificar as alterações.', continueKeyInsertBtn: 'Insira a chave', continueKeyInvalid: 'Chave inválida.', licenseLabel: 'Licenças disponíveis: ', uploaderLabel: 'Utilizador(a) ', helpLinkTitle: 'Ajuda e documentação desta ferramenta', descriptionLoadText: 'Carregando descrição ...' }, ru: { delayInsertBtn: 'Установить задержку в милисек.', delayInsert: 'Сколько милисек. ждать до смены изображения?', delayInvalid: 'Неправильный ввод. Разрешены только числа больше 1500.', playLinkText: 'Запуск', pauseLinkText: 'Пауза', prevLinkText: 'Предыдущее', nextLinkText: 'Следующее', hideText: 'Закрыть слайдшоу', continueKeyHowTo: 'Ключ продолжения — вы можете сохранить этот ключ или добавить ссылку в закладки, если хотите начать с этого места позже.', continueKeyInsert: 'Вставьте, пожалуйста, ключ продолжения. Вам нужно вернуться обратно в слайдшоу, чтобы увидеть эффект.', continueKeyInsertBtn: 'Вставьте ключ продолжения', continueKeyInvalid: 'Неверный ключ', licenseLabel: 'Доступные лицензии: ', uploaderLabel: 'Загрузивший ', helpLinkTitle: 'Справка и документация для этого инструмента', descriptionLoadText: 'Загрузка описания ...' }, sv: { delayInsertBtn: 'Ställ in fördröjning i ms', delayInsert: 'Hur många ms det ska dröja innan nästa bild visas?', delayInvalid: 'Ogiltig indata. Endast nummer större än 1500 accepteras.', playLinkText: 'Spela', pauseLinkText: 'Pausa', prevLinkText: 'Föregående', nextLinkText: 'Nästa', hideText: 'Stäng bildspel', continueKeyHowTo: 'Fortsättningsnyckel - Du kan spara denna nyckel eller lägga ett bokmärke på länken om du vill börja på denna plats senare.', continueKeyInsert: 'Var god ange fortsättningsnyckeln. Du måste gå framåt i bildspelet för att det ska träda i kraft.', continueKeyInsertBtn: 'Ange fortsättningsnyckel', continueKeyInvalid: 'Ogiltig nyckel.', licenseLabel: 'Tillgängliga licenser: ', uploaderLabel: 'Uppladdare ', helpLinkTitle: 'Hjälp och dokumentation för detta verktyg', descriptionLoadText: 'Läser in beskrivning ...' } }; i18n = $.extend({}, i18nStore.en, i18nStore[mw.config.get('wgUserLanguage').split('-')[0]], i18nStore[mw.config.get('wgUserLanguage')]); var defaults = { delay: 7000, preloadAhead: 25, enableKeyboardNavigation: true, autoPlay: false, defaultTransitionDuration: 700, defaultSizes: [{ w: 1500, h: 1500 }, { w: 1280, h: 1024 }, { w: 1024, h: 768 }, { w: 800, h: 600 }, { w: 640, h: 480 }, { w: 320, h: 240 }, { w: 220, h: 240 }], // The maximum image heigh (window's height - space - thumbbar) maxImageHeight: $(window).height() - 125, // The maximum image width (window's width - navi-controls - space - caption bar) maxImageWidth: $(window).width() - 50 - Math.max(Math.min($(window).width() * 0.2, 320), 180), actualMaxSize: { w: 640, h: 480 }, cmdir: 'asc', continueKey: '', continueKeyPattern: isCategory ? /^file\|[\da-fA-F]+\|\d+$/ : /\d+\|.+/, lastPositionExpiry: 2, readFromScreen: false, readFromScreenSmallImages: false, licenseRecognization: [ // RegExp for the tag note to add to the thumb-page [/Category:CC[\- _]BY-SA.*/i, 'CC-By-SA'], [/Category:CC[\- _]BY.*/i, 'CC-By'], [/Category:CC[\- _]Zero.*/i, 'CC0'], [/Category:GFDL.*/i, 'GFDL'], [/Category:PD[\- _]Old.*/i, 'PD-old'], [/Category:PD[\- _]self.*/i, 'PD-self'], [/Category:PD[\- _]author.*/i, 'PD-author'], [/Category:PD.*/i, 'PDx'], [/Category:FAL/i, 'Art Libre - Free Art'], [/Category:Images requiring attribution/i, 'Attribution'], [/Category:Copyrighted free use.*/i, 'Copyrighted FreeUse'], [/Category:Mozilla Public License/i, 'MPL'], [/Category:GPL/i, 'GPL'], [/Category:LGPL/i, 'LGPL'], [/Category:Copyright by Wikimedia.*/i, '(c)WMF'], [/Category:Free screenshot.*/i, 'free-Screenshot'] ], onSlideChange: function(prevIndex, nextIndex) { var current, offset; var displayed = Math.floor(this.$thumbsUl.parent().width() / 81); var spaceRight = displayed - (nextIndex - this.hiddenLeft); var spaceLeft = (1 + nextIndex - this.hiddenLeft); if (spaceRight < 3) { // Time to slide viewport current = this.$thumbsUl.css('left').replace('px', ''); offset = (parseFloat(current) - (3 - spaceRight) * 81); this.$thumbsUl.animate({ left: offset }, 'fast'); this.hiddenLeft = this.hiddenLeft + (3 - spaceRight); } if (spaceLeft < 3 && nextIndex > 1) { current = this.$thumbsUl.css('left').replace('px', ''); offset = (parseFloat(current) + (3 - spaceLeft) * 81); this.$thumbsUl.animate({ left: offset }, 'fast'); this.hiddenLeft = this.hiddenLeft - (3 - spaceLeft); } if (nextIndex === 0) { this.$thumbsUl.animate({ left: 0 }, 'fast'); this.hiddenLeft = 0; } if (this.data.length - 5 < nextIndex) { // Time to fetch more this.queryApi(); } }, // accepts a delegate like such: function(prevIndex, nextIndex) { ... } onTransitionOut: undefined, // accepts a delegate like such: function(slide, caption, isSync, callback) { ... } onTransitionIn: undefined }; // Primary Galleriffic initialization function that should be called on the thumbnail container. $.fn.galleriffic = function(settings) { // Extend Gallery Object $.extend(this, { // Returns the version of the script version: $.galleriffic.version, // Current state of the slideshow isSlideshowRunning: false, slideshowTimeout: undefined, hiddenLeft: 0, apiURL: mw.util.wikiScript('api'), indexURL: mw.util.wikiScript('index'), initial: true, data: [], // This function is attached to the click event of generated hyperlinks within the gallery clickHandler: function(e, link) { this.pause(); // The href attribute holds the unique hash for an image var hash = $.galleriffic.normalizeHash($(link).attr('href')); $.galleriffic.gotoImage(hash); e.preventDefault(); }, createContainer: function() { var gallery = this; this.$slideshowContainer = $('<div class="slideshow-container"></div>'); this.$imageContainer = $('<div id="slideshow" class="slideshow"></div>'); this.$captionContainer = $('<div id="caption" class="caption-container"></div>'); this.$loadingContainer = $('<div id="loading" class="loader"></div>'); this.$controlsContainer = $('<div id="controls" class="controls"></div>'); // Gray lines for navigation this.$ctrBack = $('<div>', { 'class': 'bar-bwd' }).append($('<div>', { 'class': 'bar-btn-bwd' }).text('<')).click(function(e) { gallery.previous(); e.preventDefault(); }).mouseenter(function() { $(this).find('div').fadeIn('fast'); }).mouseleave(function() { $(this).find('div').fadeOut('fast'); }); this.$ctrFwd = $('<div>', { 'class': 'bar-fwd' }).append($('<div>', { 'class': 'bar-btn-fwd' }).text('>')).click(function(e) { gallery.next(); e.preventDefault(); }).mouseenter(function() { $(this).find('div').last().fadeIn('fast'); }).mouseleave(function() { $(this).find('div').last().fadeOut('fast'); }); this.$closeButton = $('<div>', { 'class': 'slideshow-close-button', 'title': this.hideText }).text('×').click(function(e) { gallery.pause(); gallery.toggleVisibility(); // stop propagation & prevent default return false; }); this.append('<div id="com-gs-thumbs" class="navigation"><ul class="com-gs-thumbs"></ul></div>'); this.append(this.$controlsContainer).append(this.$slideshowContainer); this.$slideshowContainer.append(this.$loadingContainer).append(this.$captionContainer).append(this.$imageContainer); this.append(this.$ctrBack).append(this.$ctrFwd.prepend(this.$closeButton)); this.$thumbsUl = this.find('ul.com-gs-thumbs'); }, // Scrapes the thumbnail container for thumbs and adds each to the gallery initializeThumbs: function() { var data = this.passedData; var gallery = this; $.each(data, function(i, imageData) { var hash; imageData.index = hash = imageCounter; imageData.gallery = gallery; var aspect = (imageData.width / imageData.height); var size = (aspect > 1) ? 'height' : 'width'; var $thumb = $('<li><a class="com-gs-thumb"><img ' + size + '=75px src="' + (imageData.slideThumb || imageData.slideUrl) + '" /></a></li>'); $thumb.css('opacity', 0.67).hover(function() { $(this).not('.selected').fadeTo('fast', 1); }, function() { $(this).not('.selected').fadeTo('fast', 0.67); }); gallery.$thumbsUl.append($thumb); imageData.caption = $('<div>').append( $('<a>', { href: imageData.link, text: imageData.title.replace('File:', '').replace(/\.[\w]{3,4}$/, '') })).html(); // Register the image globally allImages['' + hash] = imageData; // Setup attributes and click handler $thumb.find('a').attr('href', '#' + hash).removeAttr('name').click(function(e) { gallery.clickHandler(e, this); }); imageCounter++; }); this.data = this.data.concat(data); return this; }, isPreloadComplete: false, // Initalizes the image preloader preloadInit: function() { if (this.preloadAhead === 0) { return this; } this.preloadStartIndex = this.currentImage.index; var nextIndex = this.getNextIndex(this.preloadStartIndex); return this.preloadRecursive(this.preloadStartIndex, nextIndex); }, // Changes the location in the gallery the preloader should work // @param {Integer} index The index of the image where the preloader should restart at. preloadRelocate: function(index) { // By changing this startIndex, the current preload script will restart this.preloadStartIndex = index; return this; }, // Recursive function that performs the image preloading // @param {Integer} startIndex The index of the first image the current preloader started on. // @param {Integer} currentIndex The index of the current image to preload. preloadRecursive: function(startIndex, currentIndex) { // Check if startIndex has been relocated if (startIndex !== this.preloadStartIndex) { var nextIndex = this.getNextIndex(this.preloadStartIndex); return this.preloadRecursive(this.preloadStartIndex, nextIndex); } var gallery = this; // Now check for preloadAhead count var preloadCount = currentIndex - startIndex; if (preloadCount < 0) { preloadCount = this.data.length - 1 - startIndex + currentIndex; } if (this.preloadAhead >= 0 && preloadCount > this.preloadAhead) { // Do this in order to keep checking for relocated start index setTimeout(function() { gallery.preloadRecursive(startIndex, currentIndex); }, 500); return this; } var imageData = this.data[currentIndex]; if (!imageData) { return this; } // If already loaded, continue if (imageData.image) { return this.preloadNext(startIndex, currentIndex); } // Preload the image var image = new Image(); image.onload = function() { imageData.image = this; gallery.preloadNext(startIndex, currentIndex); }; image.alt = imageData.title; image.src = imageData.slideUrl; return this; }, // Called by preloadRecursive in order to preload the next image after the previous has loaded. // @param {Integer} startIndex The index of the first image the current preloader started on. // @param {Integer} currentIndex The index of the current image to preload. preloadNext: function(startIndex, currentIndex) { var nextIndex = this.getNextIndex(currentIndex); if (nextIndex === startIndex) { this.isPreloadComplete = true; } else { // Use setTimeout to free up thread var gallery = this; setTimeout(function() { gallery.preloadRecursive(startIndex, nextIndex); }, 100); } return this; }, // Safe way to get the next image index relative to the current image. // If the current image is the last, returns 0 getNextIndex: function(index) { var nextIndex = index + 1; if (nextIndex >= this.data.length) { nextIndex = 0; } return nextIndex; }, // Safe way to get the previous image index relative to the current image. // If the current image is the first, return the index of the last image in the gallery. getPrevIndex: function(index) { var prevIndex = index - 1; if (prevIndex < 0) { prevIndex = this.data.length - 1; } return prevIndex; }, // Pauses the slideshow pause: function() { this.isSlideshowRunning = false; $(document).triggerHandler('slideshow', ['actionPause', this]); // For external scripts if (this.slideshowTimeout) { clearTimeout(this.slideshowTimeout); this.slideshowTimeout = undefined; } if (this.$controlsContainer) { this.$controlsContainer.find('div.nav-controls a.gs-play-pause').removeClass('gs-play-pause').addClass('gs-play-play').attr('title', this.playLinkText).attr('href', '#play'); } return this; }, // Plays the slideshow play: function() { this.isSlideshowRunning = true; $(document).triggerHandler('slideshow', ['actionPlay', this]); // For external scripts if (this.$controlsContainer) { this.$controlsContainer.find('div.nav-controls a.gs-play-play').removeClass('gs-play-play').addClass('gs-play-pause').attr('title', this.pauseLinkText).attr('href', '#pause'); } if (!this.slideshowTimeout) { var gallery = this; this.slideshowTimeout = setTimeout(function() { gallery.next(true); }, this.delay); } return this; }, // Toggles the state of the slideshow (playing/paused) toggleSlideshow: function() { if (this.isSlideshowRunning) { this.pause(); } else { this.play(); } return this; }, // Advances the gallery to the next image. // @param {Boolean} dontPause Specifies whether to pause the slideshow. next: function(dontPause) { this.gotoIndex(this.getNextIndex(this.currentImage.index), dontPause); return this; }, // Navigates to the previous image in the gallery. // @param {Boolean} dontPause Specifies whether to pause the slideshow. previous: function(dontPause) { this.gotoIndex(this.getPrevIndex(this.currentImage.index), dontPause); return this; }, // Navigates to the image at the specified index in the gallery // @param {Integer} index The index of the image in the gallery to display. // @param {Boolean} dontPause Specifies whether to pause the slideshow. gotoIndex: function(index, dontPause) { if (!dontPause) { this.pause(); } if (index < 0) { index = 0; } else if (index >= this.data.length) { index = this.data.length - 1; } var imageData = this.data[index]; this.gotoImage(imageData); return this; }, // This function is guaranteed to be called anytime a gallery slide changes. // @param {Object} imageData An object holding the image metadata of the image to navigate to. gotoImage: function(imageData) { var index = imageData.index; if (this.onSlideChange) { this.onSlideChange(this.currentImage.index, index); } this.currentImage = imageData; this.preloadRelocate(index); this.refresh(); return this; }, // Returns the default transition duration value. The value is halved when not // performing a synchronized transition. // @param {Boolean} isSync Specifies whether the transitions are synchronized. getDefaultTransitionDuration: function(isSync) { if (isSync) { return this.defaultTransitionDuration; } return this.defaultTransitionDuration / 2; }, // Rebuilds the slideshow image and controls and performs transitions refresh: function() { var imageData = this.currentImage; if (!imageData) { return this; } var previousSlide = this.$imageContainer.find('span.current').addClass('previous').removeClass('current'); var previousCaption = 0; if (this.$captionContainer) { previousCaption = this.$captionContainer.find('span.current').addClass('previous').removeClass('current'); } // Perform transitions simultaneously if the next image is already preloaded var isSync = imageData.image; // Flag we are transitioning var isTransitioning = true; var gallery = this; var transitionOutCallback = function() { // Flag that the transition has completed isTransitioning = false; // Remove the old slide previousSlide.remove(); // Remove old caption if (previousCaption) { previousCaption.remove(); } if (!isSync) { if (imageData.image && imageData.index === gallery.data[gallery.currentImage.index].index) { gallery.buildImage(imageData, isSync); } else { // Show loading container if (gallery.$loadingContainer) { gallery.$loadingContainer.show(); } } } }; if (previousSlide.length === 0) { // For the first slide, the previous slide will be empty, so we will call the callback immediately transitionOutCallback(); } else { previousSlide.fadeTo(this.getDefaultTransitionDuration(isSync), 0.0, transitionOutCallback); if (previousCaption) { previousCaption.fadeTo(this.getDefaultTransitionDuration(isSync), 0.0); } } // Go ahead and begin transitioning in of next image if (isSync) { this.buildImage(imageData, isSync); } if (!imageData.image) { var image = new Image(); // Wire up mainImage onload event image.onload = function() { imageData.image = this; // Only build image if the out transition has completed and we are still on the same image hash if (!isTransitioning && imageData.index === gallery.data[gallery.currentImage.index].index) { gallery.buildImage(imageData, isSync); } }; // set alt and src image.alt = imageData.title; image.src = imageData.slideUrl; } // This causes the preloader (if still running) to relocate out from the currentIndex this.relocatePreload = true; return this.syncThumbs(); }, // Shrinking The Tower of Babel // Hide other languages, if script is available shrinkTowerOfBabel: function($node) { if (window.multilingual) { var ml = window.multilingual; ml.langCountThreshold = 2; ml.method = 'prepend'; ml.$p = ml.$OuterContainer = $node; ml.init(); } }, // Called by the refresh method after the previous image has been transitioned out or at the same time // as the out transition when performing a synchronous transition. // @param {Object} imageData An object holding the image metadata of the image to build. // @param {Boolean} isSync Specifies whether the transitions are synchronized. buildImage: function(imageData, isSync) { var gallery = this; var nextIndex = this.getNextIndex(imageData.index); // We have loaded bigger images, size them down, now; 1 prevents upscaling (looks ugly) var scaleRatio = Math.max( imageData.width / this.maxImageWidth, imageData.height / (this.maxImageHeight - 20), 1); var imgWidth = Math.floor(imageData.width / scaleRatio); var imgHeight = Math.floor(imageData.height / scaleRatio); // computing the "center-position of the space" var hSpace = isRtl ? (this.$captionContainer.position().left - 2 * this.$imageContainer.position().left) : (this.$imageContainer.width() - this.$captionContainer.position().left - this.$captionContainer.width()); var hPos = isRtl ? (this.$imageContainer.width() - this.$captionContainer.position().left + 2 * this.$imageContainer.position().left + (hSpace - imgWidth) / 2) : (this.$captionContainer.position().left + this.$captionContainer.width() + (hSpace - imgWidth) / 2); var vSpace = this.$imageContainer.height() - 130; var top = (vSpace - imgHeight) / 2; // Send a XHrequest in case of unknown description if (typeof imageData.description !== 'string') { this.queryFile(imageData.title); } if (typeof this.data[nextIndex].description !== 'string') { this.queryFile(this.data[nextIndex].title); } // Construct new hidden span for the image var newSlide = this.$imageContainer.append( $('<span>', { 'class': 'image-wrapper current' }).css(isRtl ? 'right' : 'left', hPos).css('top', top).append( $('<a>', { 'class': 'advance-link', href: imageData.link, title: imageData.title, target: '_blank', css: { width: imgWidth } }))).find('span.current').css('opacity', '0'); newSlide.find('a').append(imageData.image); var descript = imageData.description || $('<span>').append($.createSpinner(), mw.html.escape(this.descriptionLoadText)); var newCaption = 0; var extraParams = '&gsDir=' + this.cmdir + '&gsAutoStart=1' + (mw.util.getParamValue('withJS') ? ('&withJS=' + mw.util.getParamValue('withJS')) : '') + (mw.util.getParamValue('withCSS') ? ('&withCSS=' + mw.util.getParamValue('withCSS')) : ''); if (this.$captionContainer) { // Construct new hidden caption for the image newCaption = this.$captionContainer.append( $('<span>', { 'class': 'image-caption current', style: 'height:' + (this.maxImageHeight - 30) + 'px;' })).find('span.current').css('opacity', '0').append(imageData.caption, $('<br>')).append( $('<div>', { 'class': 'gs-img-description', id: 'desc' + imageData.index, append: descript })).append( $('<div>', { 'class': 'gs-img-uploader' }).append(imageData.$user.clone())).append( imageData.$cats, imageData.$licenses).append( $('<div>', { 'class': 'gs-img-metrics', html: imageData.oWidth + ' × ' + imageData.oHeight + ' / ' + imageData.oSize })).append(imageData.contKey ? $('<div>', { 'class': 'cont-key-container gs-icon', title: this.continueKeyHowTo, append: $('<a>', { href: mw.util.getUrl(mw.config.get('wgPageName')) + '?gsContinue=' + imageData.contKey + extraParams, target: '_blank', text: imageData.contKey }) }) : ''); } this.shrinkTowerOfBabel(newCaption.find('.gs-img-description')); // Hide the loading conatiner if (this.$loadingContainer) { this.$loadingContainer.hide(); } // Transition in the new image if (this.onTransitionIn) { this.onTransitionIn(newSlide, newCaption, isSync); } else { newSlide.fadeTo(this.getDefaultTransitionDuration(isSync), 1.0); if (newCaption) { newCaption.fadeTo(this.getDefaultTransitionDuration(isSync), 1.0); } } if (this.isSlideshowRunning) { if (this.slideshowTimeout) { clearTimeout(this.slideshowTimeout); } this.slideshowTimeout = setTimeout(function() { gallery.next(true); }, this.delay); } // Save the current position in a cookie or delete the cookie if (imageData.contKey) { this.saveContinueKey(imageData.contKey); } else { this.saveContinueKey(null); } $(document).triggerHandler('slideshow', ['newSlide', this]); // For external scripts return this; }, saveContinueKey: function(key) { // Breaks some clients! Needs investigation and migration to jStorage. return; /*if (this.remoteUse) { return; } $.cookie('gs' + mw.config.get('wgPageName').replace('Category:', '1:').replace('Commons:', '2:'), key, { expires: this.lastPositionExpiry });*/ }, getContinueKey: function() { return $.cookie('gs' + mw.config.get('wgPageName').replace('Category:', '1:').replace('Commons:', '2:')); }, // Applies the selected class to the current image's corresponding thumbnail. // Also checks if the current page has changed and updates the displayed page of thumbnails if necessary. syncThumbs: function() { // Remove existing selected class and add selected class to new thumb var $thumbs = this.$thumbsUl.children(); $thumbs.filter('.selected').removeClass('selected').fadeTo('fast', 0.67); $thumbs.eq(this.currentImage.index).addClass('selected').fadeTo('fast', 1); return this; }, findImageSize: function(h, w) { var that = this, sOld = this.defaultSizes[0]; $.each(this.defaultSizes, function(i, s) { if (s.w > w || s.h > h) { sOld = s; return; } else { that.actualMaxSize = sOld; return false; } }); }, init: function() { var $navCont, $playBtn, $prevBtn, $nextBtn; this.createContainer(); var gallery = this; $(window).resize(function() { gallery.maxImageHeight = $(window).height() - gallery.$thumbsUl.height() - gallery.$controlsContainer.height(); gallery.maxImageWidth = gallery.$slideshowContainer.width() - gallery.$captionContainer.width() - 16; gallery.findImageSize(gallery.maxImageHeight, gallery.maxImageWidth); gallery.css('height', $(window).height()); }).resize(); // Initialize the thumbails this.initializeThumbs(); this.currentImage = this.data[0]; // Hide the loadingContainer this.$loadingContainer.hide(); this.gotoIndex(0); // Setup controls if (this.autoPlay) { $playBtn = $('<a>').attr({ 'class': 'gs-play gs-play-pause', title: this.pauseLinkText, href: '#pause' }); } else { $playBtn = $('<a>').attr({ 'class': 'gs-play gs-play-play', title: this.playLinkText, href: '#play' }); } $playBtn.click(function(e) { gallery.toggleSlideshow(); e.preventDefault(); }); $prevBtn = $('<a>').attr({ 'class': 'gs-play gs-play-bwd', title: this.prevLinkText, href: '#previous' }).click(function(e) { gallery.previous(); e.preventDefault(); }); $nextBtn = $('<a>').attr({ 'class': 'gs-play gs-play-fwd', title: this.nextLinkText, href: '#next' }).click(function(e) { gallery.next(); e.preventDefault(); }); $navCont = $('<div class="nav-controls">'); $navCont.hover(function() { $(this).fadeTo('fast', 0.75); }, function() { $(this).fadeTo('fast', 0.4); }); this.$controlsContainer.append( $navCont.append($prevBtn, $playBtn, $nextBtn)); // Option icons if (!this.remoteUse) { this.$continueKey = $('<a>', { 'class': 'continue-key-insert gs-icon gs-icon-keyGo', href: '#', title: gallery.continueKeyInsertBtn, click: function(e) { e.preventDefault(); var ckey = prompt(gallery.continueKeyInsert, gallery.cont ? gallery.cont : ''); ckey = $.trim(ckey); if (gallery.continueKeyPattern.test(ckey)) { gallery.cont = ckey; } else { alert(gallery.continueKeyInvalid); } } }); } var $setDelay = $('<a>', { 'class': 'delay-insert gs-icon gs-icon-clock', href: '#', title: gallery.delayInsertBtn, click: function(e) { e.preventDefault(); var delay = prompt(gallery.delayInsert, gallery.delay ? gallery.delay : ''); if (!delay) { return; } delay = $.trim(delay.replace(/ms|s/, '')); if (/^\d+$/.test(delay)) { if (delay > 1000) { gallery.delay = delay; } else if ((0.9 < delay) && (delay < 61)) { gallery.delay = delay * 1000; } else { alert(gallery.delayInvalid); } // Set cookie $.cookie('slideshow-delay', gallery.delay, { expires: 100, path: '/' }); } else { alert(gallery.delayInvalid); } } }); var $helpLink = $('<a>', { 'class': 'gs-help-link gs-icon gs-icon-help', href: mw.util.getUrl('Help:Slideshow'), title: gallery.helpLinkTitle, target: '_blank' }); var otherCont = $('<div>', { 'class': 'other-controls' }); this.$controlsContainer.append( otherCont.append( (this.$continueKey || ' '), $setDelay, $helpLink)); otherCont.hover(function() { $(this).fadeTo('fast', 1); }, function() { $(this).fadeTo('fast', 0.6); }); // Setup Keyboard Navigation if (this.enableKeyboardNavigation) { $(document).keydown(function(e) { var key = e.charCode || e.keyCode || 0; switch (key) { case 32: // space gallery.next(); e.preventDefault(); break; case 35: // End gallery.gotoIndex(gallery.data.length - 1); e.preventDefault(); break; case 37: // left arrow gallery.previous(); e.preventDefault(); break; case 39: // right arrow gallery.next(); e.preventDefault(); break; case 19: // break gallery.toggleSlideshow(); e.preventDefault(); break; } }); $(document).keyup(function(e) { var key = e.charCode || e.keyCode || 0; //Hide on escape if ($('#SlideContainer').height() && key === 27) { gallery.pause(); gallery.toggleVisibility(); } }); } // Auto start the slideshow if (this.autoPlay) { this.play(); } // Kickoff Image Preloader after 1 second setTimeout(function() { gallery.preloadInit(); }, 1000); $(document).triggerHandler('slideshow', ['shown', this]); // For external scripts }, start: function() { $(document).triggerHandler('slideshow', ['starting', this]); // For external scripts $('#GallerySlideStartButtons').find('button').hide(); $('#SlideContainer').animate({ height: $(window).height() }); // Once done, hide scrollbar // disabled for IE 6/7 if ('\v' !== 'v') { $('body').css('overflow', 'hidden'); } // Settings from URL var autoPlay = mw.util.getParamValue('gsAutoPlay'); if (autoPlay) { if ('1' === autoPlay || 'true' === autoPlay || 'yes' === autoPlay || '-1' === autoPlay) { this.autoPlay = true; } else { this.autoPlay = false; } } var delay = mw.util.getParamValue('gsDelay') || $.cookie('slideshow-delay'); if (delay) { if (/^\d+$/.test(delay)) { if (delay > 1999) { this.delay = delay; } else if ((1 < delay) && (delay < 61)) { this.delay = delay * 1000; } } } var cmdir = mw.util.getParamValue('gsDir'); if (cmdir) { if ('climbing' === cmdir || 'ascending' === cmdir || 'asc' === cmdir || '123' === cmdir || 'rising' === cmdir) { this.cmdir = 'asc'; } else { this.cmdir = 'desc'; } } var cmcontinue = mw.util.getParamValue('gsContinue'); if (cmcontinue) { if (this.continueKeyPattern.test(cmcontinue)) { this.cont = cmcontinue; } else { this.cont = ''; } } var readFromScreen = mw.util.getParamValue('gsReadFromScreen'); if (readFromScreen) { this.readFromScreen = true; this.remoteUse = true; } this.findImageSize(this.maxImageHeight, this.maxImageWidth); this.queryApi(); // For IE 6 if ('\v' === 'v') { setTimeout(function() { window.location.hash = '#SlideContainer'; }, 2000); } // Display dynamic help from Help:Gadget-GallerySlideshow/OSDHelp var _this = this; var showHelpSplash = function(result) { if (!result) { return; } result = $(result); result.find('.editsection').remove(); var $slideC = $('#SlideContainer'); var helpSplash = $('<div id="GallerySlideHelpSplash"></div>').append(result); setTimeout(function() { helpSplash.fadeOut(); }, 15000); helpSplash.css('right', 0); helpSplash.css('bottom', 0); $slideC.prepend(helpSplash.hide().fadeTo(400, 0.7)); helpSplash.click(function() { $(this).fadeOut(); }); }; $.get(this.indexURL, { title: 'Help:Gadget-GallerySlideshow/OSDHelp', action: 'render', uselang: mw.config.get('wgUserLanguage') }, showHelpSplash); }, toggleVisibility: function() { $('#GallerySlidestart').toggle().off('click').click(GallerySlide.toggleVisibility); $('#GallerySlideStartButtons').buttonset(); $('#SlideContainer').slideToggle(function() { $(document).triggerHandler('slideshow', ['visibility', GallerySlide]); }); $('body').css('overflow', 'visible'); // For external scripts }, queryFile: function(title) { var _this = this; var params = { action: 'render', title: title }; $.ajax({ url: this.indexURL, cache: true, dataType: 'html', data: params, type: 'GET', success: function(result) { _this.processDetails(result, title); } }); }, processDetails: function(result, title) { if (typeof result !== 'string') { return; } var i, dItem, dDescription, $node, parsedDOM = $(result), $author; parsedDOM.find('table, div').attr('style', ''); parsedDOM.find('table').attr('cellspacing', 1).attr('cellpadding', 0); // Clean up author field. Some users are very important and therefore designed logos etc. for themselves // But they are not really important and possibly distract the slideviewer $author = $('#fileinfotpl_aut', parsedDOM).siblings().eq(0).contents().clone(); $author.find('img').remove(); $author.find('*').removeAttr('style'); $author.find('font').contents().unwrap(); $author.find('b').contents().unwrap(); dDescription = $('<div>').append( $('<div>').addClass('gs-img-description-desc').append( $('#fileinfotpl_desc', parsedDOM).siblings().eq(0).contents()), $('<div>').addClass('gs-img-description-aut').append($author).prepend( $('<span>', { 'class': 'gs-author-label' }).text($(parsedDOM).find('#fileinfotpl_aut').text())), $('<div>').addClass('gs-img-description-date').append( $(parsedDOM).find('#fileinfotpl_date').siblings().eq(0).contents().clone()).prepend( $('<span>', { 'class': 'gs-date-label' }).text($('#fileinfotpl_date', parsedDOM).text()))).html(); if (!dDescription) { dDescription = result; } for (i in this.data) { dItem = this.data[i]; if (dItem.title === title) { dItem.description = dDescription; $node = $('#desc' + i); if ($node.length !== 0) { $node.html(dDescription); this.shrinkTowerOfBabel($node); } } } }, queryApi: function() { var _this = this; var params = {}; if (_this.queryRunning || (_this.cont === false && !_this.readFromScreen)) { return; } $(document).triggerHandler('slideshow', ['beforeQuery', this]); // For external scripts // New restrictions in image info API var limit = Math.min(Math.floor($('#SlideContainer').width() / 81) + 1, 50); if (_this.readFromScreen) { _this.qFiles = []; if (!_this.$galleryBoxes) { _this.$galleryBoxes = $('.gallerybox'); _this.queryImageId = -1; } if (_this.queryImageId === (_this.$galleryBoxes.length - 1)) { return; // All images loaded } _this.$galleryBoxes.each(function(i, e) { if (_this.queryImageId >= i) { return; } if (_this.qFiles.length === limit) { return; } _this.queryImageId = i; _this.qFiles.push('File:' + mw.libs.commons.titleFromImgSrc($(e).find('.thumb').find('img').attr('src'))); }); } if (_this.readFromScreen) { params = { format: 'json', action: 'query', titles: _this.qFiles.join('|'), rawcontinue: 1, prop: 'imageinfo|categories', clprop: 'hidden', cllimit: 500, iiprop: 'url|user|size', iilimit: 1, iiurlwidth: this.actualMaxSize.w, iiurlheight: this.actualMaxSize.h, indexpageids: 1 }; } else if (isCategory) { params = { format: 'json', action: 'query', generator: 'categorymembers', rawcontinue: 1, gcmtitle: mw.config.get('wgPageName'), gcmlimit: limit, gcmtype: 'file', gcmdir: this.cmdir, prop: 'imageinfo|categories', clprop: 'sortkey|hidden', cllimit: 500, iiprop: 'url|user|size', iilimit: 1, iiurlwidth: this.actualMaxSize.w, iiurlheight: this.actualMaxSize.h, indexpageids: 1 }; if (this.cont) { params.gcmcontinue = this.cont; } } else { params = { action: 'query', generator: 'images', rawcontinue: 1, titles: mw.config.get('wgPageName'), gimlimit: limit, prop: 'imageinfo|categories', clprop: 'hidden', cllimit: 500, iiprop: 'url|user|size', iilimit: 1, iiurlwidth: this.actualMaxSize.w, iiurlheight: this.actualMaxSize.h, format: 'json', indexpageids: 1 }; if (this.cont) { params.gimcontinue = this.cont; } } if (!this.initial && !this.cont && !this.readFromScreen) { return; } _this.queryRunning = true; $.ajax({ url: this.apiURL, cache: false, dataType: 'json', data: params, type: 'POST', success: function(result) { _this.queryRunning = false; _this.processReturn(result); }, error: function() { _this.queryRunning = false; } }); }, processReturn: function(result) { $(document).triggerHandler('slideshow', ['afterQuery', this]); // For external scripts var pages = result.query.pages, pages2 = [], data = [], i = 0, j = 0; if (result['query-continue'] && (result['query-continue'].categorymembers || result['query-continue'].images)) { if (typeof this.cont !== 'undefined') { this.contOld = this.cont; } this.cont = isCategory ? result['query-continue'].categorymembers.gcmcontinue : result['query-continue'].images.gimcontinue; } else { this.cont = false; } // Fromatt a number var fm = function(iNr) { iNr += ''; var rx = /(\d+)(\d{3})/; while (rx.test(iNr)) { iNr = iNr.replace(rx, '$1' + '<span class="digit-separator"> </span>' + '$2'); } return iNr; }; if (this.readFromScreen) { // sorting the mess, the API has created to fit the page-diplay var qFilesL = this.qFiles.length, qFilesTitle, qfi, p, pg; sreenreadorderloop: for (qfi = 0; qfi !== qFilesL; qfi++) { qFilesTitle = this.qFiles[qfi]; for (p in pages) { pg = pages[p]; if (pg.title === qFilesTitle) { pages2.push(pg); continue sreenreadorderloop; } } } pages = pages2; } else { // This is for Chrome: https://bugs.chromium.org/p/v8/issues/detail?id=164 // and Opera and InternetExplorer var pgIds = result.query.pageids; for (j = 0; j < pgIds.length; j++) { pages2.push(pages[pgIds[j]]); } pages = pages2; } for (j = 0; j < pages.length; j++) { var v = pages[j]; if ('undefined' !== typeof v.missing || !v.imageinfo) { continue; } var r = v.imageinfo[v.imageinfo.length - 1], rc = v.imageinfo[0], n = data[i] = {}, sortkey = '', $cats = $('<div>', { 'class': 'cat-wrap' }), $licenses = $('<div>', { 'class': 'license-wrap' }).append($('<span>', { style: 'display:inline-block;' }).text(this.licenseLabel)); // Process categories; Extract visible cats, sortkey for current cat, licenses if (v.categories) { var c, clen = v.categories.length; processCats: for (c = 0; c < clen; c++) { var tCat = v.categories[c]; if (isCategory && tCat.title === mw.config.get('wgPageName').replace(/_/g, ' ')) { sortkey = 'file' + '|' + tCat.sortkey + '|' + v.pageid; } if (typeof tCat.hidden === 'undefined') { $cats.append( $('<a>', { 'class': 'cat-label', href: mw.util.getUrl(tCat.title), target: '_blank', text: tCat.title.replace('Category:', '') }), ' '); } else { var recogID, recogLen = this.licenseRecognization.length; for (recogID = 0; recogID < recogLen; recogID++) { if (this.licenseRecognization[recogID][0].test(tCat.title)) { $licenses.append( $('<span>', { 'class': 'license-label', title: tCat.title.replace('Category:', ''), html: this.licenseRecognization[recogID][1] }), ' '); continue processCats; } } } } } if (!isCategory) { sortkey = mw.config.get('wgArticleId') + '|' + v.title.replace('File:', ''); } n.title = v.title; n.link = rc.descriptionurl; n.slideUrl = rc.thumburl; n.width = rc.thumbwidth; n.height = rc.thumbheight; n.oWidth = fm(r.width); n.oHeight = fm(r.height); n.oSize = fm(r.size >> 10) + ' <abbr title="1 KibiByte= 1024 Bytes">KiB</abbr>'; n.$user = $('<span>').append( $('<span>', { 'class': 'gs-uploader-label' }).text(this.uploaderLabel)).append( $('<span>').css({ direction: 'ltr', display: 'inline-block' }).append( $('<a>', { href: mw.util.getUrl(mw.config.get('wgFormattedNamespaces')[2] + ':' + r.user), target: '_blank', text: r.user }), ' (', $('<a>', { href: mw.util.getUrl(mw.config.get('wgFormattedNamespaces')[3] + ':' + r.user), target: '_blank', text: 'talk' }), ')')); n.$cats = $cats; n.$licenses = $licenses; n.contKey = (sortkey || this.contOld); // reset to empty string when using screen-read-mode (too instable to rely on it) if (this.readFromScreen) { n.contKey = ''; } i++; } this.passedData = data; if (this.initial) { this.init(); } else { this.initializeThumbs(); } this.initial = false; } }); // Now initialize the gallery $.extend(this, defaults, i18n, settings); return this; }; $(document).ready(function() { if ($('.gallery li').length < 2) { // no need for a gallery with a few images return; } mw.loader.using(['jquery.cookie', 'mediawiki.util', 'jquery.ui.dialog', 'jquery.spinner'], function() { $('body').append('<div id="SlideContainer"></div>'); window.GallerySlide = $('#SlideContainer').galleriffic(); $(document).triggerHandler('slideshow', ['codeLoaded', window.GallerySlide]); // For external scripts }); }); // When this script is loaded, someone started the slideshow gadget // To prepare un-defaulting, set a user preference and count usage if (mw.user.isAnon()) return; mw.loader.using('ext.gadget.SettingsManager', function() { mw.libs.settingsManager.fetchGadgetSetting('slideshow-usage').done(function(o, v) { v = Number(v) || 0; v++; if (v > 9) return; mw.libs.settingsManager.switchGadgetPref('slideshow-usage', v); }); }); })(jQuery);