zhangnaisong
2023-08-05 24d66c8d82b628a06e93dbb1abfea2049b3d45ab
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import Emitter from 'tiny-emitter';
import listen from 'good-listener';
import ClipboardAction from './clipboard-action';
 
/**
 * Helper function to retrieve attribute value.
 * @param {String} suffix
 * @param {Element} element
 */
function getAttributeValue(suffix, element) {
  const attribute = `data-clipboard-${suffix}`;
 
  if (!element.hasAttribute(attribute)) {
    return;
  }
 
  return element.getAttribute(attribute);
}
 
/**
 * Base class which takes one or more elements, adds event listeners to them,
 * and instantiates a new `ClipboardAction` on each click.
 */
class Clipboard extends Emitter {
  /**
   * @param {String|HTMLElement|HTMLCollection|NodeList} trigger
   * @param {Object} options
   */
  constructor(trigger, options) {
    super();
 
    this.resolveOptions(options);
    this.listenClick(trigger);
  }
 
  /**
   * Defines if attributes would be resolved using internal setter functions
   * or custom functions that were passed in the constructor.
   * @param {Object} options
   */
  resolveOptions(options = {}) {
    this.action =
      typeof options.action === 'function'
        ? options.action
        : this.defaultAction;
    this.target =
      typeof options.target === 'function'
        ? options.target
        : this.defaultTarget;
    this.text =
      typeof options.text === 'function' ? options.text : this.defaultText;
    this.container =
      typeof options.container === 'object' ? options.container : document.body;
  }
 
  /**
   * Adds a click event listener to the passed trigger.
   * @param {String|HTMLElement|HTMLCollection|NodeList} trigger
   */
  listenClick(trigger) {
    this.listener = listen(trigger, 'click', (e) => this.onClick(e));
  }
 
  /**
   * Defines a new `ClipboardAction` on each click event.
   * @param {Event} e
   */
  onClick(e) {
    const trigger = e.delegateTarget || e.currentTarget;
 
    if (this.clipboardAction) {
      this.clipboardAction = null;
    }
 
    this.clipboardAction = new ClipboardAction({
      action: this.action(trigger),
      target: this.target(trigger),
      text: this.text(trigger),
      container: this.container,
      trigger,
      emitter: this,
    });
  }
 
  /**
   * Default `action` lookup function.
   * @param {Element} trigger
   */
  defaultAction(trigger) {
    return getAttributeValue('action', trigger);
  }
 
  /**
   * Default `target` lookup function.
   * @param {Element} trigger
   */
  defaultTarget(trigger) {
    const selector = getAttributeValue('target', trigger);
 
    if (selector) {
      return document.querySelector(selector);
    }
  }
 
  /**
   * Returns the support of the given action, or all actions if no action is
   * given.
   * @param {String} [action]
   */
  static isSupported(action = ['copy', 'cut']) {
    const actions = typeof action === 'string' ? [action] : action;
    let support = !!document.queryCommandSupported;
 
    actions.forEach((action) => {
      support = support && !!document.queryCommandSupported(action);
    });
 
    return support;
  }
 
  /**
   * Default `text` lookup function.
   * @param {Element} trigger
   */
  defaultText(trigger) {
    return getAttributeValue('text', trigger);
  }
 
  /**
   * Destroy lifecycle.
   */
  destroy() {
    this.listener.destroy();
 
    if (this.clipboardAction) {
      this.clipboardAction.destroy();
      this.clipboardAction = null;
    }
  }
}
 
export default Clipboard;