profile

이 세상에 하나는 남기고 가자

세상에 필요한 소스코드 한줄 남기고 가자

마우스 우클릭/드래그 방지 해제 방법(북마크릿)

유영재

웹사이트를 서핑하다가 필요한 내용이 있어 복사하려고 하면 우클릭이나 드래그를 막아놔서 복사를 할 수 없도록 하는 사이트들이 있다. 이런 경우라면 javascript를 끄고 해당 사이트를 방문하면 대부분 복사가 가능하다.

하지만 매번 이렇게 하기에는 귀찮다. 그래서 찾아보니 기존에 돌아다니는 우클릭 해제에 관련된 스크립트들은 대부분이 지금은 동작하지 않았다. 그래서 그런대로 잘 동작하는 스크립트를 가져다가 조금 개선해서 gist에 올려 놨다.

코드를 소개하기에 앞서 무단 복제는 지양하기를 바란다. 나도 나름의 콘텐트를 생산하는 입장헤서 불펌은 반대한다. 내용을 퍼간다면 최소한 출처를 남겨주는 예의만이라도... 다만, 개발 관련된 문서를 보다 보면 소개한 내용 중 일부(쉘 명령어 등)을 복사해서 사용해야 하는데 복사하기가 막혀 너무 불편한 부분을 해소하기 위해 만든 것이다.

일단 원 소스는 asamaru7/right-click.js에 공개되어 있으니 개선이 필요한 부분은 직접 개선해서 사용해도 된다.

위 소스를 북마크릿으로 변형한 소스는 아래와 같다.

