내 웹 응용 프로그램에 대한 사용자 정의 마우스 오른쪽 버튼 클릭 컨텍스트 메뉴 만들기


127

마우스 오른쪽 버튼을 클릭하면 사용자 정의 드롭 다운 메뉴가있는 Google 문서 및 맵 퀘스트와 같은 웹 사이트가 있습니다. 어쨌든 그들은 브라우저의 드롭 다운 메뉴 동작을 무시하며, 이제 정확히 어떻게 작동하는지 확신합니다. 이 작업을 수행 하는 jQuery 플러그인 을 찾았 지만 여전히 몇 가지 사항에 대해 궁금합니다.

  • 어떻게 작동합니까? 브라우저의 드롭 다운 메뉴가 실제로 재정의되었거나 효과가 방금 시뮬레이션 되었습니까? 그렇다면 어떻게?
  • 플러그인은 무엇을 추상화합니까? 무대 뒤에서 무슨 일이 일어나고 있습니까?
  • 이것이이 효과를 얻는 유일한 방법입니까?

사용자 정의 상황에 맞는 메뉴 이미지

작동중인 몇 가지 사용자 정의 컨텍스트 메뉴보기

답변:


219

나는이 질문이 매우 오래되었다는 것을 알고 있지만 똑같은 문제를 생각해 내고 스스로 해결했기 때문에 누군가 내가 구글을 통해이를 찾은 경우 대답하고 있습니다. 나는 @Andrew의 솔루션을 기반으로했지만 나중에 기본적으로 모든 것을 수정했습니다.

편집 : 이것이 최근에 얼마나 인기가 있는지보고, 나는 스타일을 업데이트하여 2014 년처럼 보이게하고 Windows 95처럼 보이게하기로 결정했습니다. @Quantico 및 @Trengot 버그를 수정하여 더 확실한 답이되었습니다.

편집 2 : 정말 멋진 새 기능이므로 StackSnippets로 설정했습니다. 참조 생각을 위해 좋은 jsfiddle을 여기에 둡니다 (네 번째 패널을 클릭하면 작동합니다).

새로운 스택 스 니펫 :

// JAVASCRIPT (jQuery)

// Trigger action when the contexmenu is about to be shown
$(document).bind("contextmenu", function (event) {
    
    // Avoid the real one
    event.preventDefault();
    
    // Show contextmenu
    $(".custom-menu").finish().toggle(100).
    
    // In the right position (the mouse)
    css({
        top: event.pageY + "px",
        left: event.pageX + "px"
    });
});


// If the document is clicked somewhere
$(document).bind("mousedown", function (e) {
    
    // If the clicked element is not the menu
    if (!$(e.target).parents(".custom-menu").length > 0) {
        
        // Hide it
        $(".custom-menu").hide(100);
    }
});


// If the menu element is clicked
$(".custom-menu li").click(function(){
    
    // This is the triggered action name
    switch($(this).attr("data-action")) {
        
        // A case for each action. Your actions here
        case "first": alert("first"); break;
        case "second": alert("second"); break;
        case "third": alert("third"); break;
    }
  
    // Hide it AFTER the action was triggered
    $(".custom-menu").hide(100);
  });
/* CSS3 */

/* The whole thing */
.custom-menu {
    display: none;
    z-index: 1000;
    position: absolute;
    overflow: hidden;
    border: 1px solid #CCC;
    white-space: nowrap;
    font-family: sans-serif;
    background: #FFF;
    color: #333;
    border-radius: 5px;
    padding: 0;
}

/* Each of the items in the list */
.custom-menu li {
    padding: 8px 12px;
    cursor: pointer;
    list-style-type: none;
    transition: all .3s ease;
    user-select: none;
}

.custom-menu li:hover {
    background-color: #DEF;
}
<!-- HTML -->
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.1/jquery.js"></script>

<ul class='custom-menu'>
  <li data-action="first">First thing</li>
  <li data-action="second">Second thing</li>
  <li data-action="third">Third thing</li>
</ul>

