미디어위키:Common.js: 두 판 사이의 차이

편집 요약 없음
태그: 되돌려진 기여
편집 요약 없음
 
(같은 사용자의 중간 판 48개는 보이지 않습니다)
1번째 줄: 1번째 줄:
/**
* 이 스크립트는 위키백과 전체에 적용됩니다. 고칠 때는 주의해주세요.
* [[위키백과:위키프로젝트 시스템]] 참고
*
* 스크립트를 넣을 때는 충분한 설명, 출처를 넣어주세요! 이후 관리가 어려워집니다.
**//* 이 자바스크립트 설정은 리버티 스킨을 사용하는 사용자에게 적용됩니다 */
mw.loader.using(['mediawiki.util', 'jquery'], function () {
function buildQuickLinks($wrapper) {
if (!$wrapper.length || $wrapper.data('quicklinks-initialized')) return;
$wrapper.data('quicklinks-initialized', true);
var links = [
{
label: '전체',
href: 'https://w.halv.kr/%ED%8A%B9%EC%88%98:%EC%B5%9C%EA%B7%BC%EB%B0%94%EB%80%9C?limit=800&days=7'
},
{
label: '주시',
href: 'https://w.halv.kr/%ED%8A%B9%EC%88%98:%EC%B5%9C%EA%B7%BC%EB%B0%94%EB%80%9C?watchlist=watched&limit=1000&days=7'
},
{
label: '새 문서',
href: 'https://w.halv.kr/%ED%8A%B9%EC%88%98:%EC%B5%9C%EA%B7%BC%EB%B0%94%EB%80%9C?hidepageedits=1&hidelog=1&hidenewuserlog=1&limit=1000&days=7'
},
{
label: '되돌림',
href: 'https://w.halv.kr/%ED%8A%B9%EC%88%98:%EC%B5%9C%EA%B7%BC%EB%B0%94%EB%80%9C?tagfilter=mw-rollback%7Cmw-undo&limit=1000&days=7'
}
];
var currentUrl = location.href;
var $container = $('<div>', { class: 'rc-quick-links' });
var $buttons = $('<div>', { class: 'rc-quick-links__buttons' });
links.forEach(function (link) {
var $a = $('<a>', {
class: 'rc-quick-links__btn',
text: link.label,
href: link.href
});
if (currentUrl.indexOf(link.href) === 0) {
$a.addClass('rc-quick-links__btn--active');
}
$buttons.append($a);
});
$container.append($buttons);
$wrapper.empty().append($container);
// CSS
mw.util.addCSS(`
.rc-quick-links {
display: flex;
flex-direction: column;
gap: .5rem;
padding: .75rem 1rem;
border: none;
border-radius: 6px;
background-color: var(--bg);
}
.rc-quick-links__buttons {
display: flex;
  flex-direction: column;
  gap: .5rem;
  padding: .75rem 1rem;
  border: none;
  border-radius: 6px;
  background-color: var(--bg);
  margin-left: -13px;
}
.rc-quick-links__btn {
padding: .25rem .6rem;
border-radius: 999px;
border: 1px solid var(--grey);
background: var(--bg);
font-size: 1rem;
text-decoration: none;
color: var(--text);
transition: .15s;
}
.rc-quick-links__btn:hover {
background: var(--altbg)
}
.rc-quick-links__btn--active {
      font-weight: 600;
}
`);
}
// RC UI가 동적이라 wikipage.content 후에도 여러 번 변형될 수 있으므로 hook에서 실행
mw.hook('wikipage.content').add(function () {
var $wrapper = $('.mw-rcfilters-head');
if ($wrapper.length) buildQuickLinks($wrapper);
});
});
(function () {
  const TEMPLATE_SOURCE = `
<tbody>
<tr class="mw-htmlform-field-UploadSourceField">
  <td class="mw-label">
    <label for="wpUploadFile">
      <span class="upload-badge">파일명</span>
    </label>
  </td>
  <td class="mw-input">
    <input id="wpUploadFile" name="wpUploadFile" size="60" type="file">
  </td>
</tr>
<tr>
  <td class="htmlform-tip" style="width: 129px;">
    <span class="upload-badge">최대 크기</span>
  </td>
  <td>2 MB</td>
</tr>
<tr class="mw-htmlform-field-HTMLInfoField">
  <td class="mw-label">
    <span class="upload-badge">형식</span>
  </td>
  <td>png, gif, jpg, jpeg, webp, svg, ico</td>
</tr>
</tbody>
`;
  const TEMPLATE_DESC = `
<tbody>
<tr class="mw-htmlform-field-HTMLTextField">
  <td class="mw-label">
    <label for="wpDestFile">
      <span class="upload-badge">파일명</span>
    </label>
  </td>
  <td class="mw-input">
    <input id="wpDestFile" name="wpDestFile" size="60">
  </td>
</tr>
<tr class="mw-htmlform-field-HTMLTextAreaField">
  <td class="mw-label">
    <label for="wpUploadDescription">
      <span class="upload-badge">요약</span>
    </label>
  </td>
  <td class="mw-input">
    <textarea id="wpUploadDescription" cols="80" rows="8" name="wpUploadDescription"></textarea>
  </td>
</tr>
<tr>
  <td></td>
  <td class="mw-input">
    <div class="mw-editTools" tabindex="0"></div>
  </td>
</tr>
<tr class="mw-htmlform-field-Licenses">
  <td class="mw-label">
    <label for="wpLicense">
      <span class="upload-badge">라이선스</span>
    </label>
  </td>
  <td class="mw-input">
    <select name="wpLicense" id="wpLicense">
      <option value="" selected="">선택하지 않음</option>
    </select>
  </td>
</tr>
<tr><td></td><td id="mw-license-preview"></td></tr>
<tr><td id="wpDestFile-warning" colspan="2"></td></tr>
</tbody>
`;
  function replaceTables() {
    let replaced = false;
    const sourceTable = document.querySelector("table#mw-htmlform-source");
    if (sourceTable) {
      sourceTable.innerHTML = TEMPLATE_SOURCE;
      replaced = true;
    }
    const descTable = document.querySelector("table#mw-htmlform-description");
    if (descTable) {
      descTable.innerHTML = TEMPLATE_DESC;
      replaced = true;
    }
    return replaced;
  }
  // Run once on DOM ready
  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", replaceTables);
  } else {
    replaceTables();
  }
  // Mutation observer for late-loaded content
  const observer = new MutationObserver(() => {
    if (replaceTables()) {
      observer.disconnect();
    }
  });
  observer.observe(document.documentElement, {
    childList: true,
    subtree: true
  });
})();
mw.loader.using('mediawiki.util', function () {
mw.loader.using('mediawiki.util', function () {
   $(function () {
   $(function () {
     // Check if the #timetable-indicator span exists
     // Check if the #timetable-indicator span exists
     if (document.getElementById('timetable-indicator')) {
     if (document.getElementById('timetable-indicator')) {
 
  const logo = document.querySelector('img.vh-logo');
    logo.src = 'https://w.halv.kr/images/2/27/시각표로고.svg';
       // Create a <style> element
       // Create a <style> element
       var style = document.createElement('style');
       var style = document.createElement('style');
       style.textContent = `
       style.textContent = `
        .Liberty .nav-wrapper .navbar .navbar-brand {
img.vh-logo {
          background: transparent
height: 2rem !important;
                    url(/images/2/27/%EC%8B%9C%EA%B0%81%ED%91%9C%EB%A1%9C%EA%B3%A0.svg)
}
                    no-repeat left center / auto 1.6em !important;
        }


.mw-page-title-namespace {
.mw-page-title-namespace::after {
   box-shadow: inset 0 -0.5rem 0 #ef413750;
   background-color: #ef4137;
}
}


.Liberty .nav-wrapper, .Liberty .content-wrapper .liberty-sidebar .live-recent-wrapper .live-recent .live-recent-footer .label:hover, .Liberty .nav-wrapper .navbar .navbar-nav .nav-item .nav-link:hover, .Liberty .nav-wrapper .navbar .navbar-nav .nav-item .nav-link:focus, .dropdown-menu .dropdown-item:hover {
:root {
  background-color: #ef4137;
--vh-navbar-bg: #ef4137 !important;
}
}
        .Liberty .nav-wrapper .navbar .form-inline .btn:hover,
 
        .Liberty .nav-wrapper .navbar .form-inline .btn:focus,
  `;
        .Liberty .content-wrapper .liberty-sidebar
          .live-recent-wrapper .live-recent
          .live-recent-header .nav .nav-item .nav-link.active::before,
        .Liberty .content-wrapper .liberty-sidebar
          .live-recent-wrapper .live-recent
          .live-recent-header .nav .nav-item .nav-link:hover::before,
        .Liberty .content-wrapper .liberty-sidebar
          .live-recent-wrapper .live-recent
          .live-recent-header .nav .nav-item .nav-link:focus::before,
        .Liberty .content-wrapper .liberty-sidebar
          .live-recent-wrapper .live-recent
          .live-recent-header .nav .nav-item .nav-link:active::before,
        .Liberty .content-wrapper .liberty-sidebar
          .live-recent-wrapper .live-recent
          .live-recent-footer .label,
        .Liberty .content-wrapper .liberty-content
          .liberty-content-header .content-tools .tools-btn:hover,
        .Liberty .content-wrapper .liberty-content
          .liberty-content-header .content-tools .tools-btn:focus,
        .Liberty .content-wrapper .liberty-content
          .liberty-content-header .content-tools .tools-btn:active {
          background-color: #ef4137 !important;
        }
      `;
       document.head.appendChild(style);
       document.head.appendChild(style);
     }
     }
335번째 줄: 91번째 줄:
})();
})();


mw.loader.using( ['mediawiki.util', 'mediawiki.notify', 'jquery.client'], function () {
/* Begin of mw.loader.using callback */
/**
* Map addPortletLink to mw.util
*
* @deprecated: Use mw.util.addPortletLink instead.
*/
mw.log.deprecate( window, 'addPortletLink', mw.util.addPortletLink,
'Use mw.util.addPortletLink instead' );
/**
* Extract a URL parameter from the current URL
*
* @deprecated: Use mw.util.getParamValue with proper escaping
*/
mw.log.deprecate( window, 'getURLParamValue', mw.util.getParamValue, 'Use mw.util.getParamValue instead' );
/**
* Test if an element has a certain class
*
* @deprecated:  Use $(element).hasClass() instead.
*/
mw.log.deprecate( window, 'hasClass', function ( element, className ) {
    return $( element ).hasClass( className );
}, 'Use jQuery.hasClass() instead' );
/**
* @source www.mediawiki.org/wiki/Snippets/Load_JS_and_CSS_by_URL
* @rev 5
*/
// CSS
var extraCSS = mw.util.getParamValue( 'withCSS' );
if ( extraCSS ) {
if ( extraCSS.match( /^MediaWiki:[^&<>=%#]*\.css$/ ) ) {
importStylesheet( extraCSS );
} else {
mw.notify( 'Only pages from the MediaWiki namespace are allowed.', { title: 'Invalid withCSS value' } );
}
}
// JS
var extraJS = mw.util.getParamValue( 'withJS' );
if ( extraJS ) {
if ( extraJS.match( /^MediaWiki:[^&<>=%#]*\.js$/ ) ) {
importScript( extraJS );
} else {
mw.notify( 'Only pages from the MediaWiki namespace are allowed.', { title: 'Invalid withJS value' } );
}
}
/**
* Import more specific scripts if necessary
*/
if ( mw.config.get( 'wgNamespaceNumber' ) === 6 ) {
    /* file description page scripts */
    importScript( 'MediaWiki:Common.js/file.js' );
}
/* ([[위키백과:관리자 요청/2007년 5월#스크립트 추가 요청]]) */
/** Collapsible tables *********************************************************
*
*  Description: Allows tables to be collapsed, showing only the header. See
*              [[:en:Wikipedia:NavFrame]].
*  Maintainers: [[:en:User:R. Koot]]
*/
var autoCollapse = 2;
var collapseCaption = '숨기기';
var expandCaption = '보이기';
window.collapseTable = function ( tableIndex ) {
    var Button = document.getElementById( 'collapseButton' + tableIndex );
    var Table = document.getElementById( 'collapsibleTable' + tableIndex );
    if ( !Table || !Button ) {
        return false;
    }
    var Rows = Table.rows;
    var i;
    if ( Button.firstChild.data === collapseCaption ) {
        for ( i = 1; i < Rows.length; i++ ) {
            Rows[i].style.display = 'none';
        }
        Button.firstChild.data = expandCaption;
    } else {
        for ( i = 1; i < Rows.length; i++ ) {
            Rows[i].style.display = Rows[0].style.display;
        }
        Button.firstChild.data = collapseCaption;
    }
};
function createCollapseButtons() {
    var tableIndex = 0;
    var NavigationBoxes = {};
    var Tables = document.getElementsByTagName( 'table' );
    var i;
    function handleButtonLink( index, e ) {
        window.collapseTable( index );
        e.preventDefault();
    }
    for ( i = 0; i < Tables.length; i++ ) {
        if ( $( Tables[i] ).hasClass( 'collapsible' ) ) {
            /* only add button and increment count if there is a header row to work with */
            var HeaderRow = Tables[i].getElementsByTagName( 'tr' )[0];
            if ( !HeaderRow ) continue;
            var Header = HeaderRow.getElementsByTagName( 'th' )[0];
            if ( !Header ) continue;
            NavigationBoxes[ tableIndex ] = Tables[i];
            Tables[i].setAttribute( 'id', 'collapsibleTable' + tableIndex );
            var Button    = document.createElement( 'span' );
            var ButtonLink = document.createElement( 'a' );
            var ButtonText = document.createTextNode( collapseCaption );
            Button.className = 'collapseButton';  /* Styles are declared in Common.css */
            ButtonLink.style.color = Header.style.color;
            ButtonLink.setAttribute( 'id', 'collapseButton' + tableIndex );
            ButtonLink.setAttribute( 'href', '#' );
            $( ButtonLink ).on( 'click', $.proxy( handleButtonLink, ButtonLink, tableIndex ) );
            ButtonLink.appendChild( ButtonText );
            Button.appendChild( document.createTextNode( '[' ) );
            Button.appendChild( ButtonLink );
            Button.appendChild( document.createTextNode( ']' ) );
            Header.insertBefore( Button, Header.firstChild );
            tableIndex++;
        }
    }
    for ( i = 0;  i < tableIndex; i++ ) {
        if ( $( NavigationBoxes[i] ).hasClass( 'collapsed' ) || ( tableIndex >= autoCollapse && $( NavigationBoxes[i] ).hasClass( 'autocollapse' ) ) ) {
            window.collapseTable( i );
        }
        else if ( $( NavigationBoxes[i] ).hasClass ( 'innercollapse' ) ) {
            var element = NavigationBoxes[i];
            while ((element = element.parentNode)) {
                if ( $( element ).hasClass( 'outercollapse' ) ) {
                    window.collapseTable ( i );
                    break;
                }
            }
        }
    }
}
mw.hook( 'wikipage.content' ).add( createCollapseButtons );
/* ([[위키백과:관리자 요청/2007년 5월#스크립트 추가 요청]]) */
/** Dynamic Navigation Bars (experimental) *************************************
*
*  Description: See [[:en:Wikipedia:NavFrame]].
*  Maintainers: UNMAINTAINED
*/
// set up the words in your language
/* set up the words in your language */
var NavigationBarHide = '[' + collapseCaption + ']';
var NavigationBarShow = '[' + expandCaption + ']';
/**
* Shows and hides content and picture (if available) of navigation bars
* Parameters:
*    indexNavigationBar: the index of navigation bar to be toggled
**/
window.toggleNavigationBar = function ( indexNavigationBar, event ) {
    var NavToggle = document.getElementById( 'NavToggle' + indexNavigationBar );
    var NavFrame = document.getElementById( 'NavFrame' + indexNavigationBar );
    var NavChild;
    if ( !NavFrame || !NavToggle ) {
        return false;
    }
    /* if shown now */
    if ( NavToggle.firstChild.data === NavigationBarHide ) {
        for ( NavChild = NavFrame.firstChild; NavChild !== null; NavChild = NavChild.nextSibling ) {
            if ( $( NavChild ).hasClass( 'NavContent' ) || $( NavChild ).hasClass( 'NavPic' ) ) {
                NavChild.style.display = 'none';
            }
        }
    NavToggle.firstChild.data = NavigationBarShow;
    /* if hidden now */
    } else if ( NavToggle.firstChild.data === NavigationBarShow ) {
        for ( NavChild = NavFrame.firstChild; NavChild !== null; NavChild = NavChild.nextSibling ) {
            if ( $( NavChild ).hasClass( 'NavContent' ) || $( NavChild ).hasClass( 'NavPic' ) ) {
                NavChild.style.display = 'block';
            }
        }
        NavToggle.firstChild.data = NavigationBarHide;
    }
    event.preventDefault();
};
/* adds show/hide-button to navigation bars */
function createNavigationBarToggleButton() {
    var indexNavigationBar = 0;
    var NavFrame;
    var NavChild;
    /* iterate over all < div >-elements */
    var divs = document.getElementsByTagName( 'div' );
    for ( var i = 0; (NavFrame = divs[i]); i++ ) {
        /* if found a navigation bar */
        if ( $( NavFrame ).hasClass( 'NavFrame' ) ) {
            indexNavigationBar++;
            var NavToggle = document.createElement( 'a' );
            NavToggle.className = 'NavToggle';
            NavToggle.setAttribute( 'id', 'NavToggle' + indexNavigationBar );
            NavToggle.setAttribute( 'href', '#' );
            $( NavToggle ).on( 'click', $.proxy( window.toggleNavigationBar, window, indexNavigationBar ) );
            var isCollapsed = $( NavFrame ).hasClass( 'collapsed' );
            /**
            * Check if any children are already hidden.  This loop is here for backwards compatibility:
            * the old way of making NavFrames start out collapsed was to manually add style="display:none"
            * to all the NavPic/NavContent elements.  Since this was bad for accessibility (no way to make
            * the content visible without JavaScript support), the new recommended way is to add the class
            * "collapsed" to the NavFrame itself, just like with collapsible tables.
            */
            for ( NavChild = NavFrame.firstChild; NavChild != null && !isCollapsed; NavChild = NavChild.nextSibling ) {
                if ( $( NavChild ).hasClass( 'NavPic' ) || $( NavChild ).hasClass( 'NavContent' ) ) {
                    if ( NavChild.style.display === 'none' ) {
                        isCollapsed = true;
                    }
                }
            }
            if ( isCollapsed ) {
                for ( NavChild = NavFrame.firstChild; NavChild != null; NavChild = NavChild.nextSibling ) {
                    if ( $( NavChild ).hasClass( 'NavPic' ) || $( NavChild ).hasClass( 'NavContent' ) ) {
                        NavChild.style.display = 'none';
                    }
                }
            }
            var NavToggleText = document.createTextNode( isCollapsed ? NavigationBarShow : NavigationBarHide );
            NavToggle.appendChild( NavToggleText );
            /* Find the NavHead and attach the toggle link (Must be this complicated because Moz's firstChild handling is borked) */
            for( var j = 0; j < NavFrame.childNodes.length; j++ ) {
                if ( $( NavFrame.childNodes[j] ).hasClass( 'NavHead' ) ) {
                    NavToggle.style.color = NavFrame.childNodes[j].style.color;
                    NavFrame.childNodes[j].appendChild( NavToggle );
                }
            }
            NavFrame.setAttribute( 'id', 'NavFrame' + indexNavigationBar );
        }
    }
}
mw.hook( 'wikipage.content' ).add( createNavigationBarToggleButton );
/***** 그림 정보 틀을 자동으로 불러옴 ********
* Adds a link to subpages of current page
* from commons.wikimedia.org
*  Maintainers: [[User:Yonidebest]], [[User:Dschwen]]
*  [[사용자:Kwj2772]]가 수정
*  JSconfig items: bool 'loadAutoInformationTemplate'
*                      (true=enabled (default), false=disabled)
* JSConfig를 사용하지 않도록 수정함. --[[사용자:Klutzy|klutzy]] ([[사용자토론:Klutzy|토론]]) 2009년 9월 27일 (일) 20:33 (KST)
****/
if (mw.config.get('wgCanonicalSpecialPageName') == 'Upload') {
  importScript('MediaWiki:Upload.js');
}
/* 인터랙티브 지도. 영어 위키백과에서 가져옴. -- [[사용자:ChongDae]] 2010년 3월 28일 (일) 02:08 (KST) */
/**
* WikiMiniAtlas
*
* Description: WikiMiniAtlas is a popup click and drag world map.
*              This script causes all of our coordinate links to display the WikiMiniAtlas popup button.
*              The script itself is located on meta because it is used by many projects.
*              See [[Meta:WikiMiniAtlas]] for more information.
* Maintainers: [[User:Dschwen]]
*/
( function () {
    var require_wikiminiatlas = false;
    var coord_filter = /geohack/;
    $( function () {
        $( 'a.external.text' ).each( function( key, link ) {
            if ( link.href && coord_filter.exec( link.href ) ) {
                require_wikiminiatlas = true;
                // break from loop
                return false;
            }
        } );
        if ( $( 'div.kmldata' ).length ) {
            require_wikiminiatlas = true;
        }
        if ( require_wikiminiatlas ) {
            mw.loader.load( '//meta.wikimedia.org/w/index.php?title=MediaWiki:Wikiminiatlas.js&action=raw&ctype=text/javascript' );
        }
    } );
} )();
/**
* Fix for Windows XP Unicode font rendering
*/
if ( navigator.appVersion.search(/windows nt 5/i) !== -1 ) {
    mw.util.addCSS( '.IPA { font-family: "Lucida Sans Unicode", "Arial Unicode MS"; } ' +
                '.Unicode { font-family: "Arial Unicode MS", "Lucida Sans Unicode"; } ' );
}
/**
* 편집 안내 ****************************************************
*
* 생존 인물에 대한 편집 안내. 영어 위키백과에서 가져옴 - ChongDae
* Maintainers: [[User:RockMFR]]
*/
function addEditIntro( name ) {
    $( '.mw-editsection, #ca-edit' ).find( 'a' ).each( function ( i, el ) {
        el.href = $( this ).attr( 'href' ) + '&editintro=' + name;
    } );
}
if ( mw.config.get( 'wgNamespaceNumber' ) === 0 ) {
    $( function () {
        var cats = mw.config.get('wgCategories');
        if ( !cats ) {
            return;
        }
        if ( $.inArray( '살아있는 사람', cats ) !== -1 || $.inArray( '생사 불명', cats ) !== -1 ) {
            addEditIntro( '틀:BLP_편집_안내' );
        }
    } );
}
/**
*
*
*
*/
function wikilink1() {
location.href='https://jwiki.kr/wiki/index.php/철통같은_믿음으로';
}
/* End of mw.loader.using callback */
} );
$(document).ready(function () {
    /* ---------- helper for sub-pixel scroll height ---------- */
  /* ---------- helper for sub-pixel scroll height (accurate & shift-free) ---------- */
function getExactScrollHeight(el) {
  /* 1 — snapshot the current inline width so the clone wraps exactly the same */
  const liveRect = el.getBoundingClientRect();          // fractional width!
  const liveW    = liveRect.width + "px";
  /* 2 — make a deep clone and neutralise its layout impact */
  const clone = el.cloneNode(true);
  Object.assign(clone.style, {
    position      : "absolute",  // out of normal flow
    left          : "-9999px",    // off-screen (but in the DOM)
    top          : "0",
    width        : liveW,        // lock wrapping width
    height        : "auto",
    overflow      : "visible",
    visibility    : "hidden",
    pointerEvents : "none",
    transition    : "none"
  });
  /* 3 — measure, then discard */
  el.parentNode.appendChild(clone);                    // within same ancestor → exact CSS context
  const h = clone.getBoundingClientRect().height;      // accurate, fractional OK
  clone.remove();
  return h;                                            // sub-pixel-precise
}
    /* -------------------------------------------------------- */
    $('.toggleBtn').click(function () {
        var $button = $(this);
        if ($button.data('animating')) return;
        $button.data('animating', true);
        var targetId = $button.data('target');
        var growDiv  = document.getElementById(targetId);
        var $growDiv = $('#' + targetId);
        const cs = window.getComputedStyle(growDiv);
        var oldHeight = growDiv.getBoundingClientRect().height;
        var paddingAndBorder = parseFloat(cs.paddingTop) + parseFloat(cs.paddingBottom);
        $growDiv.toggleClass('collapsed');
        if (growDiv.clientHeight) {
            /* ---------- COLLAPSE ---------- */
setTimeout(() => growDiv.style.display = "none", 300);
            growDiv.style.height = 0;
            var heightDifference = 0 - oldHeight - paddingAndBorder;
        } else {
            /* ---------- EXPAND ---------- */
growDiv.style.display = "block";
            const exactScrollHeight = getExactScrollHeight(growDiv);
            if ($growDiv.hasClass('textcollapsible')) {
                growDiv.style.height = (exactScrollHeight + 6) + "px";
            } else {
                growDiv.style.height = (exactScrollHeight + 6.5) + "px";
            }
            var heightDifference = exactScrollHeight - oldHeight - paddingAndBorder;
        }
        if ($growDiv.hasClass('textcollapsible') && !$growDiv.hasClass('collapsed')) {
            heightDifference += 6;
        } else if (!$growDiv.hasClass('textcollapsible') && !$growDiv.hasClass('collapsed')) {
            heightDifference += 6.5;
        }
        console.log(heightDifference);
        animateParentHeights(growDiv, heightDifference);
        setTimeout(() => $button.data('animating', false), 300);
    });
        function animateParentHeights(element, difference) {
        var parent = element.parentElement.closest('.collapsible');
        while (parent) {
            var intervalId = setInterval(() => {
                const p = element.parentElement.closest('.collapsible');
                p.style.height    = getExactScrollHeight(p) + 5 + 'px'; // float
            }, 30);
            setTimeout(() => {
                clearInterval(intervalId);
                const p = element.parentElement.closest('.collapsible');
                p.style.animation = '';
                p.style.height    = getExactScrollHeight(p) + 5 + 'px'; // float
            }, 310);
            parent = parent.parentElement.closest('.collapsible');
        }
    }
});


     const gemstoneColors = {
     const gemstoneColors = {
819번째 줄: 122번째 줄:


   const existing = document.querySelector('span.pjcg-hwijang');
   const existing = document.querySelector('span.pjcg-hwijang');
const appendafterthis = document.querySelector('.live-recent-content');
const appendafterthis = document.querySelector('.vh-rc');
   if (existing) {
   if (existing) {
     const newDiv = document.createElement('div');
     const newDiv = document.createElement('div');
     newDiv.className = 'live-recent-fake-content';
     newDiv.className = 'vh-sidebar-section';
     newDiv.setAttribute(
     newDiv.setAttribute(
       'style',
       'style',
829번째 줄: 132번째 줄:
       background: url(https://w.halv.kr/images/4/4c/%EC%B2%AD%EA%B4%91%ED%9C%98%EC%9E%A5.png) center no-repeat, light-dark(#fff,#1c1d1f) !important;
       background: url(https://w.halv.kr/images/4/4c/%EC%B2%AD%EA%B4%91%ED%9C%98%EC%9E%A5.png) center no-repeat, light-dark(#fff,#1c1d1f) !important;
       background-size: contain !important;
       background-size: contain !important;
       width: 21.5rem !important;
       height: 14.5rem !important;
height:16.76rem !important;
       `
       `
     );
     );
837번째 줄: 139번째 줄:
   };
   };


 
   const targetElements = document.querySelectorAll('.vh-rc');
  const list = document.querySelector('.live-recent-content');
  if (!list) return;
 
  const link = document.createElement('a');
  link.href = '/%ED%8A%B9%EC%88%98:%EC%B5%9C%EA%B7%BC%EB%B0%94%EB%80%9C';
  link.innerText = '최근 변경';
  link.style.display = 'block';
  link.style.fontSize = '20px';
  link.style.fontWeight = '500';
  link.style.marginTop = '3px';
  link.style.textDecoration = 'none';
  link.style.textAlign = 'left';
  link.style.color = 'var(--text)';
 
const link2 = document.createElement('a');
  link2.href = '/%ED%8A%B9%EC%88%98:%EC%B5%9C%EA%B7%BC%EB%B0%94%EB%80%9C';
  link2.innerText = '▶';
  link2.style.display = 'block';
  link2.style.fontSize = '14px';
  link2.style.fontWeight = '500';
  link2.style.marginTop = '3px';
  link2.style.textDecoration = 'none';
  link2.style.textAlign = 'right';
link2.style.opacity = '60%';
  link2.style.color = 'var(--text)';
 
const containeroflinks = document.createElement('div');
containeroflinks.style.display = 'flex';
containeroflinks.style.justifyContent = 'space-between';
containeroflinks.style.alignItems = 'center';
 
containeroflinks.append(link);
containeroflinks.append(link2);
 
  list.prepend(containeroflinks); // add to the top of the container
 
   const targetElements = document.querySelectorAll('.live-recent-content');
   targetElements.forEach((el) => {
   targetElements.forEach((el) => {
     el.insertAdjacentHTML(
     el.insertAdjacentHTML(
       'beforebegin',
       'beforebegin',
       `<a href="https://www.postype.com/@whalvkr/membership">
       `<a href="https://buymeacoffee.com/halv/">
         <div class="live-recent-fake-content" style="
         <div class="vh-sidebar-section" style="
           margin-bottom: 1rem;
           margin-bottom: 1rem;
          padding: 0;
  height: 90px;
           background: url(https://w.halv.kr/images/b/bd/후원처란.png) center no-repeat, light-dark(#fff,#1c1d1f) !important;
           background: url(https://w.halv.kr/images/b/bd/후원처란.png) center no-repeat, light-dark(#fff,#1c1d1f) !important;
  height: 6rem;
           background-size: contain !important;
           background-size: contain !important;
         "></div>
         "></div>
888번째 줄: 154번째 줄:
     );
     );
   });
   });


   const element = document.getElementById("honorific-level");
   const element = document.getElementById("honorific-level");
922번째 줄: 187번째 줄:
   const link3 = document.createElement('link');
   const link3 = document.createElement('link');
   link3.rel = 'stylesheet';
   link3.rel = 'stylesheet';
   link3.href = 'https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100..900&family=Zen+Kaku+Gothic+Antique:wght@300;400;500;700;900&display=swap';
   link3.href = 'https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100..900&family=Noto+Serif+KR:wght@300;400;500;700;900&family=Hahmlet:wght@300;400;500;700;900&family=Zen+Kaku+Gothic+Antique:wght@300;400;500;700;900&display=swap';


   const link4 = document.createElement('link');
   const link4 = document.createElement('link');
970번째 줄: 235번째 줄:
runAll();
runAll();
addEventListener('resize', runAll);
addEventListener('resize', runAll);
document.addEventListener("DOMContentLoaded", () => {
  document.querySelectorAll(".diff-menu").forEach(el => {
    const divider = document.createElement("div");
    divider.className = "dropdown-divider";
    el.insertAdjacentElement("afterend", divider);
  });
});
(function () {
  function addDropdownDividers(root = document) {
    const menus = root.querySelectorAll('.diff-menu');
    menus.forEach(menu => {
      const next = menu.nextElementSibling;
      // Avoid duplicates
      if (next && next.classList.contains('dropdown-divider')) {
        return;
      }
      const divider = document.createElement('div');
      divider.className = 'dropdown-divider';
      if (menu.parentNode) {
        menu.parentNode.insertBefore(divider, menu.nextSibling);
      }
    });
  }
  // Run after DOM is loaded
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', () => addDropdownDividers());
  } else {
    addDropdownDividers();
  }
  // Observe for new mutations
  const observer = new MutationObserver(mutations => {
    for (const mutation of mutations) {
      if (mutation.type === 'childList') {
        // Check in the subtree of added nodes
        mutation.addedNodes.forEach(node => {
          if (node.nodeType === Node.ELEMENT_NODE) {
            addDropdownDividers(node);
          }
        });
      }
    }
  });
  observer.observe(document.documentElement, {
    childList: true,
    subtree: true
  });
})();


// Call the function
// Call the function
updateBackgroundColor();
updateBackgroundColor();
changeTimeColor();  
changeTimeColor();
 
/* DO NOT ADD CODE BELOW THIS LINE */

2026년 1월 4일 (일) 19:25 기준 최신판

mw.loader.using('mediawiki.util', function () {
  $(function () {
    // Check if the #timetable-indicator span exists
    if (document.getElementById('timetable-indicator')) {
  
  const logo = document.querySelector('img.vh-logo');
    logo.src = 'https://w.halv.kr/images/2/27/시각표로고.svg';
      // Create a <style> element
      var style = document.createElement('style');
      style.textContent = `
img.vh-logo {
height: 2rem !important;
}

.mw-page-title-namespace::after {
  background-color: #ef4137;
}

:root {
--vh-navbar-bg: #ef4137 !important;
}

  `;
      document.head.appendChild(style);
    }
  });
});


(function () {
  function initStickyBorders(wrap) {
    const sticky = wrap.querySelector('#stickyparent');
    if (!sticky) return;

    function updateStickyWidth() {
      const w = sticky.getBoundingClientRect().width;
      wrap.style.setProperty('--sticky-w', w + 'px');
    }

    function updateScrollState() {
      const atLeft = wrap.scrollLeft < 5;
      wrap.classList.toggle('at-left', atLeft);
      wrap.classList.toggle('scrolled', !atLeft); // ← 추가: 왼쪽이 아니면 '띄우기' 상태
    }

    updateStickyWidth();
    updateScrollState();

    wrap.addEventListener('scroll', updateScrollState, { passive: true });
    window.addEventListener('resize', updateStickyWidth);
    if (document.fonts && document.fonts.ready) {
      document.fonts.ready.then(updateStickyWidth).catch(function(){});
    }
  }

  document.querySelectorAll('.table-wrap').forEach(initStickyBorders);
})();



(function () {
  function unwrapLibertyTable(wrapper) {
    if (!wrapper || !wrapper.querySelector('.timetable')) return;
    const table = wrapper.querySelector('.timetable');
    wrapper.parentNode.insertBefore(table, wrapper);
    wrapper.remove();
  }

  // Remove any that already exist
  document.querySelectorAll('.liberty-table-wrapper').forEach(unwrapLibertyTable);

  // Watch for new ones being added to the DOM
  const observer = new MutationObserver(mutations => {
    mutations.forEach(mutation => {
      mutation.addedNodes.forEach(node => {
        if (!(node instanceof HTMLElement)) return;
        if (node.matches('.liberty-table-wrapper') && node.querySelector('.timetable')) {
          unwrapLibertyTable(node);
        } else if (node.querySelector) {
          // If it's a container, check inside it
          node.querySelectorAll('.liberty-table-wrapper').forEach(unwrapLibertyTable);
        }
      });
    });
  });

  observer.observe(document.body, {
    childList: true,
    subtree: true
  });
})();


    const gemstoneColors = {
  "흙": "#4b4b4b",         // Coal - Dark gray
  "구리": "#c68346",       // Copper - Reddish-brown
  "청동": "#547865",       // Bronze - Warm brown
  "은": "#c0c0c0",         // Silver - Metallic silver
  "금": "#efbf04",         // Gold - Golden yellow
  "자수정": "#9966cc",     // Amethyst - Purple
  "터키석": "#069e80",     // Turquoise - Light blue
  "가넷": "#9b111e",       // Garnet - Deep red
  "토파즈": "#f0a641",     // Topaz - Golden orange
  "페리도트": "#81b11f",   // Peridot - Light green
  "에메랄드 I": "#39b647", // Emerald - Bright green
  "에메랄드 II": "#39b647",// Emerald - Bright green (variant)
  "에메랄드 III": "#39b647",// Emerald - Bright green (variant)
  "사파이어 I": "#0f52ba", // Sapphire - Rich blue
  "사파이어 II": "#0f52ba",// Sapphire - Rich blue (variant)
  "사파이어 III": "#0f52ba",// Sapphire - Rich blue (variant)
  "루비 I": "#e0115f",     // Ruby - Vivid red
  "루비 II": "#e0115f",    // Ruby - Vivid red (variant)
  "루비 III": "#e0115f",   // Ruby - Vivid red (variant)
  "다이아몬드 I": "#b9fcff", // Diamond - Pale blue/white
  "다이아몬드 II": "#b9fcff",// Diamond - Pale blue/white (variant)
  "다이아몬드 III": "#b9fcff"
    };


// Function to update background color
function updateBackgroundColor() {

  const existing = document.querySelector('span.pjcg-hwijang');
const appendafterthis = document.querySelector('.vh-rc');
  if (existing) {
    const newDiv = document.createElement('div');
    newDiv.className = 'vh-sidebar-section';
    newDiv.setAttribute(
      'style',
      `
      margin-top: 1rem;
      background: url(https://w.halv.kr/images/4/4c/%EC%B2%AD%EA%B4%91%ED%9C%98%EC%9E%A5.png) center no-repeat, light-dark(#fff,#1c1d1f) !important;
      background-size: contain !important;
      height: 14.5rem !important;
      `
    );

    appendafterthis.insertAdjacentElement('afterend', newDiv);
  };

  const targetElements = document.querySelectorAll('.vh-rc');
  targetElements.forEach((el) => {
    el.insertAdjacentHTML(
      'beforebegin',
      `<a href="https://buymeacoffee.com/halv/">
         <div class="vh-sidebar-section" style="
           margin-bottom: 1rem;
           padding: 0;
  height: 90px;
           background: url(https://w.halv.kr/images/b/bd/후원처란.png) center no-repeat, light-dark(#fff,#1c1d1f) !important;
           background-size: contain !important;
         "></div>
       </a>`
    );
  });

  const element = document.getElementById("honorific-level");
  const leveler = document.getElementById("points-level");
  const childElement = leveler.firstElementChild; // Access the first child of leveler
  const text = element.textContent.trim().replace(/[()]/g, ""); // Remove parentheses

  // Check if the text matches specific diamond levels and update the child element color
  if (text === "다이아몬드 I" || text === "다이아몬드 II" || text === "다이아몬드 III") {
    leveler.style.background = gemstoneColors[text] || "transparent";
    leveler.style.borderColor = "#22636652";
    if (childElement) {
      childElement.style.color = "#226366cc"; // Set the child element's text color to red
    }
  } else if (gemstoneColors[text]) {
    leveler.style.background = gemstoneColors[text];
    leveler.style.borderColor = gemstoneColors[text] + "52";
    if (childElement) {
      childElement.style.color = ""; // Reset child element's text color
    }
  } else {
    leveler.style.background = "transparent"; // Default if no match
    leveler.style.borderColor = "";
    if (childElement) {
      childElement.style.color = ""; // Reset child element's text color
    }
  }
}


  const head = document.head;

  const link3 = document.createElement('link');
  link3.rel = 'stylesheet';
  link3.href = 'https://fonts.googleapis.com/css2?family=Noto+Sans+KR:wght@100..900&family=Noto+Serif+KR:wght@300;400;500;700;900&family=Hahmlet:wght@300;400;500;700;900&family=Zen+Kaku+Gothic+Antique:wght@300;400;500;700;900&display=swap';

  const link4 = document.createElement('link');
  link4.rel = 'stylesheet';
  link4.href = 'https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/static/pretendard.min.css';


  head.appendChild(link3);
head.appendChild(link4);

function stretchText(el) {
  // 1) target width in px from data-max-width (in rem)
  const maxWidth =
    parseFloat(getComputedStyle(document.documentElement).fontSize) *
    parseFloat(el.dataset.maxWidth);

  // 2) get the child <span> in a Safari-safe way
  const span = (el.querySelector && (el.querySelector(':scope > span') || el.querySelector('span'))) || null;
  if (!span) return;

  // 3) ensure predictable measuring
  const prev = el.style.transform;
  el.style.transform = 'translateX(-50%)'; // remove previous scale while measuring

  const measure = () => {
    // If fonts aren't ready yet, Safari may report 0
    const w = span.scrollWidth || span.getBoundingClientRect().width;
    if (!w) return requestAnimationFrame(measure);

    const scale = maxWidth / w;
    el.style.transformOrigin = 'center center';
    el.style.transform = `translateX(-50%) scaleX(${scale})`;
    el.style.position = 'absolute';
    el.style.position = 'absolute';
  };

  // Wait for fonts/layout in Safari
  if (document.fonts && document.fonts.ready) {
    document.fonts.ready.then(() => requestAnimationFrame(measure));
  } else {
    requestAnimationFrame(measure);
  }
}

// run once and on resize
const runAll = () => document.querySelectorAll('.stretch-text').forEach(stretchText);
runAll();
addEventListener('resize', runAll);

// Call the function
updateBackgroundColor();
changeTimeColor();