Compare commits

...

45 Commits

Author SHA1 Message Date
Zeno Rocha
b6e6b80ab0 Release v1.7.1 2017-05-29 16:57:52 -07:00
Patrick H. Lauke
a55c9ac513 Move focus to trigger instead of simply blur()ing (#419)
blur() results in a loss/reset of keyboard focus, meaning keyboard users usually get thrown right back to the start of the page. Instead, this moves focus back to the trigger (which had the focus when the trigger was activated).
As with the proposed fix in https://github.com/zenorocha/clipboard.js/pull/418 this obviously results in the focus styles (like the default outline, unless suppressed) being applied to the trigger (see the related PR for rationale and future fix using `:focus-ring`)
2017-05-29 16:54:55 -07:00
Zeno Rocha
39e622456c Release v1.7.0 2017-05-29 14:43:41 -07:00
Zeno Rocha
f8c322f163 Reviews text from #368 2017-05-29 14:20:36 -07:00
Peder Johnsen
f42b57067d Container option (#368)
* Allow container option to fix bugs related to bootstrap modals etc.

* Updated readme to reflect addition of container option

* Name link

* Removed test log

* Remove unwanted whitespace

* Refactored description
2017-05-29 14:17:26 -07:00
Zeno Rocha
4065080a17 Removes bower notes from docs 2017-05-22 13:31:36 -07:00
Gabriel Kalani
5ab50475e0 ES6 refactor (#409)
* little fix

* little fix /2

* test/clipboard.js refactored

* emitter: emitter --> emitter

* Examples in ES6

* es6

* back to original code

* script > npm test

* script > npm test not necessary

* updating modules

* removing export default
2017-05-02 21:34:18 -07:00
Zeno Rocha
e1394b3b8c Adds bonus section 2017-05-01 09:17:35 -07:00
Zeno Rocha
f59d4e6b4d Release v1.6.1 2017-02-28 09:58:40 -08:00
Zeno Rocha
9ddff7e591 Prevents Node env from throwing an error 2017-02-28 09:57:36 -08:00
Zeno Rocha
a00f1fe327 Release v1.6.0 2017-02-08 00:02:14 -08:00
Zeno Rocha
38ae5b34f3 Adds documentation about #355 2017-02-07 23:59:27 -08:00
Zeno Rocha
41b7234d50 Prevents keyboard from opening on iOS when using "data-clipboard-target" 2017-02-07 23:41:46 -08:00
Zeno Rocha
3696739e5e Prevents scroll jump on iOS when using "data-clipboard-text"
Thanks to @geraldarthur at #369
2017-02-07 23:37:43 -08:00
Itai Steinherz
63d1b0f014 Add isSupported method #355 2017-02-07 23:36:29 -08:00
Zeno Rocha
402c9ee17b Release v1.5.16 2016-12-12 11:54:35 -02:00
Zeno Rocha
0538f6e212 Ensures that event delegation works with multiple documents (a page with iframes) 2016-12-12 11:53:41 -02:00
Zeno Rocha
8ad16a2c6c Release v1.5.15 2016-10-22 19:44:43 -07:00
Zeno Rocha
ce0829054b Updates dev dependencies 2016-10-22 19:37:37 -07:00
Zeno Rocha
223d30c110 Removes babel loose mode 2016-10-22 19:37:18 -07:00
Zeno Rocha
b1a68df6e9 Release v1.5.14 2016-10-22 19:36:49 -07:00
Zeno Rocha
0149e1de5e Updates good-listener package which removes two dependencies 2016-10-22 19:10:44 -07:00
Zeno Rocha
26a9e9d56c Release v1.5.13 2016-10-16 22:44:03 -07:00
Zeno Rocha
fce625f151 Fixes #320 2016-10-16 18:53:34 -07:00
Zeno Rocha
f7040bae8a Adds meta viewport for all demos 2016-10-16 17:16:02 -07:00
Zeno Rocha
9f9d03c927 Adds Edge version #316 2016-09-26 13:57:28 -05:00
Cătălin Mariș
e18c26ae07 Add Edge to the Browser Support section (#316)
Ref: https://github.com/zenorocha/clipboard.js/pull/267#issuecomment-249620994
2016-09-26 13:48:16 -05:00
Zeno Rocha
70cfabec69 Update readme.md 2016-09-21 13:21:25 -07:00
Zeno Rocha
f700a1b12e Moves URLs to HTTPS 2016-09-21 11:42:37 -07:00
Zeno Rocha
9e3d662c4e Adjusts text based on #313 2016-09-21 11:04:07 -07:00
JY Kim
76b907949c issue #282 solution (#283)
* add selectedText

* better code

* remove dist folder

* Revert "remove dist folder"

This reverts commit 50e726c7a7.

* orogin dist source
2016-09-08 08:57:41 -07:00
Zeno Rocha
60b6887100 Updates site preview 2016-06-17 14:49:39 -07:00
Zeno Rocha
eb7418b51b Release v1.5.12 2016-06-09 07:57:20 -07:00
Alexander
869c4e3219 Fix bug that unable to remove fake event listener (#256)
* Fix bug that unable to remove fake event listener

* Update dist
2016-06-09 07:54:07 -07:00
Zeno Rocha
a4ab305297 Release v1.5.11 2016-06-08 21:46:20 -07:00
Zeno Rocha
294e9fcb5d Fix fake element position from "fixed" to "absolute" #194 #250 2016-06-08 21:44:54 -07:00
Ali Bozorgkhan
79c3361ca4 Press Ctrl+C to copy -> Press Command+C to copy (#252)
As Safari users are mostly using mac, coping a text needs Command+C instead of Ctrl+C
2016-06-02 15:28:16 -07:00
grabus
c3fefc1fc0 Fix memory leak because of fakeHandlers (#243)
addEventListener always returns undefined by spec
https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget-addEventListener
2016-05-26 11:35:18 -07:00
Zeno Rocha
42bd266f0b Adds basic JSFiddle to help people reproduce their bugs 2016-05-01 16:14:51 -07:00
Zeno Rocha
53a733fcb5 Release v1.5.10 2016-04-03 22:33:02 -07:00
Zeno Rocha
7a5a910bcd Removes error message when target/text attributes are null, undefined, or false 2016-04-03 22:16:22 -07:00
Zeno Rocha
0163f7cb72 Merge pull request #216 from sebastianekstrom/master
Use const instead of let when applicable
2016-04-01 08:48:49 -07:00
Sebastian Ekström
941bdbb16e Use const instead of let when applicable 2016-04-01 13:17:12 +02:00
Zeno Rocha
00d5cc4e96 Updates tagline #210 2016-03-17 09:12:23 -07:00
Zeno Rocha
07a1f8456b Throws error when disabled or readonly attributes are used on target element
Fixes #41
2016-03-01 15:38:25 -08:00
20 changed files with 439 additions and 347 deletions

View File

@@ -1,4 +1,4 @@
{
"presets": ["es2015-loose"],
"presets": ["es2015"],
"plugins": ["transform-es2015-modules-umd"]
}

View File

@@ -1,3 +1,7 @@
### Minimal example
> Fork this [JSFiddle](https://jsfiddle.net/zenorocha/5kk0eysw/) and reproduce your issue.
### Expected behaviour
I thought that by going to the page '...' and pressing the button '...' then '...' would happen.
@@ -6,14 +10,6 @@ I thought that by going to the page '...' and pressing the button '...' then '..
Instead of '...', what I saw was that '...' happened instead.
### Steps to reproduce
1. Go this JSFiddle, JSBin, CodePen, etc
2. Click on '....'
3. Scroll down to '....'
4. Refresh the page and wait 5 secs
5. Finally the error will magically happen (if it is raining)
### Browsers affected
I tested on all major browsers and only IE 11 does not work.

View File

@@ -1,6 +1,6 @@
{
"name": "clipboard",
"version": "1.5.9",
"version": "1.7.1",
"description": "Modern copy to clipboard. No Flash. Just 2kb",
"license": "MIT",
"main": "dist/clipboard.js",

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<title>constructor-node</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<!-- 1. Define some markup -->

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<title>constructor-nodelist</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<!-- 1. Define some markup -->

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<title>constructor-selector</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<!-- 1. Define some markup -->

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<title>function-target</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<!-- 1. Define some markup -->

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<title>function-text</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<!-- 1. Define some markup -->

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<title>target-div</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<!-- 1. Define some markup -->

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<title>target-input</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<!-- 1. Define some markup -->

View File

@@ -3,6 +3,7 @@
<head>
<meta charset="UTF-8">
<title>target-textarea</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<!-- 1. Define some markup -->

482
dist/clipboard.js vendored
View File

@@ -1,23 +1,46 @@
/*!
* clipboard.js v1.5.9
* clipboard.js v1.7.1
* https://zenorocha.github.io/clipboard.js
*
* Licensed MIT © Zeno Rocha
*/
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Clipboard = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var matches = require('matches-selector')
module.exports = function (element, selector, checkYoSelf) {
var parent = checkYoSelf ? element : element.parentNode
while (parent && parent !== document) {
if (matches(parent, selector)) return parent;
parent = parent.parentNode
}
}
var DOCUMENT_NODE_TYPE = 9;
},{"matches-selector":5}],2:[function(require,module,exports){
var closest = require('closest');
/**
* A polyfill for Element.matches()
*/
if (typeof Element !== 'undefined' && !Element.prototype.matches) {
var proto = Element.prototype;
proto.matches = proto.matchesSelector ||
proto.mozMatchesSelector ||
proto.msMatchesSelector ||
proto.oMatchesSelector ||
proto.webkitMatchesSelector;
}
/**
* Finds the closest parent that matches a selector.
*
* @param {Element} element
* @param {String} selector
* @return {Function}
*/
function closest (element, selector) {
while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {
if (typeof element.matches === 'function' &&
element.matches(selector)) {
return element;
}
element = element.parentNode;
}
}
module.exports = closest;
},{}],2:[function(require,module,exports){
var closest = require('./closest');
/**
* Delegates event to a selector.
@@ -52,7 +75,7 @@ function delegate(element, selector, type, callback, useCapture) {
*/
function listener(element, selector, type, callback) {
return function(e) {
e.delegateTarget = closest(e.target, selector, true);
e.delegateTarget = closest(e.target, selector);
if (e.delegateTarget) {
callback.call(element, e);
@@ -62,7 +85,7 @@ function listener(element, selector, type, callback) {
module.exports = delegate;
},{"closest":1}],3:[function(require,module,exports){
},{"./closest":1}],3:[function(require,module,exports){
/**
* Check if argument is a HTML element.
*
@@ -211,54 +234,28 @@ function listenSelector(selector, type, callback) {
module.exports = listen;
},{"./is":3,"delegate":2}],5:[function(require,module,exports){
/**
* Element prototype.
*/
var proto = Element.prototype;
/**
* Vendor function.
*/
var vendor = proto.matchesSelector
|| proto.webkitMatchesSelector
|| proto.mozMatchesSelector
|| proto.msMatchesSelector
|| proto.oMatchesSelector;
/**
* Expose `match()`.
*/
module.exports = match;
/**
* Match `el` to `selector`.
*
* @param {Element} el
* @param {String} selector
* @return {Boolean}
* @api public
*/
function match(el, selector) {
if (vendor) return vendor.call(el, selector);
var nodes = el.parentNode.querySelectorAll(selector);
for (var i = 0; i < nodes.length; ++i) {
if (nodes[i] == el) return true;
}
return false;
}
},{}],6:[function(require,module,exports){
function select(element) {
var selectedText;
if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
if (element.nodeName === 'SELECT') {
element.focus();
selectedText = element.value;
}
else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
var isReadOnly = element.hasAttribute('readonly');
if (!isReadOnly) {
element.setAttribute('readonly', '');
}
element.select();
element.setSelectionRange(0, element.value.length);
if (!isReadOnly) {
element.removeAttribute('readonly');
}
selectedText = element.value;
}
else {
@@ -281,14 +278,14 @@ function select(element) {
module.exports = select;
},{}],7:[function(require,module,exports){
},{}],6:[function(require,module,exports){
function E () {
// Keep this empty so it's easier to inherit from
// Keep this empty so it's easier to inherit from
// (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
}
E.prototype = {
on: function (name, callback, ctx) {
on: function (name, callback, ctx) {
var e = this.e || (this.e = {});
(e[name] || (e[name] = [])).push({
@@ -349,7 +346,7 @@ E.prototype = {
module.exports = E;
},{}],8:[function(require,module,exports){
},{}],7:[function(require,module,exports){
(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(['module', 'select'], factory);
@@ -376,7 +373,7 @@ module.exports = E;
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj;
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
function _classCallCheck(instance, Constructor) {
@@ -407,7 +404,6 @@ module.exports = E;
/**
* @param {Object} options
*/
function ClipboardAction(options) {
_classCallCheck(this, ClipboardAction);
@@ -421,124 +417,126 @@ module.exports = E;
*/
ClipboardAction.prototype.resolveOptions = function resolveOptions() {
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
_createClass(ClipboardAction, [{
key: 'resolveOptions',
value: function resolveOptions() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
this.action = options.action;
this.emitter = options.emitter;
this.target = options.target;
this.text = options.text;
this.trigger = options.trigger;
this.action = options.action;
this.container = options.container;
this.emitter = options.emitter;
this.target = options.target;
this.text = options.text;
this.trigger = options.trigger;
this.selectedText = '';
};
ClipboardAction.prototype.initSelection = function initSelection() {
if (this.text && this.target) {
throw new Error('Multiple attributes declared, use either "target" or "text"');
} else if (this.text) {
this.selectFake();
} else if (this.target) {
this.selectTarget();
} else {
throw new Error('Missing required attributes, use either "target" or "text"');
this.selectedText = '';
}
};
ClipboardAction.prototype.selectFake = function selectFake() {
var _this = this;
var isRTL = document.documentElement.getAttribute('dir') == 'rtl';
this.removeFake();
this.fakeHandler = document.body.addEventListener('click', function () {
return _this.removeFake();
});
this.fakeElem = document.createElement('textarea');
// Prevent zooming on iOS
this.fakeElem.style.fontSize = '12pt';
// Reset box model
this.fakeElem.style.border = '0';
this.fakeElem.style.padding = '0';
this.fakeElem.style.margin = '0';
// Move element out of screen horizontally
this.fakeElem.style.position = 'fixed';
this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px';
// Move element to the same position vertically
this.fakeElem.style.top = (window.pageYOffset || document.documentElement.scrollTop) + 'px';
this.fakeElem.setAttribute('readonly', '');
this.fakeElem.value = this.text;
document.body.appendChild(this.fakeElem);
this.selectedText = (0, _select2.default)(this.fakeElem);
this.copyText();
};
ClipboardAction.prototype.removeFake = function removeFake() {
if (this.fakeHandler) {
document.body.removeEventListener('click');
this.fakeHandler = null;
}, {
key: 'initSelection',
value: function initSelection() {
if (this.text) {
this.selectFake();
} else if (this.target) {
this.selectTarget();
}
}
}, {
key: 'selectFake',
value: function selectFake() {
var _this = this;
if (this.fakeElem) {
document.body.removeChild(this.fakeElem);
this.fakeElem = null;
var isRTL = document.documentElement.getAttribute('dir') == 'rtl';
this.removeFake();
this.fakeHandlerCallback = function () {
return _this.removeFake();
};
this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true;
this.fakeElem = document.createElement('textarea');
// Prevent zooming on iOS
this.fakeElem.style.fontSize = '12pt';
// Reset box model
this.fakeElem.style.border = '0';
this.fakeElem.style.padding = '0';
this.fakeElem.style.margin = '0';
// Move element out of screen horizontally
this.fakeElem.style.position = 'absolute';
this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px';
// Move element to the same position vertically
var yPosition = window.pageYOffset || document.documentElement.scrollTop;
this.fakeElem.style.top = yPosition + 'px';
this.fakeElem.setAttribute('readonly', '');
this.fakeElem.value = this.text;
this.container.appendChild(this.fakeElem);
this.selectedText = (0, _select2.default)(this.fakeElem);
this.copyText();
}
};
}, {
key: 'removeFake',
value: function removeFake() {
if (this.fakeHandler) {
this.container.removeEventListener('click', this.fakeHandlerCallback);
this.fakeHandler = null;
this.fakeHandlerCallback = null;
}
ClipboardAction.prototype.selectTarget = function selectTarget() {
this.selectedText = (0, _select2.default)(this.target);
this.copyText();
};
ClipboardAction.prototype.copyText = function copyText() {
var succeeded = undefined;
try {
succeeded = document.execCommand(this.action);
} catch (err) {
succeeded = false;
if (this.fakeElem) {
this.container.removeChild(this.fakeElem);
this.fakeElem = null;
}
}
}, {
key: 'selectTarget',
value: function selectTarget() {
this.selectedText = (0, _select2.default)(this.target);
this.copyText();
}
}, {
key: 'copyText',
value: function copyText() {
var succeeded = void 0;
this.handleResult(succeeded);
};
try {
succeeded = document.execCommand(this.action);
} catch (err) {
succeeded = false;
}
ClipboardAction.prototype.handleResult = function handleResult(succeeded) {
if (succeeded) {
this.emitter.emit('success', {
this.handleResult(succeeded);
}
}, {
key: 'handleResult',
value: function handleResult(succeeded) {
this.emitter.emit(succeeded ? 'success' : 'error', {
action: this.action,
text: this.selectedText,
trigger: this.trigger,
clearSelection: this.clearSelection.bind(this)
});
} else {
this.emitter.emit('error', {
action: this.action,
trigger: this.trigger,
clearSelection: this.clearSelection.bind(this)
});
}
};
}, {
key: 'clearSelection',
value: function clearSelection() {
if (this.trigger) {
this.trigger.focus();
}
ClipboardAction.prototype.clearSelection = function clearSelection() {
if (this.target) {
this.target.blur();
window.getSelection().removeAllRanges();
}
window.getSelection().removeAllRanges();
};
ClipboardAction.prototype.destroy = function destroy() {
this.removeFake();
};
_createClass(ClipboardAction, [{
}, {
key: 'destroy',
value: function destroy() {
this.removeFake();
}
}, {
key: 'action',
set: function set() {
var action = arguments.length <= 0 || arguments[0] === undefined ? 'copy' : arguments[0];
var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy';
this._action = action;
@@ -554,6 +552,14 @@ module.exports = E;
set: function set(target) {
if (target !== undefined) {
if (target && (typeof target === 'undefined' ? 'undefined' : _typeof(target)) === 'object' && target.nodeType === 1) {
if (this.action === 'copy' && target.hasAttribute('disabled')) {
throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');
}
if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {
throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');
}
this._target = target;
} else {
throw new Error('Invalid "target" value, use a valid Element');
@@ -571,7 +577,7 @@ module.exports = E;
module.exports = ClipboardAction;
});
},{"select":6}],9:[function(require,module,exports){
},{"select":5}],8:[function(require,module,exports){
(function (global, factory) {
if (typeof define === "function" && define.amd) {
define(['module', './clipboard-action', 'tiny-emitter', 'good-listener'], factory);
@@ -599,12 +605,36 @@ module.exports = E;
};
}
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
};
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
var _createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
function _possibleConstructorReturn(self, call) {
if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
@@ -636,11 +666,10 @@ module.exports = E;
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
* @param {Object} options
*/
function Clipboard(trigger, options) {
_classCallCheck(this, Clipboard);
var _this = _possibleConstructorReturn(this, _Emitter.call(this));
var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this));
_this.resolveOptions(options);
_this.listenClick(trigger);
@@ -654,62 +683,87 @@ module.exports = E;
*/
Clipboard.prototype.resolveOptions = function resolveOptions() {
var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];
_createClass(Clipboard, [{
key: 'resolveOptions',
value: function resolveOptions() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
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;
};
Clipboard.prototype.listenClick = function listenClick(trigger) {
var _this2 = this;
this.listener = (0, _goodListener2.default)(trigger, 'click', function (e) {
return _this2.onClick(e);
});
};
Clipboard.prototype.onClick = function onClick(e) {
var trigger = e.delegateTarget || e.currentTarget;
if (this.clipboardAction) {
this.clipboardAction = null;
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;
}
}, {
key: 'listenClick',
value: function listenClick(trigger) {
var _this2 = this;
this.clipboardAction = new _clipboardAction2.default({
action: this.action(trigger),
target: this.target(trigger),
text: this.text(trigger),
trigger: trigger,
emitter: this
});
};
Clipboard.prototype.defaultAction = function defaultAction(trigger) {
return getAttributeValue('action', trigger);
};
Clipboard.prototype.defaultTarget = function defaultTarget(trigger) {
var selector = getAttributeValue('target', trigger);
if (selector) {
return document.querySelector(selector);
this.listener = (0, _goodListener2.default)(trigger, 'click', function (e) {
return _this2.onClick(e);
});
}
};
}, {
key: 'onClick',
value: function onClick(e) {
var trigger = e.delegateTarget || e.currentTarget;
Clipboard.prototype.defaultText = function defaultText(trigger) {
return getAttributeValue('text', trigger);
};
if (this.clipboardAction) {
this.clipboardAction = null;
}
Clipboard.prototype.destroy = function destroy() {
this.listener.destroy();
if (this.clipboardAction) {
this.clipboardAction.destroy();
this.clipboardAction = null;
this.clipboardAction = new _clipboardAction2.default({
action: this.action(trigger),
target: this.target(trigger),
text: this.text(trigger),
container: this.container,
trigger: trigger,
emitter: this
});
}
};
}, {
key: 'defaultAction',
value: function defaultAction(trigger) {
return getAttributeValue('action', trigger);
}
}, {
key: 'defaultTarget',
value: function defaultTarget(trigger) {
var selector = getAttributeValue('target', trigger);
if (selector) {
return document.querySelector(selector);
}
}
}, {
key: 'defaultText',
value: function defaultText(trigger) {
return getAttributeValue('text', trigger);
}
}, {
key: 'destroy',
value: function destroy() {
this.listener.destroy();
if (this.clipboardAction) {
this.clipboardAction.destroy();
this.clipboardAction = null;
}
}
}], [{
key: 'isSupported',
value: function isSupported() {
var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];
var actions = typeof action === 'string' ? [action] : action;
var support = !!document.queryCommandSupported;
actions.forEach(function (action) {
support = support && !!document.queryCommandSupported(action);
});
return support;
}
}]);
return Clipboard;
}(_tinyEmitter2.default);
@@ -732,5 +786,5 @@ module.exports = E;
module.exports = Clipboard;
});
},{"./clipboard-action":8,"good-listener":4,"tiny-emitter":7}]},{},[9])(9)
},{"./clipboard-action":7,"good-listener":4,"tiny-emitter":6}]},{},[8])(8)
});

File diff suppressed because one or more lines are too long

View File

@@ -3,7 +3,7 @@
Package.describe({
name: "zenorocha:clipboard",
summary: "Modern copy to clipboard. No Flash. Just 2kb.",
version: "1.5.9",
version: "1.7.1",
git: "https://github.com/zenorocha/clipboard.js"
});

View File

@@ -1,6 +1,6 @@
{
"name": "clipboard",
"version": "1.5.9",
"version": "1.7.1",
"description": "Modern copy to clipboard. No Flash. Just 2kb",
"repository": "zenorocha/clipboard.js",
"license": "MIT",
@@ -11,32 +11,31 @@
"cut"
],
"dependencies": {
"good-listener": "^1.1.6",
"select": "^1.0.6",
"tiny-emitter": "^1.0.0"
"good-listener": "^1.2.2",
"select": "^1.1.2",
"tiny-emitter": "^2.0.0"
},
"devDependencies": {
"babel-cli": "^6.5.1",
"babel-core": "^6.5.2",
"babel-plugin-transform-es2015-modules-umd": "^6.5.0",
"babel-preset-es2015": "^6.5.0",
"babel-preset-es2015-loose": "^7.0.0",
"babelify": "^7.2.0",
"babel-cli": "^6.24.1",
"babel-core": "^6.24.1",
"babel-plugin-transform-es2015-modules-umd": "^6.24.1",
"babel-preset-es2015": "^6.24.1",
"babelify": "^7.3.0",
"bannerify": "Vekat/bannerify#feature-option",
"browserify": "^13.0.0",
"chai": "^3.4.1",
"install": "^0.4.4",
"karma": "^0.13.10",
"karma-browserify": "^5.0.1",
"browserify": "^14.3.0",
"chai": "^3.5.0",
"install": "^0.9.6",
"karma": "^1.6.0",
"karma-browserify": "^5.1.1",
"karma-chai": "^0.1.0",
"karma-mocha": "^0.2.0",
"karma-phantomjs-launcher": "^1.0.0",
"karma-sinon": "^1.0.4",
"mocha": "^2.3.3",
"phantomjs-prebuilt": "^2.1.4",
"sinon": "^1.17.2",
"uglify-js": "^2.4.24",
"watchify": "^3.4.0"
"karma-mocha": "^1.3.0",
"karma-phantomjs-launcher": "^1.0.4",
"karma-sinon": "^1.0.5",
"mocha": "^3.3.0",
"phantomjs-prebuilt": "^2.1.14",
"sinon": "^2.2.0",
"uglify-js": "^2.8.22",
"watchify": "^3.9.0"
},
"scripts": {
"build": "npm run build-debug && npm run build-min",
@@ -44,6 +43,6 @@
"build-min": "uglifyjs dist/clipboard.js --comments '/!/' -m screw_ie8=true -c screw_ie8=true,unused=false -o dist/clipboard.min.js",
"build-watch": "watchify src/clipboard.js -s Clipboard -t [babelify] -o dist/clipboard.js -v",
"test": "karma start --single-run",
"prepublish": "babel src --out-dir lib --loose all"
"prepublish": "babel src --out-dir lib"
}
}

View File

@@ -3,9 +3,9 @@
[![Build Status](http://img.shields.io/travis/zenorocha/clipboard.js/master.svg?style=flat)](https://travis-ci.org/zenorocha/clipboard.js)
![Killing Flash](https://img.shields.io/badge/killing-flash-brightgreen.svg?style=flat)
> Modern copy to clipboard. No Flash. Just 2kb
> Modern copy to clipboard. No Flash. Just 3kb gzipped.
<a href="http://clipboardjs.com/"><img width="728" src="https://cloud.githubusercontent.com/assets/398893/9983535/5ab0a950-5fb4-11e5-9602-e73c0b661883.jpg" alt="Demo"></a>
<a href="https://clipboardjs.com/"><img width="728" src="https://cloud.githubusercontent.com/assets/398893/16165747/a0f6fc46-349a-11e6-8c9b-c5fd58d9099c.png" alt="Demo"></a>
## Why
@@ -21,13 +21,7 @@ You can get it on npm.
npm install clipboard --save
```
Or bower, too.
```
bower install clipboard --save
```
If you're not into package management, just [download a ZIP](https://github.com/zenorocha/clipboard.js/archive/master.zip) file.
Or if you're not into package management, just [download a ZIP](https://github.com/zenorocha/clipboard.js/archive/master.zip) file.
## Setup
@@ -57,7 +51,7 @@ A pretty common use case is to copy content from another element. You can do tha
The value you include on this attribute needs to match another's element selector.
<a href="http://clipboardjs.com/#example-target"><img width="473" alt="example-2" src="https://cloud.githubusercontent.com/assets/398893/9983467/a4946aaa-5fb1-11e5-9780-f09fcd7ca6c8.png"></a>
<a href="https://clipboardjs.com/#example-target"><img width="473" alt="example-2" src="https://cloud.githubusercontent.com/assets/398893/9983467/a4946aaa-5fb1-11e5-9780-f09fcd7ca6c8.png"></a>
```html
<!-- Target -->
@@ -75,7 +69,7 @@ Additionally, you can define a `data-clipboard-action` attribute to specify if y
If you omit this attribute, `copy` will be used by default.
<a href="http://clipboardjs.com/#example-action"><img width="473" alt="example-3" src="https://cloud.githubusercontent.com/assets/398893/10000358/7df57b9c-6050-11e5-9cd1-fbc51d2fd0a7.png"></a>
<a href="https://clipboardjs.com/#example-action"><img width="473" alt="example-3" src="https://cloud.githubusercontent.com/assets/398893/10000358/7df57b9c-6050-11e5-9cd1-fbc51d2fd0a7.png"></a>
```html
<!-- Target -->
@@ -93,7 +87,7 @@ As you may expect, the `cut` action only works on `<input>` or `<textarea>` elem
Truth is, you don't even need another element to copy its content from. You can just include a `data-clipboard-text` attribute in your trigger element.
<a href="http://clipboardjs.com/#example-text"><img width="147" alt="example-1" src="https://cloud.githubusercontent.com/assets/398893/10000347/6e16cf8c-6050-11e5-9883-1c5681f9ec45.png"></a>
<a href="https://clipboardjs.com/#example-text"><img width="147" alt="example-1" src="https://cloud.githubusercontent.com/assets/398893/10000347/6e16cf8c-6050-11e5-9883-1c5681f9ec45.png"></a>
```html
<!-- Trigger -->
@@ -125,7 +119,7 @@ clipboard.on('error', function(e) {
});
```
For a live demonstration, open this [site](http://clipboardjs.com/) and just your console :)
For a live demonstration, open this [site](https://clipboardjs.com/) and just your console :)
## Advanced Options
@@ -151,6 +145,14 @@ new Clipboard('.btn', {
});
```
For use in Bootstrap Modals or with any other library that changes the focus you'll want to set the focused element as the `container` value.
```js
new Clipboard('.btn', {
container: document.getElementById('#modal')
});
```
Also, if you are working with single page apps, you may want to manage the lifecycle of the DOM more precisely. Here's how you clean up the events and objects that we create.
```js
@@ -160,17 +162,21 @@ clipboard.destroy();
## Browser Support
This library relies on both [Selection](https://developer.mozilla.org/en-US/docs/Web/API/Selection) and [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand) APIs. The second one is supported in the following browsers.
This library relies on both [Selection](https://developer.mozilla.org/en-US/docs/Web/API/Selection) and [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand) APIs. The first one is [supported by all browsers](http://caniuse.com/#search=selection) while the second one is supported in the following browsers.
| <img src="http://clipboardjs.com/assets/images/chrome.png" width="48px" height="48px" alt="Chrome logo"> | <img src="http://clipboardjs.com/assets/images/firefox.png" width="48px" height="48px" alt="Firefox logo"> | <img src="http://clipboardjs.com/assets/images/ie.png" width="48px" height="48px" alt="Internet Explorer logo"> | <img src="http://clipboardjs.com/assets/images/opera.png" width="48px" height="48px" alt="Opera logo"> | <img src="http://clipboardjs.com/assets/images/safari.png" width="48px" height="48px" alt="Safari logo"> |
|:---:|:---:|:---:|:---:|:---:|
| 42+ ✔ | 41+ ✔ | 9+ ✔ | 29+ ✔ | Nope ✘ |
| <img src="https://clipboardjs.com/assets/images/chrome.png" width="48px" height="48px" alt="Chrome logo"> | <img src="https://clipboardjs.com/assets/images/edge.png" width="48px" height="48px" alt="Edge logo"> | <img src="https://clipboardjs.com/assets/images/firefox.png" width="48px" height="48px" alt="Firefox logo"> | <img src="https://clipboardjs.com/assets/images/ie.png" width="48px" height="48px" alt="Internet Explorer logo"> | <img src="https://clipboardjs.com/assets/images/opera.png" width="48px" height="48px" alt="Opera logo"> | <img src="https://clipboardjs.com/assets/images/safari.png" width="48px" height="48px" alt="Safari logo"> |
|:---:|:---:|:---:|:---:|:---:|:---:|
| 42+ ✔ | 12+ ✔ | 41+ ✔ | 9+ ✔ | 29+ ✔ | 10+ ✔ |
Although copy/cut operations with [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand) aren't supported on Safari yet (including mobile), it gracefully degrades because [Selection](https://developer.mozilla.org/en-US/docs/Web/API/Selection) is supported.
The good news is that clipboard.js gracefully degrades if you need to support older browsers. All you have to do is show a tooltip saying `Copied!` when `success` event is called and `Press Ctrl+C to copy` when `error` event is called because the text is already selected.
That means you can show a tooltip saying `Copied!` when `success` event is called and `Press Ctrl+C to copy` when `error` event is called because the text is already selected.
You can also check if clipboard.js is supported or not by running `Clipboard.isSupported()`, that way you can hide copy/cut buttons from the UI.
For a live demonstration, open this [site](http://clipboardjs.com) on Safari.
## Bonus
A browser extension that adds a "copy to clipboard" button to every code block on *GitHub, MDN, Gist, StackOverflow, StackExchange, npm, and even Medium.*
Install for [Chrome](https://chrome.google.com/webstore/detail/codecopy/fkbfebkcoelajmhanocgppanfoojcdmg) and [Firefox](https://addons.mozilla.org/en-US/firefox/addon/codecopy/).
## License

View File

@@ -18,11 +18,12 @@ class ClipboardAction {
* @param {Object} options
*/
resolveOptions(options = {}) {
this.action = options.action;
this.emitter = options.emitter;
this.target = options.target;
this.text = options.text;
this.trigger = options.trigger;
this.action = options.action;
this.container = options.container;
this.emitter = options.emitter;
this.target = options.target;
this.text = options.text;
this.trigger = options.trigger;
this.selectedText = '';
}
@@ -32,18 +33,12 @@ class ClipboardAction {
* on the existence of `text` and `target` properties.
*/
initSelection() {
if (this.text && this.target) {
throw new Error('Multiple attributes declared, use either "target" or "text"');
}
else if (this.text) {
if (this.text) {
this.selectFake();
}
else if (this.target) {
this.selectTarget();
}
else {
throw new Error('Missing required attributes, use either "target" or "text"');
}
}
/**
@@ -51,11 +46,12 @@ class ClipboardAction {
* and makes a selection on it.
*/
selectFake() {
let isRTL = document.documentElement.getAttribute('dir') == 'rtl';
const isRTL = document.documentElement.getAttribute('dir') == 'rtl';
this.removeFake();
this.fakeHandler = document.body.addEventListener('click', () => this.removeFake());
this.fakeHandlerCallback = () => this.removeFake();
this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true;
this.fakeElem = document.createElement('textarea');
// Prevent zooming on iOS
@@ -65,14 +61,16 @@ class ClipboardAction {
this.fakeElem.style.padding = '0';
this.fakeElem.style.margin = '0';
// Move element out of screen horizontally
this.fakeElem.style.position = 'fixed';
this.fakeElem.style.position = 'absolute';
this.fakeElem.style[ isRTL ? 'right' : 'left' ] = '-9999px';
// Move element to the same position vertically
this.fakeElem.style.top = (window.pageYOffset || document.documentElement.scrollTop) + 'px';
let yPosition = window.pageYOffset || document.documentElement.scrollTop;
this.fakeElem.style.top = `${yPosition}px`;
this.fakeElem.setAttribute('readonly', '');
this.fakeElem.value = this.text;
document.body.appendChild(this.fakeElem);
this.container.appendChild(this.fakeElem);
this.selectedText = select(this.fakeElem);
this.copyText();
@@ -84,12 +82,13 @@ class ClipboardAction {
*/
removeFake() {
if (this.fakeHandler) {
document.body.removeEventListener('click');
this.container.removeEventListener('click', this.fakeHandlerCallback);
this.fakeHandler = null;
this.fakeHandlerCallback = null;
}
if (this.fakeElem) {
document.body.removeChild(this.fakeElem);
this.container.removeChild(this.fakeElem);
this.fakeElem = null;
}
}
@@ -123,29 +122,20 @@ class ClipboardAction {
* @param {Boolean} succeeded
*/
handleResult(succeeded) {
if (succeeded) {
this.emitter.emit('success', {
action: this.action,
text: this.selectedText,
trigger: this.trigger,
clearSelection: this.clearSelection.bind(this)
});
}
else {
this.emitter.emit('error', {
action: this.action,
trigger: this.trigger,
clearSelection: this.clearSelection.bind(this)
});
}
this.emitter.emit(succeeded ? 'success' : 'error', {
action: this.action,
text: this.selectedText,
trigger: this.trigger,
clearSelection: this.clearSelection.bind(this)
});
}
/**
* Removes current selection and focus from `target` element.
* Moves focus away from `target` and back to the trigger, removes current selection.
*/
clearSelection() {
if (this.target) {
this.target.blur();
if (this.trigger) {
this.trigger.focus();
}
window.getSelection().removeAllRanges();
@@ -179,6 +169,14 @@ class ClipboardAction {
set target(target) {
if (target !== undefined) {
if (target && typeof target === 'object' && target.nodeType === 1) {
if (this.action === 'copy' && target.hasAttribute('disabled')) {
throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');
}
if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {
throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');
}
this._target = target;
}
else {

View File

@@ -24,9 +24,10 @@ class Clipboard extends Emitter {
* @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.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;
}
/**
@@ -42,18 +43,19 @@ class Clipboard extends Emitter {
* @param {Event} e
*/
onClick(e) {
let trigger = e.delegateTarget || e.currentTarget;
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),
trigger : trigger,
emitter : this
action : this.action(trigger),
target : this.target(trigger),
text : this.text(trigger),
container : this.container,
trigger : trigger,
emitter : this
});
}
@@ -70,13 +72,29 @@ class Clipboard extends Emitter {
* @param {Element} trigger
*/
defaultTarget(trigger) {
let selector = getAttributeValue('target', 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
@@ -105,7 +123,7 @@ class Clipboard extends Emitter {
* @param {Element} element
*/
function getAttributeValue(suffix, element) {
let attribute = `data-clipboard-${suffix}`;
const attribute = `data-clipboard-${suffix}`;
if (!element.hasAttribute(attribute)) {
return;

View File

@@ -22,10 +22,12 @@ describe('ClipboardAction', () => {
it('should set base properties', () => {
let clip = new ClipboardAction({
emitter: new Emitter(),
container: document.body,
text: 'foo'
});
assert.property(clip, 'action');
assert.property(clip, 'container');
assert.property(clip, 'emitter');
assert.property(clip, 'target');
assert.property(clip, 'text');
@@ -35,35 +37,13 @@ describe('ClipboardAction', () => {
});
describe('#initSelection', () => {
it('should throw an error since both "text" and "target" were passed', done => {
try {
new ClipboardAction({
text: 'foo',
target: document.querySelector('#input')
});
}
catch(e) {
assert.equal(e.message, 'Multiple attributes declared, use either "target" or "text"');
done();
}
});
it('should throw an error since neither "text" nor "target" were passed', done => {
try {
new ClipboardAction();
}
catch(e) {
assert.equal(e.message, 'Missing required attributes, use either "target" or "text"');
done();
}
});
it('should set the position right style property', done => {
// Set document direction
document.documentElement.setAttribute('dir', 'rtl');
let clip = new ClipboardAction({
emitter: new Emitter(),
container: document.body,
text: 'foo'
});
@@ -105,6 +85,7 @@ describe('ClipboardAction', () => {
it('should create a fake element and select its value', () => {
let clip = new ClipboardAction({
emitter: new Emitter(),
container: document.body,
text: 'blah'
});
@@ -116,6 +97,7 @@ describe('ClipboardAction', () => {
it('should remove a temporary fake element', () => {
let clip = new ClipboardAction({
emitter: new Emitter(),
container: document.body,
text: 'blah'
});
@@ -129,6 +111,7 @@ describe('ClipboardAction', () => {
it('should select text from editable element', () => {
let clip = new ClipboardAction({
emitter: new Emitter(),
container: document.body,
target: document.querySelector('#input')
});
@@ -138,6 +121,7 @@ describe('ClipboardAction', () => {
it('should select text from non-editable element', () => {
let clip = new ClipboardAction({
emitter: new Emitter(),
container: document.body,
target: document.querySelector('#paragraph')
});
@@ -164,7 +148,7 @@ describe('ClipboardAction', () => {
});
let clip = new ClipboardAction({
emitter: emitter,
emitter,
target: document.querySelector('#input')
});
});
@@ -179,7 +163,7 @@ describe('ClipboardAction', () => {
});
let clip = new ClipboardAction({
emitter: emitter,
emitter,
target: document.querySelector('#input')
});
});
@@ -189,6 +173,7 @@ describe('ClipboardAction', () => {
it('should fire a success event with certain properties', done => {
let clip = new ClipboardAction({
emitter: new Emitter(),
container: document.body,
target: document.querySelector('#input')
});
@@ -207,6 +192,7 @@ describe('ClipboardAction', () => {
it('should fire a error event with certain properties', done => {
let clip = new ClipboardAction({
emitter: new Emitter(),
container: document.body,
target: document.querySelector('#input')
});
@@ -226,6 +212,7 @@ describe('ClipboardAction', () => {
it('should remove focus from target and text selection', () => {
let clip = new ClipboardAction({
emitter: new Emitter(),
container: document.body,
target: document.querySelector('#input')
});
@@ -243,6 +230,7 @@ describe('ClipboardAction', () => {
it('should destroy an existing fake element', () => {
let clip = new ClipboardAction({
emitter: new Emitter(),
container: document.body,
text: 'blah'
});

View File

@@ -26,7 +26,7 @@ describe('Clipboard', () => {
describe('#resolveOptions', () => {
before(() => {
global.fn = function() {};
global.fn = () => {};
});
it('should set action as a function', () => {
@@ -52,6 +52,20 @@ describe('Clipboard', () => {
assert.equal(global.fn, clipboard.text);
});
it('should set container as an object', () => {
let clipboard = new Clipboard('.btn', {
container: document.body
});
assert.equal(document.body, clipboard.container);
});
it('should set container as body by default', () => {
let clipboard = new Clipboard('.btn');
assert.equal(document.body, clipboard.container);
});
});
describe('#listenClick', () => {
@@ -79,8 +93,8 @@ describe('Clipboard', () => {
it('should throw an exception when target is invalid', done => {
try {
var clipboard = new Clipboard('.btn', {
target: function() {
const clipboard = new Clipboard('.btn', {
target() {
return null;
}
});
@@ -94,6 +108,17 @@ describe('Clipboard', () => {
});
});
describe('#static isSupported', () => {
it('should return the support of the given action', () => {
assert.equal(Clipboard.isSupported('copy'), false);
assert.equal(Clipboard.isSupported('cut'), false);
});
it('should return the support of the cut and copy actions', () => {
assert.equal(Clipboard.isSupported(), false);
});
});
describe('#destroy', () => {
it('should destroy an existing instance of ClipboardAction', () => {
let clipboard = new Clipboard('.btn');