미디어위키:Gadget-dictionary.js: 두 판 사이의 차이

편집 요약 없음
태그: 수동 되돌리기
편집 요약 없음
1번째 줄: 1번째 줄:
/* ▣ Dictionary “검색 전용” 가젯 ▣
/* ▣ Dictionary “검색 전용” 가젯 ▣
   - 페이지에 .dictionary-container 가 있을 때만 동작
   위키문법을 서버 파서로 HTML 변환 후 출력
  - 표 없이: 검색 → 결과 카드 or “없음” 메시지
-------------------------------------------------- */
-------------------------------------------------- */
mw.loader.using(
mw.loader.using(
   ['oojs-ui-core', 'oojs-ui.styles.icons-interactions'],
   ['oojs-ui-core', 'oojs-ui.styles.icons-interactions', 'mediawiki.api'],
   function () {
   function () {


     /* 사전 JSON 로드 */
     /* ─ 1. JSON → {소문자 단어: 정의(wikitext)} ─ */
     var jsonNode = document.getElementById('dictionary-json');
     var jsonNode = document.getElementById('dictionary-json');
     if (!jsonNode) return;                       // 사전 없는 페이지
     if (!jsonNode) return;
     var dictRaw = JSON.parse(jsonNode.textContent);
     var raw = JSON.parse(jsonNode.textContent);
     var dict     = {};                           // 소문자 키 → 정의
     var dict = {};
     Object.keys(dictRaw).forEach(function (k) {
     Object.keys(raw).forEach(k => dict[k.toLowerCase()] = raw[k]);
      dict[k.toLowerCase()] = dictRaw[k];
    });


     /* 한 번만 카드/없음 CSS 삽입 */
     /* ─ 2. 위키텍스트 → HTML 변환용 함수 (캐시 포함) ─ */
    const api  = new mw.Api();
    const cache = Object.create(null);      // wt → html
 
    function parseWT(wt) {
      if (cache[wt]) return $.Deferred().resolve(cache[wt]).promise();
      return api.get({
        action: 'parse',
        format: 'json',
        prop:  'text',
        text:  wt,
        contentmodel: 'wikitext',
        disablelimitreport: 1
      }).then(data => {
        const html = data?.parse?.text['*'] || mw.html.escape(wt);
        cache[wt] = html;
        return html;
      }, () => mw.html.escape(wt));        // 오류 시 그대로 출력
    }
 
    /* ─ 3. 한 번만 결과·버튼 CSS 삽입 */
     if (!document.getElementById('dict-card-style')) {
     if (!document.getElementById('dict-card-style')) {
       mw.util.addCSS(`
       mw.util.addCSS(`
.oo-ui-inputWidget-input, .oo-ui-buttonElement-button {
.oo-ui-inputWidget-input, .oo-ui-buttonElement-button{
background: light-dark(#fff, rgb(30,30,30)) !important;
  background:light-dark(#fff,rgb(30,30,30));color:var(--text);
color: var(--text) !important;
  border:1px solid light-dark(#ccc,#555);height:36px}
border: 1px solid light-dark(#ccc, #555) !important;
.oo-ui-inputWidget-input{border-radius:5px 0 0 5px}
height: 36px !important;
.oo-ui-buttonElement-button{border-radius:0 5px 5px 0;padding:6px 8px}
}
.dict-card{border:1px solid light-dark(#ccc,#555);border-radius:8px;
.oo-ui-inputWidget-input {
  background:light-dark(#f9f9f9,hsl(200,5%,17%));margin:8px 0;padding:12px 16px}
border-radius: 5px 0 0 5px !important;
.dict-none{padding:8px;color:light-dark(#d33,hsl(0,71%,75%))}
}
.oo-ui-buttonElement-button {
border-radius: 0 5px 5px 0 !important;
padding-top: 6px;
padding-bottom: 6px;
}
        .dict-card   {padding:0px 16px 8px; border:1px solid light-dark(#ccc, #555);border-radius:8px;
                      background:light-dark(#f9f9f9, HSL(200, 5%, 17%)); padding-top: }
        .dict-card .term{font-weight:600;font-size:1.2em;margin-right:.4em; padding:8px; padding-bottom: 3px;}
.dict-card .def{padding-left:8px; padding-right:8px; padding-bottom: 8px;}
        .dict-none   {padding:8px;color:light-dark(#d33, HSL(0, 71%, 75%));}
       `).id = 'dict-card-style';
       `).id = 'dict-card-style';
     }
     }


     /* 콘텐츠 훅: 컨테이너마다 UI 주입 */
     /* ─ 4. 검색 UI 주입 */
     mw.hook('wikipage.content').add(function ($content) {
     mw.hook('wikipage.content').add($c => {
       $content.find('.dictionary-container').each(function () {
       $c.find('.dictionary-container').each(function () {
         var $box = $(this);
         const $box = $(this);
         if ($box.children().length) return;       // 중복 방지
         if ($box.children().length) return;   // 중복 방지


         /* 1) 입력창 + 버튼 */
         /* 입력창 + 버튼 */
         var input  = new OO.ui.TextInputWidget({
         const input  = new OO.ui.TextInputWidget({placeholder:'단어 입력…',icons:['search']});
          placeholder: '단어 입력…', icons:['search']
         const button = new OO.ui.ButtonWidget({label:'검색',icon:'search',flags:['progressive']});
        });
         const field  = new OO.ui.ActionFieldLayout(input,button,{align:'top'})
         var button = new OO.ui.ButtonWidget({
                        .$element.css('margin-bottom','10px');
          label:'검색', icon:'search', flags:['progressive']
        const $res  = $('<div class="dict-result"></div>');
        });
        $box.append(field,$res);
         var field  = new OO.ui.ActionFieldLayout(input, button, {align:'top'})
                    .$element.css('margin-bottom','10px');


         /* 2) 결과 영역 */
         /* 검색 실행 */
        var $result = $('<div class="dict-result"></div>');
 
        $box.append(field, $result);
 
        /* 3) 검색 실행 */
         function run() {
         function run() {
           var q = input.getValue().trim();
           const q = input.getValue().trim();
           var key = q.toLowerCase();
           const key = q.toLowerCase();
          if (!q) return $res.empty();


          if (!q) {
            $result.empty();
            return;
          }
           if (dict.hasOwnProperty(key)) {
           if (dict.hasOwnProperty(key)) {
             $result.html(
             const wt = `'''${q}'''<br>${dict[key]}`;  // 카드 안에 단어+정의를 한 번에 파싱
                '<div class="term">' + mw.html.escape(q) + '</div>' +
            parseWT(wt).then(html => {
                '<div class="def">'  + mw.html.escape(dict[key]) + '</div>'
              $res.html(`<div class="dict-card">${html}</div>`);
             );
             });
           } else {
           } else {
             $result.html('<div class="dict-none">해당 단어가 없습니다.</div>');
             $res.html('<div class="dict-none">해당 단어가 없습니다.</div>');
           }
           }
         }
         }
 
         button.on('click',run);
         button.on('click', run);
         input.on('enter',run);
         input.on('enter', run);
       });
       });
     });
     });
   }
   }
);
);

2025년 6월 29일 (일) 03:58 판

/* ▣ Dictionary “검색 전용” 가젯 ▣
   위키문법을 서버 파서로 HTML 변환 후 출력
-------------------------------------------------- */
mw.loader.using(
  ['oojs-ui-core', 'oojs-ui.styles.icons-interactions', 'mediawiki.api'],
  function () {

    /* ─ 1. JSON → {소문자 단어: 정의(wikitext)} ─ */
    var jsonNode = document.getElementById('dictionary-json');
    if (!jsonNode) return;
    var raw  = JSON.parse(jsonNode.textContent);
    var dict = {};
    Object.keys(raw).forEach(k => dict[k.toLowerCase()] = raw[k]);

    /* ─ 2. 위키텍스트 → HTML 변환용 함수 (캐시 포함) ─ */
    const api   = new mw.Api();
    const cache = Object.create(null);      // wt → html

    function parseWT(wt) {
      if (cache[wt]) return $.Deferred().resolve(cache[wt]).promise();
      return api.get({
        action: 'parse',
        format: 'json',
        prop:   'text',
        text:   wt,
        contentmodel: 'wikitext',
        disablelimitreport: 1
      }).then(data => {
        const html = data?.parse?.text['*'] || mw.html.escape(wt);
        cache[wt] = html;
        return html;
      }, () => mw.html.escape(wt));         // 오류 시 그대로 출력
    }

    /* ─ 3. 한 번만 결과·버튼 CSS 삽입 ─ */
    if (!document.getElementById('dict-card-style')) {
      mw.util.addCSS(`
.oo-ui-inputWidget-input, .oo-ui-buttonElement-button{
  background:light-dark(#fff,rgb(30,30,30));color:var(--text);
  border:1px solid light-dark(#ccc,#555);height:36px}
.oo-ui-inputWidget-input{border-radius:5px 0 0 5px}
.oo-ui-buttonElement-button{border-radius:0 5px 5px 0;padding:6px 8px}
.dict-card{border:1px solid light-dark(#ccc,#555);border-radius:8px;
  background:light-dark(#f9f9f9,hsl(200,5%,17%));margin:8px 0;padding:12px 16px}
.dict-none{padding:8px;color:light-dark(#d33,hsl(0,71%,75%))}
      `).id = 'dict-card-style';
    }

    /* ─ 4. 검색 UI 주입 ─ */
    mw.hook('wikipage.content').add($c => {
      $c.find('.dictionary-container').each(function () {
        const $box = $(this);
        if ($box.children().length) return;   // 중복 방지

        /* 입력창 + 버튼 */
        const input  = new OO.ui.TextInputWidget({placeholder:'단어 입력…',icons:['search']});
        const button = new OO.ui.ButtonWidget({label:'검색',icon:'search',flags:['progressive']});
        const field  = new OO.ui.ActionFieldLayout(input,button,{align:'top'})
                         .$element.css('margin-bottom','10px');
        const $res   = $('<div class="dict-result"></div>');
        $box.append(field,$res);

        /* 검색 실행 */
        function run() {
          const q = input.getValue().trim();
          const key = q.toLowerCase();
          if (!q) return $res.empty();

          if (dict.hasOwnProperty(key)) {
            const wt = `'''${q}'''<br>${dict[key]}`;   // 카드 안에 단어+정의를 한 번에 파싱
            parseWT(wt).then(html => {
              $res.html(`<div class="dict-card">${html}</div>`);
            });
          } else {
            $res.html('<div class="dict-none">해당 단어가 없습니다.</div>');
          }
        }
        button.on('click',run);
        input.on('enter',run);
      });
    });
  }
);