javascript:(function () %7B%0A%09if (window.subvaAllowRightClick === undefined) %7B%0A%09%09// https://greasyfork.org/en/scripts/23772-absolute-enable-right-click-copy/code%0A%09%09window.subvaAllowRightClick = function (dom) %7B%0A%09%09%09(function GetSelection() %7B%0A%09%09%09%09var Style = dom.createElement('style');%0A%09%09%09%09Style.type = 'text/css';%0A%09%09%09%09var TextNode = '*%7Buser-select:text!important;-webkit-user-select:text!important;%7D';%0A%09%09%09%09if (Style.styleSheet) %7B%0A%09%09%09%09%09Style.styleSheet.cssText = TextNode;%0A%09%09%09%09%7D%0A%09%09%09%09else %7B%0A%09%09%09%09%09Style.appendChild(dom.createTextNode(TextNode));%0A%09%09%09%09%7D%0A%09%09%09%09dom.getElementsByTagName('head')%5B0%5D.appendChild(Style);%0A%09%09%09%7D)();%0A%0A%09%09%09(function SetEvents() %7B%0A%09%09%09%09var events = %5B'copy', 'cut', 'paste', 'select', 'selectstart'%5D;%0A%09%09%09%09for (var i = 0; i < events.length; i++)%0A%09%09%09%09%09dom.addEventListener(events%5Bi%5D, function (e) %7B%0A%09%09%09%09%09%09e.stopPropagation();%0A%09%09%09%09%09%7D, true);%0A%09%09%09%7D)();%0A%0A%09%09%09(function RestoreEvents() %7B%0A%09%09%09%09var n = null;%0A%09%09%09%09var d = document;%0A%09%09%09%09var b = dom.body;%0A%09%09%09%09var SetEvents = %5Bd.oncontextmenu = n, d.onselectstart = n, d.ondragstart = n, d.onmousedown = n%5D;%0A%09%09%09%09var GetEvents = %5Bb.oncontextmenu = n, b.onselectstart = n, b.ondragstart = n, b.onmousedown = n, b.oncut = n, b.oncopy = n, b.onpaste = n%5D;%0A%09%09%09%7D)();%0A%0A%09%09%09(function RightClickButton() %7B%0A%09%09%09%09setTimeout(function () %7B%0A%09%09%09%09%09dom.oncontextmenu = null;%0A%09%09%09%09%7D, 2000);%0A%09%09%09%09function EventsCall(callback) %7B%0A%09%09%09%09%09this.events = %5B'DOMAttrModified', 'DOMNodeInserted', 'DOMNodeRemoved', 'DOMCharacterDataModified', 'DOMSubtreeModified'%5D;%0A%09%09%09%09%09this.bind();%0A%09%09%09%09%7D%0A%0A%09%09%09%09EventsCall.prototype.bind = function () %7B%0A%09%09%09%09%09this.events.forEach(function (event) %7B%0A%09%09%09%09%09%09dom.addEventListener(event, this, true);%0A%09%09%09%09%09%7D.bind(this));%0A%09%09%09%09%7D;%0A%09%09%09%09EventsCall.prototype.handleEvent = function () %7B%0A%09%09%09%09%09this.isCalled = true;%0A%09%09%09%09%7D;%0A%09%09%09%09EventsCall.prototype.unbind = function () %7B%0A%09%09%09%09%09this.events.forEach(function (event) %7B%0A%09%09%09%09%09%7D.bind(this));%0A%09%09%09%09%7D;%0A%09%09%09%09function EventHandler(event) %7B%0A%09%09%09%09%09this.event = event;%0A%09%09%09%09%09this.contextmenuEvent = this.createEvent(this.event.type);%0A%09%09%09%09%7D%0A%0A%09%09%09%09EventHandler.prototype.createEvent = function (type) %7B%0A%09%09%09%09%09var target = this.event.target;%0A%09%09%09%09%09var event = target.ownerDocument.createEvent('MouseEvents');%0A%09%09%09%09%09event.initMouseEvent(type, this.event.bubbles, this.event.cancelable,%0A%09%09%09%09%09%09target.ownerDocument.defaultView, this.event.detail,%0A%09%09%09%09%09%09this.event.screenX, this.event.screenY, this.event.clientX, this.event.clientY,%0A%09%09%09%09%09%09this.event.ctrlKey, this.event.altKey, this.event.shiftKey, this.event.metaKey,%0A%09%09%09%09%09%09this.event.button, this.event.relatedTarget);%0A%09%09%09%09%09return event;%0A%09%09%09%09%7D;%0A%09%09%09%09EventHandler.prototype.fire = function () %7B%0A%09%09%09%09%09var target = this.event.target;%0A%09%09%09%09%09var contextmenuHandler = function (event) %7B%0A%09%09%09%09%09%09event.preventDefault();%0A%09%09%09%09%09%7D.bind(this);%0A%09%09%09%09%09target.dispatchEvent(this.contextmenuEvent);%0A%09%09%09%09%09this.isCanceled = this.contextmenuEvent.defaultPrevented;%0A%09%09%09%09%7D;%0A%09%09%09%09window.addEventListener('contextmenu', handleEvent, true);%0A%09%09%09%09function handleEvent(event) %7B%0A%09%09%09%09%09event.stopPropagation();%0A%09%09%09%09%09event.stopImmediatePropagation();%0A%09%09%09%09%09var handler = new EventHandler(event);%0A%09%09%09%09%09window.removeEventListener(event.type, handleEvent, true);%0A%09%09%09%09%09var EventsCallBback = new EventsCall(function () %7B%0A%09%09%09%09%09%7D);%0A%09%09%09%09%09handler.fire();%0A%09%09%09%09%09window.addEventListener(event.type, handleEvent, true);%0A%09%09%09%09%09if (handler.isCanceled && (EventsCallBback.isCalled))%0A%09%09%09%09%09%09event.preventDefault();%0A%09%09%09%09%7D%0A%09%09%09%7D)();%0A%0A%09%09%09// function KeyPress(e) %7B%0A%09%09%09// %09if (e.altKey && e.ctrlKey) %7B%0A%09%09%09// %09%09if (confirm("Activate Absolute Right Click Mode!") === true) %7B%0A%09%09%09// %09%09%09Absolute_Mod();%0A%09%09%09// %09%09%7D%0A%09%09%09// %09%7D%0A%09%09%09// %7D%0A%09%09%09// dom.addEventListener("keydown", KeyPress);%0A%0A%09%09%09(function Absolute_Mod() %7B%0A%09%09%09%09var events = %5B'contextmenu', 'copy', 'cut', 'paste', 'mouseup', 'mousedown', 'keyup', 'keydown', 'drag', 'dragstart', 'select', 'selectstart'%5D;%0A%09%09%09%09for (var i = 0; i < events.length; i++) %7B%0A%09%09%09%09%09dom.addEventListener(events%5Bi%5D, function (e) %7B%0A%09%09%09%09%09%09e.stopPropagation();%0A%09%09%09%09%09%7D, true);%0A%09%09%09%09%7D%0A%09%09%09%7D)();%0A%09%09%7D;%0A%0A//%09%09window.subvaAllowRightClick(document);%0A%0A%09%09function runAll(w) %7B%0A%09%09%09try %7B%0A%09%09%09%09window.subvaAllowRightClick(w.document);%0A%09%09%09%7D catch (e) %7B%0A%09%09%09%7D%0A%09%09%09for (var i = 0; i < w.frames.length; i++) %7B%0A%09%09%09%09runAll(w.frames%5Bi%5D);%0A%09%09%09%7D%0A%09%09%7D%0A%09%7D%0A%09runAll(window);%0A%7D)();

사용하는 브라우저에서 북마크를 하나 만들고 위 소스를 복사해서 url에 입력하면 된다. 사용 방법은 당연히 우클릭이 막힌 페이지에 가서 새로 만든 북마크를 누르면 해당 페이지에서 우클릭을 사용할 수 있다.

위 방법이 어렵다면 "북마크릿 생성기-우클릭 해제"에 가서 아래쪽에 있는 "DRAG INTO YOUR BOOKMARK-TOOLBAR" 링크를 드래그해서 북마크바에 올려 놓으면 자동 추가된다.

gist에 공개되어 있지만 여기에도 소스를 남겨둔다.

(function () {
    if (window.subvaAllowRightClick === undefined) {
        // https://greasyfork.org/en/scripts/23772-absolute-enable-right-click-copy/code
        window.subvaAllowRightClick = function (dom) {
            (function GetSelection() {
                var Style = dom.createElement('style');
                Style.type = 'text/css';
                var TextNode = '*{user-select:text!important;-webkit-user-select:text!important;}';
                if (Style.styleSheet) {
                    Style.styleSheet.cssText = TextNode;
                }
                else {
                    Style.appendChild(dom.createTextNode(TextNode));
                }
                dom.getElementsByTagName('head')[0].appendChild(Style);
            })();

            (function SetEvents() {
                var events = ['copy', 'cut', 'paste', 'select', 'selectstart'];
                for (var i = 0; i < events.length; i++)
                    dom.addEventListener(events[i], function (e) {
                        e.stopPropagation();
                    }, true);
            })();

            (function RestoreEvents() {
                var n = null;
                var d = document;
                var b = dom.body;
                var SetEvents = [d.oncontextmenu = n, d.onselectstart = n, d.ondragstart = n, d.onmousedown = n];
                var GetEvents = [b.oncontextmenu = n, b.onselectstart = n, b.ondragstart = n, b.onmousedown = n, b.oncut = n, b.oncopy = n, b.onpaste = n];
            })();

            (function RightClickButton() {
                setTimeout(function () {
                    dom.oncontextmenu = null;
                }, 2000);
                function EventsCall(callback) {
                    this.events = ['DOMAttrModified', 'DOMNodeInserted', 'DOMNodeRemoved', 'DOMCharacterDataModified', 'DOMSubtreeModified'];
                    this.bind();
                }

                EventsCall.prototype.bind = function () {
                    this.events.forEach(function (event) {
                        dom.addEventListener(event, this, true);
                    }.bind(this));
                };
                EventsCall.prototype.handleEvent = function () {
                    this.isCalled = true;
                };
                EventsCall.prototype.unbind = function () {
                    this.events.forEach(function (event) {
                    }.bind(this));
                };
                function EventHandler(event) {
                    this.event = event;
                    this.contextmenuEvent = this.createEvent(this.event.type);
                }

                EventHandler.prototype.createEvent = function (type) {
                    var target = this.event.target;
                    var event = target.ownerDocument.createEvent('MouseEvents');
                    event.initMouseEvent(type, this.event.bubbles, this.event.cancelable,
                        target.ownerDocument.defaultView, this.event.detail,
                        this.event.screenX, this.event.screenY, this.event.clientX, this.event.clientY,
                        this.event.ctrlKey, this.event.altKey, this.event.shiftKey, this.event.metaKey,
                        this.event.button, this.event.relatedTarget);
                    return event;
                };
                EventHandler.prototype.fire = function () {
                    var target = this.event.target;
                    var contextmenuHandler = function (event) {
                        event.preventDefault();
                    }.bind(this);
                    target.dispatchEvent(this.contextmenuEvent);
                    this.isCanceled = this.contextmenuEvent.defaultPrevented;
                };
                window.addEventListener('contextmenu', handleEvent, true);
                function handleEvent(event) {
                    event.stopPropagation();
                    event.stopImmediatePropagation();
                    var handler = new EventHandler(event);
                    window.removeEventListener(event.type, handleEvent, true);
                    var EventsCallBback = new EventsCall(function () {
                    });
                    handler.fire();
                    window.addEventListener(event.type, handleEvent, true);
                    if (handler.isCanceled && (EventsCallBback.isCalled))
                        event.preventDefault();
                }
            })();

            // function KeyPress(e) {
            //  if (e.altKey && e.ctrlKey) {
            //      if (confirm("Activate Absolute Right Click Mode!") === true) {
            //          Absolute_Mod();
            //      }
            //  }
            // }
            // dom.addEventListener("keydown", KeyPress);

            (function Absolute_Mod() {
                var events = ['contextmenu', 'copy', 'cut', 'paste', 'mouseup', 'mousedown', 'keyup', 'keydown', 'drag', 'dragstart', 'select', 'selectstart'];
                for (var i = 0; i < events.length; i++) {
                    dom.addEventListener(events[i], function (e) {
                        e.stopPropagation();
                    }, true);
                }
            })();
        };

//      window.subvaAllowRightClick(document);

        function runAll(w) {
            try {
                window.subvaAllowRightClick(w.document);
            } catch (e) {
            }
            for (var i = 0; i < w.frames.length; i++) {
                runAll(w.frames[i]);
            }
        }
    }
    runAll(window);
})();
comments powered by Disqus