위키쨩은 여러 서브컬쳐 작품과 캐릭터, 업계 인물들에 대한 정보를 기록으로 남길 수 있는 웹 저장소입니다. 나의 관심 대상을 다른 사람들에게 소개하는 데 이용해 보세요.
"미디어위키:Common.js"의 두 판 사이의 차이
(주석 툴팁 강화: 애니메이션 추가, 셀렉터 최적화) |
|||
96번째 줄: | 96번째 줄: | ||
/** | /** | ||
− | + | * 주석 툴팁 | |
− | * 주석 | + | * @Author(s) User:Cafeinlove |
− | + | * @License MIT License | |
− | * @Author | + | |
− | * @License | + | |
*/ | */ | ||
− | void function( document ) { | + | void function( window, document, mw, undefined ) { |
"use strict"; | "use strict"; | ||
− | var | + | /** |
− | var | + | * Namespace numbers for pages that do not need this module |
+ | * -1: Special | ||
+ | * 8: MediaWiki | ||
+ | */ | ||
+ | var excludedNS = [-1, 8]; | ||
+ | |||
+ | if ( excludedNS.indexOf( mw.config.get("wgNamespaceNumber") ) > 0 ) return; | ||
+ | if ( mw.config.get( "wgAction" ) != "view" ) return; | ||
+ | |||
+ | // triggering elements (links like [1], [2]...) | ||
+ | var container = document.getElementById( "mw-content-text" ); | ||
+ | var citations = container.getElementsByClassName( "reference" ); | ||
if ( citations.length === 0 ) return; | if ( citations.length === 0 ) return; | ||
+ | var tooltip; // dynamic tooltip | ||
+ | var animation; // animationFrame queue | ||
+ | |||
+ | /** | ||
+ | * init | ||
+ | * @description initialize the tooltip | ||
+ | */ | ||
void function init() { | void function init() { | ||
// create tooltip element and append it to body element | // create tooltip element and append it to body element | ||
tooltip = document.createElement( "div" ); | tooltip = document.createElement( "div" ); | ||
tooltip.id = "liveReference"; | tooltip.id = "liveReference"; | ||
− | |||
document.body.appendChild( tooltip ); | document.body.appendChild( tooltip ); | ||
− | // | + | // delegate mouse events to "#mw-content-text" |
− | + | [ "mouseover", "mouseout" ].forEach( function( e ) { | |
− | + | container.addEventListener( e, function( event ) { | |
− | + | checkEvent( event ); | |
+ | }); | ||
}); | }); | ||
}(); | }(); | ||
− | function setTooltip( citation ) { | + | /** |
− | var sourceHash = | + | * checkTarget |
+ | * @description check detected mouse event and call tooltip functions if necessary | ||
+ | * @param{Object} detected mouse event object | ||
+ | */ | ||
+ | function checkEvent( event ) { | ||
+ | var target = event.target; | ||
+ | |||
+ | if ( target.tagName != "A" ) return; | ||
+ | if ( !target.parentNode.classList.contains( "reference" ) ) return; | ||
+ | |||
+ | if ( event.type == "mouseover" ) { | ||
+ | setTooltip( target ); | ||
+ | } else { | ||
+ | resetTooltip(); | ||
+ | } | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * setTooltip | ||
+ | * @description set content and position of the tooltip | ||
+ | * @param{HTMLElement} the citation link element which user has mouse-overed | ||
+ | */ | ||
+ | function setTooltip( trigger ) { | ||
+ | // replace() escapes all dots within the anchor's hash | ||
+ | // preventing error when <ref/>'s name attribute has unicode string as its value | ||
+ | var sourceHash = trigger.hash.replace( /\./g, "\\." ); | ||
var sourceEl = document.querySelector( sourceHash + " .reference-text" ); | var sourceEl = document.querySelector( sourceHash + " .reference-text" ); | ||
var scroll = { | var scroll = { | ||
− | top: document.body.scrollTop || document.documentElement. | + | top: document.body.scrollTop || document.documentElement.scrollTop, |
left: document.body.scrollLeft || document.documentElement.scrollLeft | left: document.body.scrollLeft || document.documentElement.scrollLeft | ||
}; | }; | ||
− | var rect = | + | var rect = trigger.getBoundingClientRect(); |
var isInViewport = ( rect.top - tooltip.offsetHeight ) > 100; | var isInViewport = ( rect.top - tooltip.offsetHeight ) > 100; | ||
− | // position tooltip | + | // position the tooltip |
tooltip.style.top = ( scroll.top + rect.top ) + "px"; | tooltip.style.top = ( scroll.top + rect.top ) + "px"; | ||
tooltip.style.left = ( scroll.left + rect.left ) + "px"; | tooltip.style.left = ( scroll.left + rect.left ) + "px"; | ||
+ | |||
+ | // copy content from target element to the tooltip | ||
tooltip.innerHTML = sourceEl.innerHTML; | tooltip.innerHTML = sourceEl.innerHTML; | ||
+ | |||
+ | // flip the tooltip if it is going off the viewport | ||
if ( !isInViewport ) tooltip.classList.add( "is-flipped" ); | if ( !isInViewport ) tooltip.classList.add( "is-flipped" ); | ||
− | tooltip | + | // fade in the tooltip |
+ | fadeIn( tooltip ); | ||
} | } | ||
+ | /** | ||
+ | * resetTooltip | ||
+ | * @description reset content and position of the tooltip | ||
+ | */ | ||
function resetTooltip() { | function resetTooltip() { | ||
− | tooltip.className = "" | + | fadeOut( tooltip, function() { |
− | + | // reset flip | |
+ | tooltip.className = ""; | ||
− | + | // remove content | |
− | + | while ( tooltip.lastChild ) { | |
+ | tooltip.removeChild( tooltip.lastChild ); | ||
+ | } | ||
+ | }); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * fadeIn | ||
+ | * @description fade in the tooltip | ||
+ | * @param{HTMLElement} element to fade in | ||
+ | */ | ||
+ | function fadeIn( el ) { | ||
+ | var opacity = 0; | ||
+ | var interval = 1 / 8; | ||
+ | |||
+ | el.style.opacity = opacity; | ||
+ | el.style.visibility = "visible"; | ||
+ | |||
+ | function animate() { | ||
+ | opacity += interval; | ||
+ | |||
+ | if ( opacity < 1 ) { | ||
+ | el.style.opacity = opacity; | ||
+ | animation = window.requestAnimationFrame( animate ); | ||
+ | } else { | ||
+ | el.style.opacity = 1; | ||
+ | return true; | ||
+ | } | ||
} | } | ||
+ | |||
+ | stopAnimation(); | ||
+ | animate(); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * fadeOut | ||
+ | * @description fade out the tooltip | ||
+ | * @param{HTMLElement} element to fade out | ||
+ | * @param{Function} function to execute when fade animation completes | ||
+ | */ | ||
+ | function fadeOut( el, callback ) { | ||
+ | var opacity = 1; | ||
+ | var interval = 1 / 8; | ||
+ | |||
+ | function animate() { | ||
+ | opacity -= interval; | ||
+ | |||
+ | if ( opacity > 0 ) { | ||
+ | el.style.opacity = opacity; | ||
+ | animation = window.requestAnimationFrame( animate ); | ||
+ | } else { | ||
+ | el.style.opacity = 0; | ||
+ | el.removeAttribute( "style" ); | ||
+ | |||
+ | if ( typeof callback == "function" ) callback(); | ||
+ | |||
+ | return true; | ||
+ | } | ||
+ | } | ||
+ | |||
+ | stopAnimation(); | ||
+ | animate(); | ||
+ | } | ||
+ | |||
+ | /** | ||
+ | * stopAnimation | ||
+ | * @description clear animationFrame queue | ||
+ | */ | ||
+ | function stopAnimation() { | ||
+ | if ( !animation ) return; | ||
+ | window.cancelAnimationFrame( animation ); | ||
+ | animation = undefined; | ||
} | } | ||
− | }( document ); | + | }( window, document, window.mediaWiki ); |
/* 네타바레 감추기 | /* 네타바레 감추기 |
2017년 2월 24일 (금) 17:46 기준 최신판
/*! * 위키쨩 페이지별 자바스크립트 * * 작성자: wiki-chan.net * 저작권: Refer to each script block (MIT license unless defined differently) */ // 페이지 설정을 변수에 캐시 var cfg = mw.config.get([ "wgAction", "wgCanonicalSpecialPageName", "wgIsMainPage", "wgNamespaceNumber" ]); /* Pure JavaScript Tabs https://github.com/ltebean/tabs @Author: Itebean @License: MIT License ----------------------------------------*/ ! function( name, definition ) { if ( typeof module != "undefined" && module.exports ) module.exports = definition(); else if ( typeof define == "function" ) define( definition ); else this[name] = definition(); }( "wikitabs", function() { return function tabs( container ) { var tabs = container.getElementsByClassName( "tab" ); var panes = container.getElementsByClassName( "tab-pane" ); each(tabs, function( i, tab ) { tab.addEventListener( "click", function( e ) { activate( tabs, i ); activate( panes, i ); }); }) function activate( tabs, index ) { each( tabs, function( i, tab ) { if ( i != index ) { tab.classList.remove( "active" ); } else { tab.classList.add( "active" ); } }); } } function each( elements, fn ) { for ( var i = elements.length - 1; i >= 0; i-- ) { fn( i, elements[i] ); } } }); var tabs_container = document.getElementsByClassName( "tab-container" ); Array.from(tabs_container).forEach( function(e) { wikitabs(e); }); /* 코드 블럭을 더블클릭해서 내용 모두 선택하기 ----------------------------------------*/ (function() { const selectors = ["code", "pre", "samp", "var"]; var targetArray = []; var nodeList; selectors.forEach( function(e) { nodeList = document.getElementsByTagName(e); targetArray = targetArray.concat( Array.from(nodeList) ); }); targetArray.forEach( function(e) { e.addEventListener("dblclick", function() { selectCode(e); }); }); function selectCode(el) { var range = document.createRange(); var selection = window.getSelection(); range.selectNodeContents(el); selection.removeAllRanges(); selection.addRange(range); } })(); /** * 주석 툴팁 * @Author(s) User:Cafeinlove * @License MIT License */ void function( window, document, mw, undefined ) { "use strict"; /** * Namespace numbers for pages that do not need this module * -1: Special * 8: MediaWiki */ var excludedNS = [-1, 8]; if ( excludedNS.indexOf( mw.config.get("wgNamespaceNumber") ) > 0 ) return; if ( mw.config.get( "wgAction" ) != "view" ) return; // triggering elements (links like [1], [2]...) var container = document.getElementById( "mw-content-text" ); var citations = container.getElementsByClassName( "reference" ); if ( citations.length === 0 ) return; var tooltip; // dynamic tooltip var animation; // animationFrame queue /** * init * @description initialize the tooltip */ void function init() { // create tooltip element and append it to body element tooltip = document.createElement( "div" ); tooltip.id = "liveReference"; document.body.appendChild( tooltip ); // delegate mouse events to "#mw-content-text" [ "mouseover", "mouseout" ].forEach( function( e ) { container.addEventListener( e, function( event ) { checkEvent( event ); }); }); }(); /** * checkTarget * @description check detected mouse event and call tooltip functions if necessary * @param{Object} detected mouse event object */ function checkEvent( event ) { var target = event.target; if ( target.tagName != "A" ) return; if ( !target.parentNode.classList.contains( "reference" ) ) return; if ( event.type == "mouseover" ) { setTooltip( target ); } else { resetTooltip(); } } /** * setTooltip * @description set content and position of the tooltip * @param{HTMLElement} the citation link element which user has mouse-overed */ function setTooltip( trigger ) { // replace() escapes all dots within the anchor's hash // preventing error when <ref/>'s name attribute has unicode string as its value var sourceHash = trigger.hash.replace( /\./g, "\\." ); var sourceEl = document.querySelector( sourceHash + " .reference-text" ); var scroll = { top: document.body.scrollTop || document.documentElement.scrollTop, left: document.body.scrollLeft || document.documentElement.scrollLeft }; var rect = trigger.getBoundingClientRect(); var isInViewport = ( rect.top - tooltip.offsetHeight ) > 100; // position the tooltip tooltip.style.top = ( scroll.top + rect.top ) + "px"; tooltip.style.left = ( scroll.left + rect.left ) + "px"; // copy content from target element to the tooltip tooltip.innerHTML = sourceEl.innerHTML; // flip the tooltip if it is going off the viewport if ( !isInViewport ) tooltip.classList.add( "is-flipped" ); // fade in the tooltip fadeIn( tooltip ); } /** * resetTooltip * @description reset content and position of the tooltip */ function resetTooltip() { fadeOut( tooltip, function() { // reset flip tooltip.className = ""; // remove content while ( tooltip.lastChild ) { tooltip.removeChild( tooltip.lastChild ); } }); } /** * fadeIn * @description fade in the tooltip * @param{HTMLElement} element to fade in */ function fadeIn( el ) { var opacity = 0; var interval = 1 / 8; el.style.opacity = opacity; el.style.visibility = "visible"; function animate() { opacity += interval; if ( opacity < 1 ) { el.style.opacity = opacity; animation = window.requestAnimationFrame( animate ); } else { el.style.opacity = 1; return true; } } stopAnimation(); animate(); } /** * fadeOut * @description fade out the tooltip * @param{HTMLElement} element to fade out * @param{Function} function to execute when fade animation completes */ function fadeOut( el, callback ) { var opacity = 1; var interval = 1 / 8; function animate() { opacity -= interval; if ( opacity > 0 ) { el.style.opacity = opacity; animation = window.requestAnimationFrame( animate ); } else { el.style.opacity = 0; el.removeAttribute( "style" ); if ( typeof callback == "function" ) callback(); return true; } } stopAnimation(); animate(); } /** * stopAnimation * @description clear animationFrame queue */ function stopAnimation() { if ( !animation ) return; window.cancelAnimationFrame( animation ); animation = undefined; } }( window, document, window.mediaWiki ); /* 네타바레 감추기 ----------------------------------------*/ $(function(){ // {{네타 시작}} & {{네타 끝}} $('span.netabare').on('click', function() { $(this).toggleClass('show'); }); // {{네타}} var $netabox = $('blockquote.netabare'); if ( $netabox.length == 0 || mw.config.get("wgNamespaceNumber") == 10 ) return; $netabox .each(function(){ var $self = $(this); // {{네타}} // 다음 단락까지 모든 내용을 숨김 $self.nextAll().each(function() { var nexts = this.tagName; var stop = ['H2', 'H3', 'H4', 'H5']; if ( $.inArray( nexts, stop ) !== -1 ) { return false; // stop function } $(this).addClass('invisible').css('opacity', '0'); // censor spoiler }); // {{네타}}를 클릭하면 해당 내용이 나타남 $self.on('click', function(){ $(this).nextUntil($netabox).removeClass('invisible').animate({ opacity: 1 }); $(this).addClass('expired'); }); }); }); /* 경고 틀 * Taken from http://dev.wikia.com/wiki/SpoilerAlert by Pecoes * Customized version ----------------------------------------*/ $(function(){ window.Censor = (function(){ // Get current page's Id var wgArticleId = (window.mediaWiki && window.mediaWiki.config && window.mediaWiki.config.get('wgArticleId')) || window.wgArticleId; // Use LocalStorage var ids = localStorage.getItem('censorJS'+wgArticleId); // Censoring action if ( $('.censor').length && ids != 'valid' ) { // Check content type and switch alert message if ( $('#netabare').length && $('#mature').length ) { var messageType = '이 문서는 네타바레 & 성인용 컨텐츠를 포함하고 있습니다.<br />귀하는 성인이며, 네타바레를 읽으시겠습니까?'; } else if ( $('#netabare').length ) { var messageType = '이 문서는 네타바레를 포함하고 있습니다. 문서를 읽으시겠습니까?'; } else if ( $('#mature').length ) { var messageType = '이 문서는 성인용 컨텐츠를 포함하고 있습니다. 귀하는 성인입니까?'; }; var pageName = wgPageName.split("_").join(" "); var dialog = '<div id="dialog" class="center">' + '<div class="head">' + pageName + '</div>' + '<div class="body">' + messageType + '<br>(취소를 누르면 이전 페이지로 이동합니다.)</div>' + '<div class="foot">' + '<input type="button" id="yes" value="네" />' + '<input type="button" id="no" value="아니오"/>' + '</div></div>'; var article = $('div.content'); var dialogHeight; $('<div id="overlay">' + dialog + '</div>').appendTo(article).css({ position: 'absolute', top: 0, left: 0, right: 0, bottom: 0, backgroundColor: 'rgba(255,255,255,0.9)', minHeight: (dialogHeight = $('#dialog').outerHeight()) }); $('#yes').click(function () { $('#dialog').remove(); $('#overlay').fadeOut(1600, function () { $(this).remove(); }); localStorage.setItem('censorJS'+wgArticleId, 'valid'); }); $('#no').click(function () { $('#dialog').remove(); if (history.length) { history.back(); } else { location.href = location.protocol + '//' + location.host; } }); } }) (window.Censor); }); /* * leanModal * @Author: Ray Stone, http://finelysliced.com.au * @Customization: 카페인러브 * * lazyload fix by 페네트- */ (function($) { $.fn.extend({ openModal: function(options) { var defaults = { type: null, selector: '#modal' }; options = $.extend(defaults, options); return this.each(function() { var o = options; $(this).click(function(e) { $(o.selector).click(function(e) { e.stopPropagation(); }); $('div.modal-container, span.modal-close').click(function() { closeModal(o.selector, o.type); }); $(document).bind('keyup', function(e) { if (e.keyCode == 27) closeModal(o.selector, o.type); }); if (o.type == 'info') { initContent(o.selector, $(this)); } $(o.selector).parent().css('display', 'block'); if (o.type !== 'filter') { adjustModal(o.selector); $(window).resize(function() { adjustModal(o.selector); }); } // lazyload fix $(o.selector).find('img').lazyload().trigger('appear'); $('div.modal-overlay').css({ 'display': 'block', 'opacity': 0 }).fadeTo(200, 0.8); $(o.selector).fadeTo(200, 1); $('body').addClass('modal-open'); e.preventDefault(); }); }); function adjustModal(modal) { var height = $(modal).outerHeight(), windowHeight = $(window).height(); $(modal).css('margin-top', (windowHeight - height) / 2); // lazyload fix // 이상하게 :visible이 아니라고 뜨므로 강제로 trigger함 $(modal).find('img').lazyload().trigger('appear'); } function closeModal(modal, type) { $('div.modal-overlay').fadeOut(200); $(modal).fadeOut(200).parent().css('display', 'none'); if (type == 'info') { $(modal).find('.detail').remove(); } $('body').removeClass('modal-open'); } function initContent(modal, $this) { $this.children('div.detail').clone().appendTo(modal); } } }); })(jQuery); // Toggle by Categories function toggleCategories(trigger, target) { if ( trigger.hasClass('checked') ) { trigger.removeClass('checked'); target.hide(); } else { trigger.addClass('checked'); target.show(); } } function assembleDetails(source, table) { var len = source.length, rows = ''; source.each(function(i){ var c = $(this).html(); rows += ( i==0 || i%2==0 ? '<tr><td>' : '<td>' ) + '<span class="toggle bright">★</span>' + c + ( i%2==0 && i!==len-1 ? '</td>' : '</td></tr>' ); }); table.html(rows); } function mapSelected() { var targets = $('#whole-season td:not(.dim)'), length = targets.length, text = ''; for (var i = 0; i < length; i++) { if (i > 0) { text += ', ' }; text += $(targets[i]).find('.title-kr a').text(); } $('#selected-items-list').val(text); $('#selected-items-length').text(length); } // Common variables window.$games = { otome : $('div.item.otome'), bl : $('div.item.bl'), all_ages : $('div.item.all-ages'), mature : $('div.item.mature') }; $(window).load(function() { // 애니 방영표 if ($('#anitable').length) { // 현재 요일을 하이라이트 $('#day' + (new Date()).getDay().toString()).addClass('current'); // 섬네일 추가 (anitableCurrentDay에서 옮겨옴) if ($.fn.lazyload) $('img').lazyload(); // 작품을 클릭하면 상세정보를 띄움 $('#anitable div.item').openModal({ type: 'info', selector: '#individual' }); // 신작정보만 모아서 보기 $('#filter-new a').one('click', function() { assembleDetails($('#anitable div.item.new div.detail'), $('#whole-season')); // 활성 작품 리스트를 출력하는 부분 $('#selected-items').prepend('<textarea id="selected-items-list" />'); $('#selected-items').on('click', function(e){ e.stopPropagation(); }); mapSelected(); }); $('#filter-new a').openModal({ type: 'filter', selector: '#whole-season' }); // 신작정보를 클릭하면 토글 + 토글 상태에 따라서 활성 작품의 리스트를 출력 $('#whole-season').on('click', 'span.bright', function() { $(this).removeClass('bright').addClass('dim'); $(this).parent().addClass('dim'); mapSelected(); }); $('#whole-season').on('click', 'span.dim', function() { $(this).removeClass('dim').addClass('bright'); $(this).parent().removeClass('dim'); mapSelected(); }); } // 게임 발매표 if ($('#gametable').length) { // 현재 월을 하이라이트 var month = ((new Date()).getMonth()); $('#gametable div.month').eq(month).addClass('current'); // 카테고리의 크기 표시 $('#filter-otome span.num') .text($games.otome.length); $('#filter-bl span.num') .text($games.bl.length); $('#filter-all-ages span.num') .text($games.all_ages.length); $('#filter-mature span.num') .text($games.mature.length); // 카테고리를 클릭하면 장르별 토글 $('#filter-otome').click(function(){ toggleCategories($(this), $games.otome); }); $('#filter-bl').click(function(){ toggleCategories($(this), $games.bl); }); $('#filter-all-ages').click(function(){ toggleCategories($(this), $games.all_ages); }); $('#filter-mature').click(function(){ toggleCategories($(this), $games.mature); }); // 작품을 클릭하면 상세정보를 띄움 $('#gametable div.item').openModal({ type: 'info', selector: '#individual' }); // 연간 정보 모아서 보기 assembleDetails($('#gametable div.detail'), $('#whole-year')); $('#view-all a').openModal({ type: 'filter', selector: '#whole-year' }); // 월별 정보 보기 $('#gametable span.more').click(function() { var source = $(this).parent().siblings('div.item').children('div.detail'); assembleDetails(source, $('#by-month')); }); $('#gametable span.more').openModal({ type: 'filter', selector: '#by-month' }); } });