<!-- Not needed, only for making it clickable on StackOverflow -->
Right click me

참고 : 몇 가지 작은 버그 (커서에서 떨어진 드롭 다운 등) 가 나타날 수 있습니다. 스택 스 니펫보다 웹 페이지와 더 유사 하므로 jsfiddle 에서 작동하는지 확인하십시오 .


1
마우스 다운에 문제가있는 것 같습니다. 메뉴 항목을 클릭하면 마우스 다운 및 마우스 업인 클릭이 트리거되므로 경쟁 조건이 발생할 수 있습니다.
Quantico

2
@Quantico에게 감사드립니다. 사실 코드와 jsfiddle에서 모두 수정해야합니다. 다른 문제가 있습니까? 참고 사항 : 와우, 170 개의 이전 수정 사항이 jsfiddle에 적용 되었음이 틀림 없습니다.
Francisco Presencia

1
새 바이올린을 사용할 때 페이지에서 다른 HTML 요소를 사용하면 팝업이 투명하게 나타납니다. 편집 : CSS에 배경색을 추가하면 해결됩니다.
Holloway

1
또 다른 사소한 문제 : 메뉴가 표시된 상태에서 마우스 오른쪽 버튼을 클릭하면 표시되기 전에 깜박입니다. 나는 그것이 기본값처럼 숨기거나 새로운 위치에 나타나야한다고 생각합니다.
Holloway

@ChetanJoshi는 MDN에 따라 IE11에서 작동 해야하는 것 같습니다 : developer.mozilla.org/en-US/docs/Web/Events/… 오류가 보입니까?
Francisco Presencia

63

Adrian이 말했듯이 플러그인은 같은 방식으로 작동합니다. 필요한 세 가지 기본 부분이 있습니다.

1 : 이벤트에 대한 이벤트 핸들러 'contextmenu':

$(document).bind("contextmenu", function(event) {
    event.preventDefault();
    $("<div class='custom-menu'>Custom menu</div>")
        .appendTo("body")
        .css({top: event.pageY + "px", left: event.pageX + "px"});
});

여기에서 메뉴를 표시하려는 선택기에 이벤트 핸들러를 바인딩 할 수 있습니다. 전체 문서를 선택했습니다.

2 : 이벤트에 대한 이벤트 핸들러 'click'(사용자 정의 메뉴를 닫는 경우) :

$(document).bind("click", function(event) {
    $("div.custom-menu").hide();
});

3 : 메뉴 위치를 제어하는 ​​CSS :

.custom-menu {
    z-index:1000;
    position: absolute;
    background-color:#C0C0C0;
    border: 1px solid black;
    padding: 2px;
}

CSS를 가진 중요한 것은을 포함하는 것입니다 z-indexposition: absolute

매끄러운 jQuery 플러그인 으로이 모든 것을 감싸는 것은 어렵지 않습니다.

간단한 데모를 볼 수 있습니다 : http://jsfiddle.net/andrewwhitaker/fELma/


이 컨텍스트 메뉴는 사용자가 내부를 클릭 할 때 열린 상태로 유지되면 더 유용 할 것입니다 (그러나 사용자가 외부를 클릭하면 닫힙니다). 이런 식으로 작동하도록 수정할 수 있습니까?
Anderson Green

2
event.target의 클릭 바인딩 내부를 살펴보십시오 document. 이 상황에 맞는 메뉴 안에 아니라면, 메뉴를 숨기기 : jsfiddle.net/fELma/286
앤드류 휘태커

2
약간 수정했습니다 (여러 메뉴가 한 번에 표시되지 않도록). jsfiddle.net/fELma/287
Anderson Green

방사형 마우스 오른쪽 버튼 클릭 상황에 맞는 메뉴를 만들려고합니다 (예 : pushing-pixels.org/wp-content/uploads/2012/07/… ). 이 방법을 이해하는 데 큰 도움이됩니다. 감사합니다!
보리스

@AndrewWhitaker는 귀하의 답변이 문서 전체에 적용될 것이라고 말합니다. 예를 들어 버튼 (ID가 button1이라고 가정)과 같은 특정 컨트롤에 적용하려면 어떻게해야합니까?
Tk1993

