Fossil SCM

fossil-scm / src / fossil.copybutton.js
Blame History Raw 129 lines
1
(function(F/*fossil object*/){
2
/**
3
A basic API for creating and managing a copy-to-clipboard button.
4
5
Requires: fossil.bootstrap, fossil.dom
6
*/
7
const D = F.dom;
8
9
/**
10
Initializes element e as a copy button using the given options
11
object.
12
13
The first argument may be a DOM element or a string (CSS selector
14
suitable for use with document.querySelector()).
15
16
Options:
17
18
.copyFromElement: DOM element
19
20
.copyFromId: DOM element ID
21
22
.extractText: optional callback which is triggered when the copy
23
button is clicked. It must return the text to copy to the
24
clipboard. The default is to extract it from the copy-from
25
element, using its [value] member, if it has one, else its
26
[innerText]. A client-provided callback may use any data source
27
it likes, so long as it's synchronous. If this function returns a
28
falsy value then the clipboard is not modified. This function is
29
called with the fully expanded/resolved options object as its
30
"this" (that's a different instance than the one passed to this
31
function!).
32
33
At least one of copyFromElement, copyFromId, or extractText must
34
be provided, but if copyFromId is not set and e.dataset.copyFromId
35
is then that value is used in its place. extractText() trumps the
36
other two options.
37
38
.cssClass: optional CSS class, or list of classes, to apply to e.
39
40
.style: optional object of properties to copy directly into
41
e.style.
42
43
.oncopy: an optional callback function which is added as an event
44
listener for the 'text-copied' event (see below). There is
45
functionally no difference from setting this option or adding a
46
'text-copied' event listener to the element, and this option is
47
considered to be a convenience form of that.
48
49
Note that this function's own defaultOptions object holds default
50
values for some options. Any changes made to that object affect
51
any future calls to this function.
52
53
Be aware that clipboard functionality might or might not be
54
available in any given environment. If this button appears to
55
have no effect, that may be because it is not enabled/available
56
in the current platform.
57
58
The copy button emits custom event 'text-copied' after it has
59
successfully copied text to the clipboard. The event's "detail"
60
member is an object with a "text" property holding the copied
61
text. Other properties may be added in the future. The event is
62
not fired if copying to the clipboard fails (e.g. is not
63
available in the current environment).
64
65
The copy button's click handler is suppressed (becomes a no-op)
66
for as long as the element has the "disabled" attribute.
67
68
Returns the copy-initialized element.
69
70
Example:
71
72
const button = fossil.copyButton('#my-copy-button', {
73
copyFromId: 'some-other-element-id'
74
});
75
button.addEventListener('text-copied',function(ev){
76
console.debug("Copied text:",ev.detail.text);
77
});
78
*/
79
F.copyButton = function f(e, opt){
80
if('string'===typeof e){
81
e = document.querySelector(e);
82
}
83
opt = F.mergeLastWins(f.defaultOptions, opt);
84
if(opt.cssClass){
85
D.addClass(e, opt.cssClass);
86
}
87
var srcId, srcElem;
88
if(opt.copyFromElement){
89
srcElem = opt.copyFromElement;
90
}else if((srcId = opt.copyFromId || e.dataset.copyFromId)){
91
srcElem = document.querySelector('#'+srcId);
92
}
93
const extract = opt.extractText || (
94
undefined===srcElem.value ? ()=>srcElem.innerText : ()=>srcElem.value
95
);
96
D.copyStyle(e, opt.style);
97
e.addEventListener(
98
'click',
99
function(ev){
100
ev.preventDefault();
101
ev.stopPropagation();
102
if(e.disabled) return; /* This check is probably redundant. */
103
const txt = extract.call(opt);
104
if(txt && D.copyTextToClipboard(txt)){
105
e.dispatchEvent(new CustomEvent('text-copied',{
106
detail: {text: txt}
107
}));
108
}
109
},
110
false
111
);
112
if('function' === typeof opt.oncopy){
113
e.addEventListener('text-copied', opt.oncopy, false);
114
}
115
/* Make sure the <button> contains a single nested <span>. */
116
if(e.childElementCount!=1 || e.firstChild.tagName!='SPAN'){
117
D.append(D.clearElement(e), D.span());
118
}
119
return e;
120
};
121
122
F.copyButton.defaultOptions = {
123
cssClass: 'copy-button',
124
oncopy: undefined,
125
style: {/*properties copied as-is into element.style*/}
126
};
127
128
})(window.fossil);
129

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button