diff --git a/static/css/global.css b/static/css/global.css index 64360ae1..b80fe0f7 100644 --- a/static/css/global.css +++ b/static/css/global.css @@ -802,3 +802,24 @@ div.screenshotviewer-caption { .options-menu-widget.open { color:#fff; } + +.click-to-copy { + cursor: pointer; +} + +.fade-out { + opacity: 0; + pointer-events: none; + transition: opacity 1s; + transition-timing-function: ease-in; +} + +.hidden-element { + height: 0; + left: 0; + opacity: 0; + padding: 0; + pointer-events: none; + position: fixed; + top: 1px; +} diff --git a/static/js/basic.js b/static/js/basic.js index 4163d42a..05d30daf 100644 --- a/static/js/basic.js +++ b/static/js/basic.js @@ -2077,6 +2077,12 @@ $WH.Tooltip = { $WH.Tooltip.move(x, y, 0, 0, paddX, paddY); }, + showFadingTooltipAtCursor: function (text, ev, className, noWrap, maxWidth) { + text = $WH.Tooltip.prepareTooltipHtml(text, noWrap, maxWidth, ev); + $WH.Tooltip.showAtCursor(ev, text, undefined, undefined, className); + requestAnimationFrame(function () { $WH.Tooltip.tooltip.classList.add('fade-out'); }); + }, + cursorUpdate: function(e, x, y) { // Used along with showAtCursor if ($WH.Tooltip.disabled || !$WH.Tooltip.tooltip) { return; @@ -2126,12 +2132,42 @@ $WH.Tooltip = { $WH.Tooltip.iconVisible = icon ? 1 : 0; }, - simple: function(element, text, className, fixed) { + prepareTooltipHtml: function (textOrFn, noWrap, maxWidth, ev) { + textOrFn = typeof textOrFn === "function" ? textOrFn.call(ev.target, ev) : textOrFn; + if (typeof textOrFn === "string") { + if (noWrap === undefined && textOrFn.length < 30) + noWrap = true; + + let attr = []; + if (noWrap) + attr.push(' class="no-wrap"'); + + if (maxWidth && !isNaN(maxWidth)) + attr.push(' style="max-width:' + maxWidth + 'px"'); + + if (attr.length) + textOrFn = "" + textOrFn + ""; + } + + return textOrFn; + }, + + simple: function(element, textOrFn, className, fixed) { if (fixed) - element.onmouseover = function(x) { $WH.Tooltip.show(element, text, false, false, className) }; + { + element.onmouseover = function(ev) + { + let text = $WH.Tooltip.prepareTooltipHtml(textOrFn, null, null, ev); + $WH.Tooltip.show(element, text, false, false, className); + }; + } else { - element.onmouseover = function(x) { $WH.Tooltip.showAtCursor(x, text, false, false, className) }; + element.onmouseover = function(ev) + { + let text = $WH.Tooltip.prepareTooltipHtml(textOrFn, null, null, ev); + $WH.Tooltip.showAtCursor(ev, text, false, false, className); + }; element.onmousemove = $WH.Tooltip.cursorUpdate; } diff --git a/static/js/global.js b/static/js/global.js index 574eaa0b..cead8fe9 100644 --- a/static/js/global.js +++ b/static/js/global.js @@ -5346,32 +5346,23 @@ function Listview(opt) { id: 'debug-id', compute: function(data, td) { if (data.id) { - $WH.ae(td, $WH.ct(data.id)); + let pre = $WH.ce('pre', { style: { display: 'inline', margin: '0' }}, $WH.ct(data.id)); + $WH.clickToCopy(pre); + $WH.ae(td, pre); } }, getVisibleText: function(data) { - if (data.id) { - return data.id; - } - else { - return ''; - } + return data.id || ''; }, getValue: function(data) { - if (data.id) { - return data.id; - } - else { - return 0; - } + return data.id || 0; }, sortFunc: function(a, b, col) { - if (a.id == null) { + if (a.id == null) return -1; - } - else if (b.id == null) { + + if (b.id == null) return 1; - } return $WH.strcmp(a.id, b.id); }, @@ -5379,19 +5370,17 @@ function Listview(opt) { width: '5%', tooltip: 'ID' }); + this.visibility.splice(0, 0, -1); - for (var i = 0, len = this.visibility.length; i < len; ++i) { + for (var i = 0, len = this.visibility.length; i < len; ++i) this.visibility[i] = this.visibility[i] + 1; - } for (var i = 0, len = this.sort.length; i < len; ++i) { - if (this.sort[i] < 0) { + if (this.sort[i] < 0) this.sort[i] = this.sort[i] - 1; - } - else { + else this.sort[i] = this.sort[i] + 1; - } } } @@ -22637,6 +22626,114 @@ function CreateAjaxLoader() { } +$WH.clickToCopy = function (el, textOrFn, opt) { + opt = opt || {}; + + $WH.aE(el, 'click', $WH.clickToCopy.copy.bind(null, el, textOrFn, opt)); + // $WH.preventSelectStart(el); + + el.classList.add('click-to-copy'); + + if (opt.modifyTooltip) { + el._fixTooltip = function (e) { + return e + '
' + $WH.ce('span', { className: 'q2', innerHTML: $WH.clickToCopy.getTooltip(false, opt) }).outerHTML; + }; + + opt.overrideOtherTooltips = false; + } + + // Aowow - fitted to old system + // $WH.Tooltips.attach( + $WH.Tooltip.simple( + el, + $WH.clickToCopy.getTooltip.bind(null, false, opt), + undefined, + // { + /* byCursor: */ !opt.attachToElement, + // stopPropagation: opt.overrideOtherTooltips + // } + ); +}; +$WH.clickToCopy.copy = function (el, textOrFn, opt, ev) { + ev.preventDefault(); + ev.stopPropagation(); + + if (textOrFn === undefined) { + if (!el.childNodes[0] || !el.childNodes[0].textContent) { + let text = 'Could not find text to copy.'; + // $WH.error(text, el); + + if (opt.attachToElement) + $WH.Tooltip.show(el, text, 'q10'); + else + $WH.Tooltip.showAtCursor(ev, text, 'q10'); + + return; + } + + textOrFn = el.childNodes[0].textContent; + } + else if (typeof textOrFn === 'function') + textOrFn = textOrFn(); + + $WH.copyToClipboard(textOrFn); + + if (opt.attachToElement) + $WH.Tooltip.show(el, $WH.clickToCopy.getTooltip(true, opt)); + else + $WH.Tooltip.showAtCursor(ev, $WH.clickToCopy.getTooltip(true, opt)); +}; +$WH.clickToCopy.getTooltip = function (clicked, opt) { + let txt = ''; + let attr = undefined; + if (clicked) { + txt = ' ' + LANG.copied; + attr = { className: 'q1 icon-tick' }; + } + else + txt = LANG.clickToCopy; + + let tt = $WH.ce('div', attr, $WH.ct(txt)); + + if (opt.prefix) { + tt.style.marginTop = '10px'; + let prefix = typeof opt.prefix === 'function' ? opt.prefix() : opt.prefix; + return prefix + tt.outerHTML; + } + + return tt.outerHTML; +}; +$WH.copyToClipboard = function (text, t) { + if (!$WH.copyToClipboard.hiddenInput) { + $WH.copyToClipboard.hiddenInput = $WH.ce('textarea', { className: 'hidden-element' }); + $WH.ae(document.body, $WH.copyToClipboard.hiddenInput); + } + + $WH.copyToClipboard.hiddenInput.value = text; + + let isEmpty = $WH.copyToClipboard.hiddenInput.value === ''; + if (isEmpty) + $WH.copyToClipboard.hiddenInput.value = LANG.nothingToCopy_tip; + + $WH.copyToClipboard.hiddenInput.focus(); + $WH.copyToClipboard.hiddenInput.select(); + + if (!document.execCommand('copy')) + prompt(null, text); + + $WH.copyToClipboard.hiddenInput.blur(); + + if (t) { + if (isEmpty) + $WH.Tooltips.showFadingTooltipAtCursor(LANG.nothingToCopy_tip, t, 'q10'); + else { + let e = $WH.ce('span', { className: 'q1 icon-tick' }, $WH.ct(' ' + LANG.copied)); + $WH.Tooltips.showFadingTooltipAtCursor(e.outerHTML, t) + } + } +}; + + /* Global utility functions related to arrays, format validation, regular expressions, and strings */ diff --git a/static/js/locale_dede.js b/static/js/locale_dede.js index 83692e30..1564acb3 100644 --- a/static/js/locale_dede.js +++ b/static/js/locale_dede.js @@ -4896,6 +4896,12 @@ var LANG = { /* AoWoW: start custom */ + // click to copy fn + copied: 'Kopiert', + clickToCopy: 'Klicke zum Kopieren', + nothingToCopy_tip: 'Nichts zu kopieren!', + + // TC conditions display tab_conditions: 'Konditionen', tab_condition_for: 'Kondition für', cnd_either: 'Entweder', diff --git a/static/js/locale_enus.js b/static/js/locale_enus.js index eda14775..012bb207 100644 --- a/static/js/locale_enus.js +++ b/static/js/locale_enus.js @@ -4944,6 +4944,12 @@ var LANG = { /* AoWoW: start custom */ + // click to copy fn + copied: 'Copied', + clickToCopy: 'Click to Copy', + nothingToCopy_tip: 'Nothing to copy!', + + // TC conditions display tab_conditions: 'Conditions', tab_condition_for: 'Condition for', cnd_either: 'Either', diff --git a/static/js/locale_eses.js b/static/js/locale_eses.js index da7e06bc..d9f78932 100644 --- a/static/js/locale_eses.js +++ b/static/js/locale_eses.js @@ -4898,6 +4898,12 @@ var LANG = { /* AoWoW: start custom */ + // click to copy fn + copied: 'Copiado', + clickToCopy: 'Click para copiar', + nothingToCopy_tip: '[Nothing to copy!]', + + // TC conditions display tab_conditions: 'Condiciones', tab_condition_for: 'Condición para', cnd_either: 'Cualquiera', diff --git a/static/js/locale_frfr.js b/static/js/locale_frfr.js index fa562af5..a27b4eae 100644 --- a/static/js/locale_frfr.js +++ b/static/js/locale_frfr.js @@ -4898,6 +4898,12 @@ var LANG = { /* AoWoW: start custom */ + // click to copy fn + copied: 'Copié', + clickToCopy: 'Cliquer pour Copier', + nothingToCopy_tip: 'Rien à copier !', + + // TC conditions display tab_conditions: '[Conditions]', tab_condition_for: '[Condition for]', cnd_either: '[Either]', diff --git a/static/js/locale_ruru.js b/static/js/locale_ruru.js index b2aea8e5..6825f61d 100644 --- a/static/js/locale_ruru.js +++ b/static/js/locale_ruru.js @@ -4900,6 +4900,12 @@ var LANG = { /* AoWoW: start custom */ + // click to copy fn + copied: 'Скопировано', + clickToCopy: 'Нажмите, чтобы скопировать', + nothingToCopy_tip: 'Нет данных для копирования!', + + // TC conditions display tab_conditions: '[Conditions]', tab_condition_for: '[Condition for]', cnd_either: '[Either]', diff --git a/static/js/locale_zhcn.js b/static/js/locale_zhcn.js index d6a21db6..b5bce119 100644 --- a/static/js/locale_zhcn.js +++ b/static/js/locale_zhcn.js @@ -4923,6 +4923,12 @@ var LANG = { /* AoWoW: start custom */ + // click to copy fn + copied: '已复制', + clickToCopy: '点击复制', + nothingToCopy_tip: '[Nothing to copy!]', + + // TC conditions display tab_conditions: '[Conditions]', tab_condition_for: '[Condition for]', cnd_either: '[Either]',