8

<!DOCTYPE html>
<html>
<head>
    <title>Right Click</title>

    <link href="https://swisnl.github.io/jQuery-contextMenu/dist/jquery.contextMenu.css" rel="stylesheet" type="text/css" />

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
    <script src="https://swisnl.github.io/jQuery-contextMenu/dist/jquery.contextMenu.js" type="text/javascript"></script>

    <script src="https://swisnl.github.io/jQuery-contextMenu/dist/jquery.ui.position.min.js" type="text/javascript"></script>

</head>
<body>
    <span class="context-menu-one" style="border:solid 1px black; padding:5px;">Right Click Me</span>
    <script type="text/javascript">
        
        $(function() {
        $.contextMenu({
            selector: '.context-menu-one', 
            callback: function(key, options) {
                var m = "clicked: " + key;
                window.console && console.log(m) || alert(m); 
            },
            items: {
                "edit": {name: "Edit", icon: "edit"},
                "cut": {name: "Cut", icon: "cut"},
               copy: {name: "Copy", icon: "copy"},
                "paste": {name: "Paste", icon: "paste"},
                "delete": {name: "Delete", icon: "delete"},
                "sep1": "---------",
                "quit": {name: "Quit", icon: function(){
                    return 'context-menu-icon context-menu-icon-quit';
                }}
            }
        });

        $('.context-menu-one').on('click', function(e){
            console.log('clicked', this);
        })    
    });
    </script>
</body>
</html>


4

다음은 자바 스크립트에서 마우스 오른쪽 버튼 클릭 컨텍스트 메뉴 의 예입니다. 마우스 오른쪽 버튼 클릭 컨텍스트 메뉴

컨텍스트 메뉴 기능에 원시 javasScript 코드를 사용했습니다. 당신이 이것을 확인할 수 있습니까, 이것이 당신을 도울 수 있기를 바랍니다.

라이브 코드 :

