diff options
| author | Shishkevich D. <135337715+shishkevichd@users.noreply.github.com> | 2025-03-06 22:43:46 +0300 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2025-03-06 22:43:46 +0300 |
| commit | 2d8cca3a2ec1ae9034b7bc28a2fb5f2260f43e1a (patch) | |
| tree | d9b0f6b919d75d872b0c1a3cf30a4b009ccf21e5 | |
| parent | cf7fec1351e44c624e599c226c35748f187ff8d1 (diff) | |
refactor: delete `clipboardjs` (#2727)
text copying can be done without using additional libraries
| -rw-r--r-- | web/assets/clipboard/clipboard.min.js | 7 | ||||
| -rw-r--r-- | web/assets/js/util/common.js | 18 | ||||
| -rw-r--r-- | web/html/common/qrcode_modal.html | 17 | ||||
| -rw-r--r-- | web/html/common/text_modal.html | 20 | ||||
| -rw-r--r-- | web/html/xui/inbound_info_modal.html | 23 | ||||
| -rw-r--r-- | web/html/xui/inbounds.html | 1 | ||||
| -rw-r--r-- | web/html/xui/index.html | 1 |
7 files changed, 40 insertions, 47 deletions
diff --git a/web/assets/clipboard/clipboard.min.js b/web/assets/clipboard/clipboard.min.js deleted file mode 100644 index 1103f811..00000000 --- a/web/assets/clipboard/clipboard.min.js +++ /dev/null @@ -1,7 +0,0 @@ -/*! - * clipboard.js v2.0.11 - * https://clipboardjs.com/ - * - * Licensed MIT © Zeno Rocha - */ -!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.ClipboardJS=e():t.ClipboardJS=e()}(this,function(){return n={686:function(t,e,n){"use strict";n.d(e,{default:function(){return b}});var e=n(279),i=n.n(e),e=n(370),u=n.n(e),e=n(817),r=n.n(e);function c(t){try{return document.execCommand(t)}catch(t){return}}var a=function(t){t=r()(t);return c("cut"),t};function o(t,e){var n,o,t=(n=t,o="rtl"===document.documentElement.getAttribute("dir"),(t=document.createElement("textarea")).style.fontSize="12pt",t.style.border="0",t.style.padding="0",t.style.margin="0",t.style.position="absolute",t.style[o?"right":"left"]="-9999px",o=window.pageYOffset||document.documentElement.scrollTop,t.style.top="".concat(o,"px"),t.setAttribute("readonly",""),t.value=n,t);return e.container.appendChild(t),e=r()(t),c("copy"),t.remove(),e}var f=function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{container:document.body},n="";return"string"==typeof t?n=o(t,e):t instanceof HTMLInputElement&&!["text","search","url","tel","password"].includes(null==t?void 0:t.type)?n=o(t.value,e):(n=r()(t),c("copy")),n};function l(t){return(l="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}var s=function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{},e=t.action,n=void 0===e?"copy":e,o=t.container,e=t.target,t=t.text;if("copy"!==n&&"cut"!==n)throw new Error('Invalid "action" value, use either "copy" or "cut"');if(void 0!==e){if(!e||"object"!==l(e)||1!==e.nodeType)throw new Error('Invalid "target" value, use a valid Element');if("copy"===n&&e.hasAttribute("disabled"))throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');if("cut"===n&&(e.hasAttribute("readonly")||e.hasAttribute("disabled")))throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes')}return t?f(t,{container:o}):e?"cut"===n?a(e):f(e,{container:o}):void 0};function p(t){return(p="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t})(t)}function d(t,e){for(var n=0;n<e.length;n++){var o=e[n];o.enumerable=o.enumerable||!1,o.configurable=!0,"value"in o&&(o.writable=!0),Object.defineProperty(t,o.key,o)}}function y(t,e){return(y=Object.setPrototypeOf||function(t,e){return t.__proto__=e,t})(t,e)}function h(n){var o=function(){if("undefined"==typeof Reflect||!Reflect.construct)return!1;if(Reflect.construct.sham)return!1;if("function"==typeof Proxy)return!0;try{return Date.prototype.toString.call(Reflect.construct(Date,[],function(){})),!0}catch(t){return!1}}();return function(){var t,e=v(n);return t=o?(t=v(this).constructor,Reflect.construct(e,arguments,t)):e.apply(this,arguments),e=this,!(t=t)||"object"!==p(t)&&"function"!=typeof t?function(t){if(void 0!==t)return t;throw new ReferenceError("this hasn't been initialised - super() hasn't been called")}(e):t}}function v(t){return(v=Object.setPrototypeOf?Object.getPrototypeOf:function(t){return t.__proto__||Object.getPrototypeOf(t)})(t)}function m(t,e){t="data-clipboard-".concat(t);if(e.hasAttribute(t))return e.getAttribute(t)}var b=function(){!function(t,e){if("function"!=typeof e&&null!==e)throw new TypeError("Super expression must either be null or a function");t.prototype=Object.create(e&&e.prototype,{constructor:{value:t,writable:!0,configurable:!0}}),e&&y(t,e)}(r,i());var t,e,n,o=h(r);function r(t,e){var n;return function(t){if(!(t instanceof r))throw new TypeError("Cannot call a class as a function")}(this),(n=o.call(this)).resolveOptions(e),n.listenClick(t),n}return t=r,n=[{key:"copy",value:function(t){var e=1<arguments.length&&void 0!==arguments[1]?arguments[1]:{container:document.body};return f(t,e)}},{key:"cut",value:function(t){return a(t)}},{key:"isSupported",value:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:["copy","cut"],t="string"==typeof t?[t]:t,e=!!document.queryCommandSupported;return t.forEach(function(t){e=e&&!!document.queryCommandSupported(t)}),e}}],(e=[{key:"resolveOptions",value:function(){var t=0<arguments.length&&void 0!==arguments[0]?arguments[0]:{};this.action="function"==typeof t.action?t.action:this.defaultAction,this.target="function"==typeof t.target?t.target:this.defaultTarget,this.text="function"==typeof t.text?t.text:this.defaultText,this.container="object"===p(t.container)?t.container:document.body}},{key:"listenClick",value:function(t){var e=this;this.listener=u()(t,"click",function(t){return e.onClick(t)})}},{key:"onClick",value:function(t){var e=t.delegateTarget||t.currentTarget,n=this.action(e)||"copy",t=s({action:n,container:this.container,target:this.target(e),text:this.text(e)});this.emit(t?"success":"error",{action:n,text:t,trigger:e,clearSelection:function(){e&&e.focus(),window.getSelection().removeAllRanges()}})}},{key:"defaultAction",value:function(t){return m("action",t)}},{key:"defaultTarget",value:function(t){t=m("target",t);if(t)return document.querySelector(t)}},{key:"defaultText",value:function(t){return m("text",t)}},{key:"destroy",value:function(){this.listener.destroy()}}])&&d(t.prototype,e),n&&d(t,n),r}()},828:function(t){var e;"undefined"==typeof Element||Element.prototype.matches||((e=Element.prototype).matches=e.matchesSelector||e.mozMatchesSelector||e.msMatchesSelector||e.oMatchesSelector||e.webkitMatchesSelector),t.exports=function(t,e){for(;t&&9!==t.nodeType;){if("function"==typeof t.matches&&t.matches(e))return t;t=t.parentNode}}},438:function(t,e,n){var u=n(828);function i(t,e,n,o,r){var i=function(e,n,t,o){return function(t){t.delegateTarget=u(t.target,n),t.delegateTarget&&o.call(e,t)}}.apply(this,arguments);return t.addEventListener(n,i,r),{destroy:function(){t.removeEventListener(n,i,r)}}}t.exports=function(t,e,n,o,r){return"function"==typeof t.addEventListener?i.apply(null,arguments):"function"==typeof n?i.bind(null,document).apply(null,arguments):("string"==typeof t&&(t=document.querySelectorAll(t)),Array.prototype.map.call(t,function(t){return i(t,e,n,o,r)}))}},879:function(t,n){n.node=function(t){return void 0!==t&&t instanceof HTMLElement&&1===t.nodeType},n.nodeList=function(t){var e=Object.prototype.toString.call(t);return void 0!==t&&("[object NodeList]"===e||"[object HTMLCollection]"===e)&&"length"in t&&(0===t.length||n.node(t[0]))},n.string=function(t){return"string"==typeof t||t instanceof String},n.fn=function(t){return"[object Function]"===Object.prototype.toString.call(t)}},370:function(t,e,n){var f=n(879),l=n(438);t.exports=function(t,e,n){if(!t&&!e&&!n)throw new Error("Missing required arguments");if(!f.string(e))throw new TypeError("Second argument must be a String");if(!f.fn(n))throw new TypeError("Third argument must be a Function");if(f.node(t))return c=e,a=n,(u=t).addEventListener(c,a),{destroy:function(){u.removeEventListener(c,a)}};if(f.nodeList(t))return o=t,r=e,i=n,Array.prototype.forEach.call(o,function(t){t.addEventListener(r,i)}),{destroy:function(){Array.prototype.forEach.call(o,function(t){t.removeEventListener(r,i)})}};if(f.string(t))return t=t,e=e,n=n,l(document.body,t,e,n);throw new TypeError("First argument must be a String, HTMLElement, HTMLCollection, or NodeList");var o,r,i,u,c,a}},817:function(t){t.exports=function(t){var e,n="SELECT"===t.nodeName?(t.focus(),t.value):"INPUT"===t.nodeName||"TEXTAREA"===t.nodeName?((e=t.hasAttribute("readonly"))||t.setAttribute("readonly",""),t.select(),t.setSelectionRange(0,t.value.length),e||t.removeAttribute("readonly"),t.value):(t.hasAttribute("contenteditable")&&t.focus(),n=window.getSelection(),(e=document.createRange()).selectNodeContents(t),n.removeAllRanges(),n.addRange(e),n.toString());return n}},279:function(t){function e(){}e.prototype={on:function(t,e,n){var o=this.e||(this.e={});return(o[t]||(o[t]=[])).push({fn:e,ctx:n}),this},once:function(t,e,n){var o=this;function r(){o.off(t,r),e.apply(n,arguments)}return r._=e,this.on(t,r,n)},emit:function(t){for(var e=[].slice.call(arguments,1),n=((this.e||(this.e={}))[t]||[]).slice(),o=0,r=n.length;o<r;o++)n[o].fn.apply(n[o].ctx,e);return this},off:function(t,e){var n=this.e||(this.e={}),o=n[t],r=[];if(o&&e)for(var i=0,u=o.length;i<u;i++)o[i].fn!==e&&o[i].fn._!==e&&r.push(o[i]);return r.length?n[t]=r:delete n[t],this}},t.exports=e,t.exports.TinyEmitter=e}},r={},o.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return o.d(e,{a:e}),e},o.d=function(t,e){for(var n in e)o.o(e,n)&&!o.o(t,n)&&Object.defineProperty(t,n,{enumerable:!0,get:e[n]})},o.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},o(686).default;function o(t){if(r[t])return r[t].exports;var e=r[t]={exports:{}};return n[t](e,e.exports,o),e.exports}var n,r});
\ No newline at end of file diff --git a/web/assets/js/util/common.js b/web/assets/js/util/common.js index 779af0bf..df826aa9 100644 --- a/web/assets/js/util/common.js +++ b/web/assets/js/util/common.js @@ -64,6 +64,24 @@ function formatSecond(second) { } } +function copyToClipboard(text) { + // !! here old way of copying is used because not everyone can afford https connection + return new Promise((resolve) => { + const textarea = document.createElement("textarea"); + + textarea.value = text; + + document.body.appendChild(textarea); + + textarea.select(); + document.execCommand("copy"); + + document.body.removeChild(textarea); + + resolve(text) + }) +} + function addZero(num) { if (num < 10) { return "0" + num; diff --git a/web/html/common/qrcode_modal.html b/web/html/common/qrcode_modal.html index 94e750c7..117dd640 100644 --- a/web/html/common/qrcode_modal.html +++ b/web/html/common/qrcode_modal.html @@ -10,7 +10,7 @@ <a-tag color="purple" class="qr-tag"><span>{{ i18n "pages.settings.subSettings"}}</span></a-tag> <tr-qr-bg class="qr-bg-sub"> <tr-qr-bg-inner class="qr-bg-sub-inner"> - <canvas @click="copyToClipboard('qrCode-sub',genSubLink(qrModal.client.subId))" id="qrCode-sub" class="qr-cv"></canvas> + <canvas @click="copyToClipboard(genSubLink(qrModal.client.subId))" id="qrCode-sub" class="qr-cv"></canvas> </tr-qr-bg-inner> </tr-qr-bg> </tr-qr-box> @@ -18,7 +18,7 @@ <a-tag color="purple" class="qr-tag"><span>{{ i18n "pages.settings.subSettings"}} Json</span></a-tag> <tr-qr-bg class="qr-bg-sub"> <tr-qr-bg-inner class="qr-bg-sub-inner"> - <canvas @click="copyToClipboard('qrCode-subJson',genSubJsonLink(qrModal.client.subId))" id="qrCode-subJson" class="qr-cv"></canvas> + <canvas @click="copyToClipboard(genSubJsonLink(qrModal.client.subId))" id="qrCode-subJson" class="qr-cv"></canvas> </tr-qr-bg-inner> </tr-qr-bg> </tr-qr-box> @@ -27,7 +27,7 @@ <tr-qr-box class="qr-box"> <a-tag color="green" class="qr-tag"><span>[[ row.remark ]]</span></a-tag> <tr-qr-bg class="qr-bg"> - <canvas @click="copyToClipboard('qrCode-'+index, row.link)" :id="'qrCode-'+index" class="qr-cv"></canvas> + <canvas @click="copyToClipboard(row.link)" :id="'qrCode-'+index" class="qr-cv"></canvas> </tr-qr-bg> </tr-qr-box> </template> @@ -41,7 +41,6 @@ dbInbound: new DBInbound(), client: null, qrcodes: [], - clipboard: null, visible: false, subId: '', show: function(title = '', dbInbound, client) { @@ -79,14 +78,10 @@ qrModal: qrModal, }, methods: { - copyToClipboard(elementId, content) { - this.qrModal.clipboard = new ClipboardJS('#' + elementId, { - text: () => content, - }); - this.qrModal.clipboard.on('success', () => { + copyToClipboard(content) { + return copyToClipboard(content).then(() => { app.$message.success('{{ i18n "copied" }}') - this.qrModal.clipboard.destroy(); - }); + }) }, setQrCode(elementId, content) { new QRious({ diff --git a/web/html/common/text_modal.html b/web/html/common/text_modal.html index d668c792..36589199 100644 --- a/web/html/common/text_modal.html +++ b/web/html/common/text_modal.html @@ -7,7 +7,7 @@ :href="'data:application/text;charset=utf-8,' + encodeURIComponent(txtModal.content)" :download="txtModal.fileName">[[ txtModal.fileName ]] </a-button> - <a-button type="primary" id="copy-btn">{{ i18n "copy" }}</a-button> + <a-button type="primary" @click="txtModal.copy(txtModal.content)">{{ i18n "copy" }}</a-button> </template> <a-input style="overflow-y: auto;" type="textarea" v-model="txtModal.content" :autosize="{ minRows: 10, maxRows: 20}"></a-input> @@ -20,24 +20,18 @@ content: '', fileName: '', qrcode: null, - clipboard: null, visible: false, show: function (title = '', content = '', fileName = '') { this.title = title; this.content = content; this.fileName = fileName; this.visible = true; - textModalApp.$nextTick(() => { - if (this.clipboard === null) { - this.clipboard = new ClipboardJS('#copy-btn', { - text: () => this.content, - }); - this.clipboard.on('success', () => { - app.$message.success('{{ i18n "copied" }}') - this.close(); - }); - } - }); + }, + copy: function (content = '') { + copyToClipboard(content).then(() => { + app.$message.success('{{ i18n "copied" }}') + this.close(); + }) }, close: function () { this.visible = false; diff --git a/web/html/xui/inbound_info_modal.html b/web/html/xui/inbound_info_modal.html index d9913f32..f95461e2 100644 --- a/web/html/xui/inbound_info_modal.html +++ b/web/html/xui/inbound_info_modal.html @@ -258,7 +258,7 @@ <tr-info-title class="tr-info-title"> <a-tag color="purple">Subscription Link</a-tag> <a-tooltip title='{{ i18n "copy" }}'> - <a-button size="small" icon="snippets" id="copy-sub-link" @click="copyToClipboard('copy-sub-link', infoModal.subLink)"></a-button> + <a-button size="small" icon="snippets" @click="copyToClipboard(infoModal.subLink)"></a-button> </a-tooltip> </tr-info-title> <a :href="[[ infoModal.subLink ]]" target="_blank">[[ infoModal.subLink ]]</a> @@ -267,7 +267,7 @@ <tr-info-title class="tr-info-title"> <a-tag color="purple">Json Link</a-tag> <a-tooltip title='{{ i18n "copy" }}'> - <a-button size="small" icon="snippets" id="copy-subJson-link" @click="copyToClipboard('copy-subJson-link', infoModal.subJsonLink)"></a-button> + <a-button size="small" icon="snippets" @click="copyToClipboard(infoModal.subJsonLink)"></a-button> </a-tooltip> </tr-info-title> <a :href="[[ infoModal.subJsonLink ]]" target="_blank">[[ infoModal.subJsonLink ]]</a> @@ -279,7 +279,7 @@ <tr-info-title class="tr-info-title"> <a-tag color="blue">[[ infoModal.clientSettings.tgId ]]</a-tag> <a-tooltip title='{{ i18n "copy" }}'> - <a-button size="small" icon="snippets" id="copy-tg-link" @click="copyToClipboard('copy-tg-link', infoModal.clientSettings.tgId)"></a-button> + <a-button size="small" icon="snippets" @click="copyToClipboard(infoModal.clientSettings.tgId)"></a-button> </a-tooltip> </tr-info-title> </tr-info-row> @@ -290,7 +290,7 @@ <tr-info-title class="tr-info-title"> <a-tag class="tr-info-tag" color="green">[[ link.remark ]]</a-tag> <a-tooltip title='{{ i18n "copy" }}'> - <a-button style="min-width: 24px;" size="small" icon="snippets" :id="'copy-url-link-'+index" @click="copyToClipboard('copy-url-link-'+index, link.link)"></a-button> + <a-button style="min-width: 24px;" size="small" icon="snippets" @click="copyToClipboard(link.link)"></a-button> </a-tooltip> </tr-info-title> <code>[[ link.link ]]</code> @@ -304,7 +304,7 @@ <tr-info-title class="tr-info-title"> <a-tag class="tr-info-tag" color="green">[[ link.remark ]]</a-tag> <a-tooltip title='{{ i18n "copy" }}'> - <a-button style="min-width: 24px;" size="small" icon="snippets" :id="'copy-url-link-'+index" @click="copyToClipboard('copy-url-link-'+index, link.link)"></a-button> + <a-button style="min-width: 24px;" size="small" icon="snippets" @click="copyToClipboard(link.link)"></a-button> </a-tooltip> </tr-info-title> <code>[[ link.link ]]</code> @@ -431,7 +431,7 @@ <tr-info-title class="tr-info-title"> <a-tag color="blue">Config</a-tag> <a-tooltip title='{{ i18n "copy" }}'> - <a-button style="min-width: 24px;" size="small" icon="snippets" :id="'copy-url-link-'+index" @click="copyToClipboard('copy-url-link-'+index, infoModal.links[index])"></a-button> + <a-button style="min-width: 24px;" size="small" icon="snippets" @click="copyToClipboard(infoModal.links[index])"></a-button> </a-tooltip> </tr-info-title> <div v-html="infoModal.links[index].replaceAll(`\n`,`<br />`)" style="border-radius: 1rem; padding: 0.5rem;" class="client-table-odd-row"> @@ -464,7 +464,6 @@ clientStats: [], upStats: 0, downStats: 0, - clipboard: null, links: [], index: null, isExpired: false, @@ -533,14 +532,10 @@ }, }, methods: { - copyToClipboard(elementId, content) { - this.infoModal.clipboard = new ClipboardJS('#' + elementId, { - text: () => content, - }); - this.infoModal.clipboard.on('success', () => { + copyToClipboard(content) { + return copyToClipboard(content).then(() => { app.$message.success('{{ i18n "copied" }}') - this.infoModal.clipboard.destroy(); - }); + }) }, statsColor(stats) { return usageColor(stats.up + stats.down, app.trafficDiff, stats.total); diff --git a/web/html/xui/inbounds.html b/web/html/xui/inbounds.html index 89a37a29..86890926 100644 --- a/web/html/xui/inbounds.html +++ b/web/html/xui/inbounds.html @@ -546,7 +546,6 @@ {{template "js" .}} <script src="{{ .base_path }}assets/base64/base64.min.js"></script> <script src="{{ .base_path }}assets/qrcode/qrious2.min.js?{{ .cur_ver }}"></script> -<script src="{{ .base_path }}assets/clipboard/clipboard.min.js?{{ .cur_ver }}"></script> <script src="{{ .base_path }}assets/uri/URI.min.js?{{ .cur_ver }}"></script> <script src="{{ .base_path }}assets/js/model/inbound.js?{{ .cur_ver }}"></script> <script src="{{ .base_path }}assets/js/model/dbinbound.js?{{ .cur_ver }}"></script> diff --git a/web/html/xui/index.html b/web/html/xui/index.html index 2e57277a..8f89da90 100644 --- a/web/html/xui/index.html +++ b/web/html/xui/index.html @@ -332,7 +332,6 @@ </a-modal> </a-layout> {{template "js" .}} -<script src="{{ .base_path }}assets/clipboard/clipboard.min.js?{{ .cur_ver }}"></script> {{template "component/themeSwitcher" .}} {{template "textModal"}} <script> |