(function() {
  
  "use strict";


  /*********************************************** Context Menu Function Only ********************************/
  function clickInsideElement( e, className ) {
    var el = e.srcElement || e.target;
    if ( el.classList.contains(className) ) {
      return el;
    } else {
      while ( el = el.parentNode ) {
        if ( el.classList && el.classList.contains(className) ) {
          return el;
        }
      }
    }
    return false;
  }

  function getPosition(e) {
    var posx = 0, posy = 0;
    if (!e) var e = window.event;
    if (e.pageX || e.pageY) {
      posx = e.pageX;
      posy = e.pageY;
    } else if (e.clientX || e.clientY) {
      posx = e.clientX + document.body.scrollLeft + document.documentElement.scrollLeft;
      posy = e.clientY + document.body.scrollTop + document.documentElement.scrollTop;
    }
    return {
      x: posx,
      y: posy
    }
  }

  // Your Menu Class Name
  var taskItemClassName = "thumb";
  var contextMenuClassName = "context-menu",contextMenuItemClassName = "context-menu__item",contextMenuLinkClassName = "context-menu__link", contextMenuActive = "context-menu--active";
  var taskItemInContext, clickCoords, clickCoordsX, clickCoordsY, menu = document.querySelector("#context-menu"), menuItems = menu.querySelectorAll(".context-menu__item");
  var menuState = 0, menuWidth, menuHeight, menuPosition, menuPositionX, menuPositionY, windowWidth, windowHeight;

  function initMenuFunction() {
    contextListener();
    clickListener();
    keyupListener();
    resizeListener();
  }

  /**
   * Listens for contextmenu events.
   */
  function contextListener() {
    document.addEventListener( "contextmenu", function(e) {
      taskItemInContext = clickInsideElement( e, taskItemClassName );

      if ( taskItemInContext ) {
        e.preventDefault();
        toggleMenuOn();
        positionMenu(e);
      } else {
        taskItemInContext = null;
        toggleMenuOff();
      }
    });
  }

  /**
   * Listens for click events.
   */
  function clickListener() {
    document.addEventListener( "click", function(e) {
      var clickeElIsLink = clickInsideElement( e, contextMenuLinkClassName );

      if ( clickeElIsLink ) {
        e.preventDefault();
        menuItemListener( clickeElIsLink );
      } else {
        var button = e.which || e.button;
        if ( button === 1 ) {
          toggleMenuOff();
        }
      }
    });
  }

  /**
   * Listens for keyup events.
   */
  function keyupListener() {
    window.onkeyup = function(e) {
      if ( e.keyCode === 27 ) {
        toggleMenuOff();
      }
    }
  }

  /**
   * Window resize event listener
   */
  function resizeListener() {
    window.onresize = function(e) {
      toggleMenuOff();
    };
  }

  /**
   * Turns the custom context menu on.
   */
  function toggleMenuOn() {
    if ( menuState !== 1 ) {
      menuState = 1;
      menu.classList.add( contextMenuActive );
    }
  }

  /**
   * Turns the custom context menu off.
   */
  function toggleMenuOff() {
    if ( menuState !== 0 ) {
      menuState = 0;
      menu.classList.remove( contextMenuActive );
    }
  }

  function positionMenu(e) {
    clickCoords = getPosition(e);
    clickCoordsX = clickCoords.x;
    clickCoordsY = clickCoords.y;
    menuWidth = menu.offsetWidth + 4;
    menuHeight = menu.offsetHeight + 4;

    windowWidth = window.innerWidth;
    windowHeight = window.innerHeight;

    if ( (windowWidth - clickCoordsX) < menuWidth ) {
      menu.style.left = (windowWidth - menuWidth)-0 + "px";
    } else {
      menu.style.left = clickCoordsX-0 + "px";
    }

    // menu.style.top = clickCoordsY + "px";

    if ( Math.abs(windowHeight - clickCoordsY) < menuHeight ) {
      menu.style.top = (windowHeight - menuHeight)-0 + "px";
    } else {
      menu.style.top = clickCoordsY-0 + "px";
    }
  }


  function menuItemListener( link ) {
    var menuSelectedPhotoId = taskItemInContext.getAttribute("data-id");
    console.log('Your Selected Photo: '+menuSelectedPhotoId)
    var moveToAlbumSelectedId = link.getAttribute("data-action");
    if(moveToAlbumSelectedId == 'remove'){
      console.log('You Clicked the remove button')
    }else if(moveToAlbumSelectedId && moveToAlbumSelectedId.length > 7){
      console.log('Clicked Album Name: '+moveToAlbumSelectedId);
    }
    toggleMenuOff();
  }
  initMenuFunction();

})();
/* For Body Padding and content */
body { padding-top: 70px; }
li a { text-decoration: none !important; }

/* Thumbnail only */
.thumb {
  margin-bottom: 30px;
}
.thumb:hover a, .thumb:active a, .thumb:focus a {
  border: 1px solid purple;
}

/************** For Context menu ***********/
/* context menu */
.context-menu {  display: none;  position: absolute;  z-index: 9999;  padding: 12px 0;  width: 200px;  background-color: #fff;  border: solid 1px #dfdfdf;  box-shadow: 1px 1px 2px #cfcfcf;  }
.context-menu--active {  display: block;  }

.context-menu__items { list-style: none;  margin: 0;  padding: 0;  }
.context-menu__item { display: block;  margin-bottom: 4px;  }
.context-menu__item:last-child {  margin-bottom: 0;  }
.context-menu__link {  display: block;  padding: 4px 12px;  color: #0066aa;  text-decoration: none;  }
.context-menu__link:hover {  color: #fff;  background-color: #0066aa;  }
.context-menu__items ul {  position: absolute;  white-space: nowrap;  z-index: 1;  left: -99999em;}
.context-menu__items > li:hover > ul {  left: auto;  padding-top: 5px  ;  min-width: 100%;  }
.context-menu__items > li li ul {  border-left:1px solid #fff;}
.context-menu__items > li li:hover > ul {  left: 100%;  top: -1px;  }
.context-menu__item ul { background-color: #ffffff; padding: 7px 11px;  list-style-type: none;  text-decoration: none; margin-left: 40px; }
.page-media .context-menu__items ul li { display: block; }
/************** For Context menu ***********/
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet"/>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/>
<body>



    <!-- Page Content -->
    <div class="container">

        <div class="row">

            <div class="col-lg-12">
                <h1 class="page-header">Thumbnail Gallery <small>(Right click to see the context menu)</small></h1>
            </div>

            <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                <a class="thumbnail" href="#">
                    <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                </a>
            </div>
            <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                <a class="thumbnail" href="#">
                    <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                </a>
            </div>
            <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                <a class="thumbnail" href="#">
                    <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                </a>
            </div>
            <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                <a class="thumbnail" href="#">
                    <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                </a>
            </div>
            <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                <a class="thumbnail" href="#">
                    <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                </a>
            </div>
            <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                <a class="thumbnail" href="#">
                    <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                </a>
            </div>
            <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                <a class="thumbnail" href="#">
                    <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                </a>
            </div>
            <div class="col-lg-3 col-md-4 col-xs-6 thumb">
                <a class="thumbnail" href="#">
                    <img class="img-responsive" src="http://placehold.it/400x300" alt="">
                </a>
            </div>

        </div>

        <hr>


    </div>
    <!-- /.container -->


    <!-- / The Context Menu -->
    <nav id="context-menu" class="context-menu">
        <ul class="context-menu__items">
            <li class="context-menu__item">
                <a href="#" class="context-menu__link" data-action="Delete This Photo"><i class="fa fa-empire"></i> Delete This Photo</a>
            </li>
            <li class="context-menu__item">
                <a href="#" class="context-menu__link" data-action="Photo Option 2"><i class="fa fa-envira"></i> Photo Option 2</a>
            </li>
            <li class="context-menu__item">
                <a href="#" class="context-menu__link" data-action="Photo Option 3"><i class="fa fa-first-order"></i> Photo Option 3</a>
            </li>
            <li class="context-menu__item">
                <a href="#" class="context-menu__link" data-action="Photo Option 4"><i class="fa fa-gitlab"></i> Photo Option 4</a>
            </li>
            <li class="context-menu__item">
                <a href="#" class="context-menu__link" data-action="Photo Option 5"><i class="fa fa-ioxhost"></i> Photo Option 5</a>
            </li>
            <li class="context-menu__item">
                <a href="#" class="context-menu__link"><i class="fa fa-arrow-right"></i> Add Photo to</a>
                <ul>
                    <li><a href="#!" class="context-menu__link" data-action="album-one"><i class="fa fa-camera-retro"></i> Album One</a></li>
                    <li><a href="#!" class="context-menu__link" data-action="album-two"><i class="fa fa-camera-retro"></i> Album Two</a></li>
                    <li><a href="#!" class="context-menu__link" data-action="album-three"><i class="fa fa-camera-retro"></i> Album Three</a></li>
                    <li><a href="#!" class="context-menu__link" data-action="album-four"><i class="fa fa-camera-retro"></i> Album Four</a></li>
                </ul>
            </li>
        </ul>
    </nav>

    <!-- End # Context Menu -->


</body>


바닐라 JS와 깨끗한 레이아웃을 사용하는 훌륭한 직업!
kurt

3

브라우저의 상황에 맞는 메뉴가 재정의되고 있습니다. 주요 브라우저에서 기본 상황에 맞는 메뉴를 확장 할 수있는 방법이 없습니다.

플러그인은 자체 메뉴를 생성하기 때문에 실제로 추상화되는 유일한 부분은 브라우저의 상황에 맞는 메뉴 이벤트입니다. 플러그인은 구성에 따라 html 메뉴를 만든 다음 클릭 위치에 해당 컨텐츠를 배치합니다.

예,이 방법은 사용자 정의 상황에 맞는 메뉴를 만드는 유일한 방법입니다. 분명히 다른 플러그인은 약간 다른 작업을 수행하지만 모두 브라우저의 이벤트를 재정의하고 자체 HTML 기반 메뉴를 올바른 위치에 배치합니다.


2
Firefox가 HTML5 기본 '컨텍스트 메뉴'(마크 업을 통해 선언)에 대한 지원을 추가하고 있음을 언급합니다. Firefox 8 베타에서 사용할 수 있습니다. ( developer.mozilla.org/en/Firefox_8_for_developers ).
poshaughnessy

2

이 자습서를 볼 수 있습니다. http://www.youtube.com/watch?v=iDyEfKWCzhg 상황에 맞는 메뉴가 처음에 숨겨져 있고 절대 위치에 있는지 확인하십시오. 이렇게하면 여러 상황에 맞는 메뉴가없고 상황에 맞는 메뉴가 쓸모 없게 만들어집니다. 페이지 링크는 YouTube 비디오 설명에 있습니다.

$(document).bind("contextmenu", function(event){
$("#contextmenu").css({"top": event.pageY +  "px", "left": event.pageX +  "px"}).show();
});
$(document).bind("click", function(){
$("#contextmenu").hide();
});

1

나는 이것이 또한 오래되었다는 것을 안다. 최근에 클릭 한 요소에 따라 다른 속성을 가진 다른 사이트에 삽입하는 컨텍스트 메뉴를 만들어야했습니다.

다소 거칠고 이것을 달성하는 더 좋은 방법이 있습니다. 그것은 jQuery를 컨텍스트 메뉴 라이브러리 사용 여기에 위치를

나는 그것을 만드는 것을 즐겼지만 너희들은 그것을 쓸모가 있을지도 모른다.

여기 바이올린이 있습니다. 나는 그것이 누군가를 희망적으로 도울 수 있기를 바랍니다.

$(function() {
  function createSomeMenu() {
    var all_array = '{';
    var x = event.clientX,
      y = event.clientY,
      elementMouseIsOver = document.elementFromPoint(x, y);
    if (elementMouseIsOver.closest('a')) {
      all_array += '"Link-Fold": {"name": "Link", "icon": "fa-external-link", "items": {"fold2-key1": {"name": "Open Site in New Tab"}, "fold2-key2": {"name": "Open Site in Split Tab"}, "fold2-key3": {"name": "Copy URL"}}},';
    }
    if (elementMouseIsOver.closest('img')) {
      all_array += '"Image-Fold": {"name": "Image","icon": "fa-picture-o","items": {"fold1-key1": {"name":"Download Image"},"fold1-key2": {"name": "Copy Image Location"},"fold1-key3": {"name": "Go To Image"}}},';
    }
    all_array += '"copy": {"name": "Copy","icon": "copy"},"paste": {"name": "Paste","icon": "paste"},"edit": {"name": "Edit HTML","icon": "fa-code"}}';
    return JSON.parse(all_array);
  }

  // setup context menu
  $.contextMenu({
    selector: 'body',
    build: function($trigger, e) {
      return {
        callback: function(key, options) {
          var m = "clicked: " + key;
          console.log(m);
        },
        items: createSomeMenu()
      };
    }
  });
});

0

부트 스트랩을 사용하여 다음과 같이 훌륭하고 쉬운 구현이 있습니다.

<select class="custom-select" id="list" multiple></select>

<div class="dropdown-menu" id="menu-right-click" style=>
    <h6 class="dropdown-header">Actions</h6>
    <a class="dropdown-item" href="" onclick="option1();">Option 1</a>
    <a class="dropdown-item" href="" onclick="option2();">Option 2</a>
</div>

<script>
    $("#menu-right-click").hide();

    $(document).on("contextmenu", "#list", function (e) {
        $("#menu-right-click")
            .css({
                position: 'absolute',
                left: e.pageX,
                top: e.pageY,
                display: 'block'
            })
        return false;
    });

    function option1() {
        // something you want...
        $("#menu-right-click").hide();
    }

    function option2() {
        // something else 
        $("#menu-right-click").hide();
    }
</script>
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.