mirror of
https://github.com/zenorocha/clipboard.js.git
synced 2023-08-10 21:12:48 +03:00
Compare commits
74 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f59d4e6b4d | ||
|
|
9ddff7e591 | ||
|
|
a00f1fe327 | ||
|
|
38ae5b34f3 | ||
|
|
41b7234d50 | ||
|
|
3696739e5e | ||
|
|
63d1b0f014 | ||
|
|
402c9ee17b | ||
|
|
0538f6e212 | ||
|
|
8ad16a2c6c | ||
|
|
ce0829054b | ||
|
|
223d30c110 | ||
|
|
b1a68df6e9 | ||
|
|
0149e1de5e | ||
|
|
26a9e9d56c | ||
|
|
fce625f151 | ||
|
|
f7040bae8a | ||
|
|
9f9d03c927 | ||
|
|
e18c26ae07 | ||
|
|
70cfabec69 | ||
|
|
f700a1b12e | ||
|
|
9e3d662c4e | ||
|
|
76b907949c | ||
|
|
60b6887100 | ||
|
|
eb7418b51b | ||
|
|
869c4e3219 | ||
|
|
a4ab305297 | ||
|
|
294e9fcb5d | ||
|
|
79c3361ca4 | ||
|
|
c3fefc1fc0 | ||
|
|
42bd266f0b | ||
|
|
53a733fcb5 | ||
|
|
7a5a910bcd | ||
|
|
0163f7cb72 | ||
|
|
941bdbb16e | ||
|
|
00d5cc4e96 | ||
|
|
07a1f8456b | ||
|
|
ff3cd2c722 | ||
|
|
d346f30e5d | ||
|
|
e5fe34c524 | ||
|
|
0a6aace544 | ||
|
|
43d9c11aaf | ||
|
|
90a52149ed | ||
|
|
83b9d6a84d | ||
|
|
3d13baa385 | ||
|
|
7e37c95121 | ||
|
|
bc9bcdd678 | ||
|
|
4c9e29a0dc | ||
|
|
bd6dc9eb9f | ||
|
|
a88bb77be4 | ||
|
|
e86dc2caa2 | ||
|
|
8392a7ba1c | ||
|
|
31e3622e17 | ||
|
|
ee1eb455f4 | ||
|
|
d50f2fb73f | ||
|
|
c8221c742d | ||
|
|
efc22cf651 | ||
|
|
4b27a72dce | ||
|
|
c12c610b22 | ||
|
|
1d772a0cbe | ||
|
|
d9254459b7 | ||
|
|
2aa163b1d0 | ||
|
|
783dc6f3cf | ||
|
|
3188ffbce3 | ||
|
|
5dba68634e | ||
|
|
a12a056ef6 | ||
|
|
cb4301658c | ||
|
|
5efcdf2810 | ||
|
|
72926580c3 | ||
|
|
4967f118fe | ||
|
|
44d59b34a2 | ||
|
|
9d6375d20e | ||
|
|
37136663df | ||
|
|
db575bb4df |
4
.babelrc
Normal file
4
.babelrc
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"presets": ["es2015"],
|
||||||
|
"plugins": ["transform-es2015-modules-umd"]
|
||||||
|
}
|
||||||
15
.github/issue_template.md
vendored
Normal file
15
.github/issue_template.md
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
### 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.
|
||||||
|
|
||||||
|
### Actual behaviour
|
||||||
|
|
||||||
|
Instead of '...', what I saw was that '...' happened instead.
|
||||||
|
|
||||||
|
### Browsers affected
|
||||||
|
|
||||||
|
I tested on all major browsers and only IE 11 does not work.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "clipboard",
|
"name": "clipboard",
|
||||||
"version": "1.5.2",
|
"version": "1.6.1",
|
||||||
"description": "Modern copy to clipboard. No Flash. Just 2kb",
|
"description": "Modern copy to clipboard. No Flash. Just 2kb",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "dist/clipboard.js",
|
"main": "dist/clipboard.js",
|
||||||
|
|||||||
@@ -3,10 +3,13 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>constructor-node</title>
|
<title>constructor-node</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- 1. Define some markup -->
|
<!-- 1. Define some markup -->
|
||||||
<button id="btn" data-clipboard-text="1">Copy</button>
|
<div id="btn" data-clipboard-text="1">
|
||||||
|
<span>Copy</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 2. Include library -->
|
<!-- 2. Include library -->
|
||||||
<script src="../dist/clipboard.min.js"></script>
|
<script src="../dist/clipboard.min.js"></script>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>constructor-nodelist</title>
|
<title>constructor-nodelist</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- 1. Define some markup -->
|
<!-- 1. Define some markup -->
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>constructor-selector</title>
|
<title>constructor-selector</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- 1. Define some markup -->
|
<!-- 1. Define some markup -->
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>function-target</title>
|
<title>function-target</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- 1. Define some markup -->
|
<!-- 1. Define some markup -->
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>function-text</title>
|
<title>function-text</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- 1. Define some markup -->
|
<!-- 1. Define some markup -->
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>target-div</title>
|
<title>target-div</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- 1. Define some markup -->
|
<!-- 1. Define some markup -->
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>target-input</title>
|
<title>target-input</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- 1. Define some markup -->
|
<!-- 1. Define some markup -->
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8">
|
||||||
<title>target-textarea</title>
|
<title>target-textarea</title>
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- 1. Define some markup -->
|
<!-- 1. Define some markup -->
|
||||||
|
|||||||
868
dist/clipboard.js
vendored
868
dist/clipboard.js
vendored
@@ -1,64 +1,43 @@
|
|||||||
/*!
|
/*!
|
||||||
* clipboard.js v1.5.2
|
* clipboard.js v1.6.1
|
||||||
* https://zenorocha.github.io/clipboard.js
|
* https://zenorocha.github.io/clipboard.js
|
||||||
*
|
*
|
||||||
* Licensed MIT © Zeno Rocha
|
* 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){
|
(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')
|
var DOCUMENT_NODE_TYPE = 9;
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
},{"matches-selector":2}],2:[function(require,module,exports){
|
/**
|
||||||
|
* A polyfill for Element.matches()
|
||||||
/**
|
*/
|
||||||
* Element prototype.
|
if (typeof Element !== 'undefined' && !Element.prototype.matches) {
|
||||||
*/
|
var proto = Element.prototype;
|
||||||
|
|
||||||
var proto = Element.prototype;
|
proto.matches = proto.matchesSelector ||
|
||||||
|
proto.mozMatchesSelector ||
|
||||||
/**
|
proto.msMatchesSelector ||
|
||||||
* Vendor function.
|
proto.oMatchesSelector ||
|
||||||
*/
|
proto.webkitMatchesSelector;
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
},{}],3:[function(require,module,exports){
|
|
||||||
var closest = require('closest');
|
/**
|
||||||
|
* 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 (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.
|
* Delegates event to a selector.
|
||||||
@@ -67,16 +46,17 @@ var closest = require('closest');
|
|||||||
* @param {String} selector
|
* @param {String} selector
|
||||||
* @param {String} type
|
* @param {String} type
|
||||||
* @param {Function} callback
|
* @param {Function} callback
|
||||||
|
* @param {Boolean} useCapture
|
||||||
* @return {Object}
|
* @return {Object}
|
||||||
*/
|
*/
|
||||||
function delegate(element, selector, type, callback) {
|
function delegate(element, selector, type, callback, useCapture) {
|
||||||
var listenerFn = listener.apply(this, arguments);
|
var listenerFn = listener.apply(this, arguments);
|
||||||
|
|
||||||
element.addEventListener(type, listenerFn);
|
element.addEventListener(type, listenerFn, useCapture);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
destroy: function() {
|
destroy: function() {
|
||||||
element.removeEventListener(type, listenerFn);
|
element.removeEventListener(type, listenerFn, useCapture);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -92,13 +72,9 @@ function delegate(element, selector, type, callback) {
|
|||||||
*/
|
*/
|
||||||
function listener(element, selector, type, callback) {
|
function listener(element, selector, type, callback) {
|
||||||
return function(e) {
|
return function(e) {
|
||||||
var delegateTarget = closest(e.target, selector, true);
|
e.delegateTarget = closest(e.target, selector);
|
||||||
|
|
||||||
if (delegateTarget) {
|
|
||||||
Object.defineProperty(e, 'target', {
|
|
||||||
value: delegateTarget
|
|
||||||
});
|
|
||||||
|
|
||||||
|
if (e.delegateTarget) {
|
||||||
callback.call(element, e);
|
callback.call(element, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -106,7 +82,7 @@ function listener(element, selector, type, callback) {
|
|||||||
|
|
||||||
module.exports = delegate;
|
module.exports = delegate;
|
||||||
|
|
||||||
},{"closest":1}],4:[function(require,module,exports){
|
},{"./closest":1}],3:[function(require,module,exports){
|
||||||
/**
|
/**
|
||||||
* Check if argument is a HTML element.
|
* Check if argument is a HTML element.
|
||||||
*
|
*
|
||||||
@@ -151,13 +127,13 @@ exports.string = function(value) {
|
|||||||
* @param {Object} value
|
* @param {Object} value
|
||||||
* @return {Boolean}
|
* @return {Boolean}
|
||||||
*/
|
*/
|
||||||
exports.function = function(value) {
|
exports.fn = function(value) {
|
||||||
var type = Object.prototype.toString.call(value);
|
var type = Object.prototype.toString.call(value);
|
||||||
|
|
||||||
return type === '[object Function]';
|
return type === '[object Function]';
|
||||||
};
|
};
|
||||||
|
|
||||||
},{}],5:[function(require,module,exports){
|
},{}],4:[function(require,module,exports){
|
||||||
var is = require('./is');
|
var is = require('./is');
|
||||||
var delegate = require('delegate');
|
var delegate = require('delegate');
|
||||||
|
|
||||||
@@ -179,7 +155,7 @@ function listen(target, type, callback) {
|
|||||||
throw new TypeError('Second argument must be a String');
|
throw new TypeError('Second argument must be a String');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is.function(callback)) {
|
if (!is.fn(callback)) {
|
||||||
throw new TypeError('Third argument must be a Function');
|
throw new TypeError('Third argument must be a Function');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -254,16 +230,36 @@ function listenSelector(selector, type, callback) {
|
|||||||
|
|
||||||
module.exports = listen;
|
module.exports = listen;
|
||||||
|
|
||||||
},{"./is":4,"delegate":3}],6:[function(require,module,exports){
|
},{"./is":3,"delegate":2}],5:[function(require,module,exports){
|
||||||
function select(element) {
|
function select(element) {
|
||||||
var selectedText;
|
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.select();
|
||||||
|
element.setSelectionRange(0, element.value.length);
|
||||||
|
|
||||||
|
if (!isReadOnly) {
|
||||||
|
element.removeAttribute('readonly');
|
||||||
|
}
|
||||||
|
|
||||||
selectedText = element.value;
|
selectedText = element.value;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
if (element.hasAttribute('contenteditable')) {
|
||||||
|
element.focus();
|
||||||
|
}
|
||||||
|
|
||||||
var selection = window.getSelection();
|
var selection = window.getSelection();
|
||||||
var range = document.createRange();
|
var range = document.createRange();
|
||||||
|
|
||||||
@@ -279,14 +275,14 @@ function select(element) {
|
|||||||
|
|
||||||
module.exports = select;
|
module.exports = select;
|
||||||
|
|
||||||
},{}],7:[function(require,module,exports){
|
},{}],6:[function(require,module,exports){
|
||||||
function E () {
|
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)
|
// (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
|
||||||
}
|
}
|
||||||
|
|
||||||
E.prototype = {
|
E.prototype = {
|
||||||
on: function (name, callback, ctx) {
|
on: function (name, callback, ctx) {
|
||||||
var e = this.e || (this.e = {});
|
var e = this.e || (this.e = {});
|
||||||
|
|
||||||
(e[name] || (e[name] = [])).push({
|
(e[name] || (e[name] = [])).push({
|
||||||
@@ -347,396 +343,436 @@ E.prototype = {
|
|||||||
|
|
||||||
module.exports = E;
|
module.exports = E;
|
||||||
|
|
||||||
},{}],8:[function(require,module,exports){
|
},{}],7:[function(require,module,exports){
|
||||||
'use strict';
|
(function (global, factory) {
|
||||||
|
if (typeof define === "function" && define.amd) {
|
||||||
|
define(['module', 'select'], factory);
|
||||||
|
} else if (typeof exports !== "undefined") {
|
||||||
|
factory(module, require('select'));
|
||||||
|
} else {
|
||||||
|
var mod = {
|
||||||
|
exports: {}
|
||||||
|
};
|
||||||
|
factory(mod, global.select);
|
||||||
|
global.clipboardAction = mod.exports;
|
||||||
|
}
|
||||||
|
})(this, function (module, _select) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
exports.__esModule = true;
|
var _select2 = _interopRequireDefault(_select);
|
||||||
|
|
||||||
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 _interopRequireDefault(obj) {
|
||||||
|
return obj && obj.__esModule ? obj : {
|
||||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
|
default: obj
|
||||||
|
};
|
||||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
|
|
||||||
|
|
||||||
var _select = require('select');
|
|
||||||
|
|
||||||
var _select2 = _interopRequireDefault(_select);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Inner class which performs selection from either `text` or `target`
|
|
||||||
* properties and then executes copy or cut operations.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var ClipboardAction = (function () {
|
|
||||||
/**
|
|
||||||
* @param {Object} options
|
|
||||||
*/
|
|
||||||
|
|
||||||
function ClipboardAction(options) {
|
|
||||||
_classCallCheck(this, ClipboardAction);
|
|
||||||
|
|
||||||
this.resolveOptions(options);
|
|
||||||
this.initSelection();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) {
|
||||||
* Defines base properties passed from constructor.
|
return typeof obj;
|
||||||
* @param {Object} options
|
} : function (obj) {
|
||||||
*/
|
return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
|
||||||
|
|
||||||
ClipboardAction.prototype.resolveOptions = 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.selectedText = '';
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
function _classCallCheck(instance, Constructor) {
|
||||||
* Decides which selection strategy is going to be applied based
|
if (!(instance instanceof Constructor)) {
|
||||||
* on the existence of `text` and `target` properties.
|
throw new TypeError("Cannot call a class as a function");
|
||||||
*/
|
|
||||||
|
|
||||||
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"');
|
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
var _createClass = function () {
|
||||||
* Creates a fake textarea element, sets its value from `text` property,
|
function defineProperties(target, props) {
|
||||||
* and makes a selection on it.
|
for (var i = 0; i < props.length; i++) {
|
||||||
*/
|
var descriptor = props[i];
|
||||||
|
descriptor.enumerable = descriptor.enumerable || false;
|
||||||
ClipboardAction.prototype.selectFake = function selectFake() {
|
descriptor.configurable = true;
|
||||||
var _this = this;
|
if ("value" in descriptor) descriptor.writable = true;
|
||||||
|
Object.defineProperty(target, descriptor.key, descriptor);
|
||||||
this.removeFake();
|
|
||||||
|
|
||||||
this.fakeHandler = document.body.addEventListener('click', function () {
|
|
||||||
return _this.removeFake();
|
|
||||||
});
|
|
||||||
|
|
||||||
this.fakeElem = document.createElement('textarea');
|
|
||||||
this.fakeElem.style.position = 'absolute';
|
|
||||||
this.fakeElem.style.left = '-9999px';
|
|
||||||
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 = _select2['default'](this.fakeElem);
|
|
||||||
this.copyText();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Only removes the fake element after another click event, that way
|
|
||||||
* a user can hit `Ctrl+C` to copy because selection still exists.
|
|
||||||
*/
|
|
||||||
|
|
||||||
ClipboardAction.prototype.removeFake = function removeFake() {
|
|
||||||
if (this.fakeHandler) {
|
|
||||||
document.body.removeEventListener('click');
|
|
||||||
this.fakeHandler = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.fakeElem) {
|
|
||||||
document.body.removeChild(this.fakeElem);
|
|
||||||
this.fakeElem = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Selects the content from element passed on `target` property.
|
|
||||||
*/
|
|
||||||
|
|
||||||
ClipboardAction.prototype.selectTarget = function selectTarget() {
|
|
||||||
this.selectedText = _select2['default'](this.target);
|
|
||||||
this.copyText();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Executes the copy operation based on the current selection.
|
|
||||||
*/
|
|
||||||
|
|
||||||
ClipboardAction.prototype.copyText = function copyText() {
|
|
||||||
var succeeded = undefined;
|
|
||||||
|
|
||||||
try {
|
|
||||||
succeeded = document.execCommand(this.action);
|
|
||||||
} catch (err) {
|
|
||||||
succeeded = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.handleResult(succeeded);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fires an event based on the copy operation result.
|
|
||||||
* @param {Boolean} succeeded
|
|
||||||
*/
|
|
||||||
|
|
||||||
ClipboardAction.prototype.handleResult = function 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)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes current selection and focus from `target` element.
|
|
||||||
*/
|
|
||||||
|
|
||||||
ClipboardAction.prototype.clearSelection = function clearSelection() {
|
|
||||||
if (this.target) {
|
|
||||||
this.target.blur();
|
|
||||||
}
|
|
||||||
|
|
||||||
window.getSelection().removeAllRanges();
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the `action` to be performed which can be either 'copy' or 'cut'.
|
|
||||||
* @param {String} action
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy lifecycle.
|
|
||||||
*/
|
|
||||||
|
|
||||||
ClipboardAction.prototype.destroy = function destroy() {
|
|
||||||
this.removeFake();
|
|
||||||
};
|
|
||||||
|
|
||||||
_createClass(ClipboardAction, [{
|
|
||||||
key: 'action',
|
|
||||||
set: function set() {
|
|
||||||
var action = arguments.length <= 0 || arguments[0] === undefined ? 'copy' : arguments[0];
|
|
||||||
|
|
||||||
this._action = action;
|
|
||||||
|
|
||||||
if (this._action !== 'copy' && this._action !== 'cut') {
|
|
||||||
throw new Error('Invalid "action" value, use either "copy" or "cut"');
|
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
|
|
||||||
|
return function (Constructor, protoProps, staticProps) {
|
||||||
|
if (protoProps) defineProperties(Constructor.prototype, protoProps);
|
||||||
|
if (staticProps) defineProperties(Constructor, staticProps);
|
||||||
|
return Constructor;
|
||||||
|
};
|
||||||
|
}();
|
||||||
|
|
||||||
|
var ClipboardAction = function () {
|
||||||
/**
|
/**
|
||||||
* Gets the `action` property.
|
* @param {Object} options
|
||||||
* @return {String}
|
|
||||||
*/
|
*/
|
||||||
get: function get() {
|
function ClipboardAction(options) {
|
||||||
return this._action;
|
_classCallCheck(this, ClipboardAction);
|
||||||
|
|
||||||
|
this.resolveOptions(options);
|
||||||
|
this.initSelection();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the `target` property using an element
|
* Defines base properties passed from constructor.
|
||||||
* that will be have its content copied.
|
* @param {Object} options
|
||||||
* @param {Element} target
|
|
||||||
*/
|
*/
|
||||||
}, {
|
|
||||||
key: 'target',
|
|
||||||
set: function set(target) {
|
_createClass(ClipboardAction, [{
|
||||||
if (target !== undefined) {
|
key: 'resolveOptions',
|
||||||
if (target && typeof target === 'object' && target.nodeType === 1) {
|
value: function resolveOptions() {
|
||||||
this._target = target;
|
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
||||||
} else {
|
|
||||||
throw new Error('Invalid "target" value, use a valid Element');
|
this.action = options.action;
|
||||||
|
this.emitter = options.emitter;
|
||||||
|
this.target = options.target;
|
||||||
|
this.text = options.text;
|
||||||
|
this.trigger = options.trigger;
|
||||||
|
|
||||||
|
this.selectedText = '';
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: 'initSelection',
|
||||||
|
value: function initSelection() {
|
||||||
|
if (this.text) {
|
||||||
|
this.selectFake();
|
||||||
|
} else if (this.target) {
|
||||||
|
this.selectTarget();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
}, {
|
||||||
|
key: 'selectFake',
|
||||||
|
value: function selectFake() {
|
||||||
|
var _this = this;
|
||||||
|
|
||||||
|
var isRTL = document.documentElement.getAttribute('dir') == 'rtl';
|
||||||
|
|
||||||
|
this.removeFake();
|
||||||
|
|
||||||
|
this.fakeHandlerCallback = function () {
|
||||||
|
return _this.removeFake();
|
||||||
|
};
|
||||||
|
this.fakeHandler = document.body.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;
|
||||||
|
|
||||||
|
document.body.appendChild(this.fakeElem);
|
||||||
|
|
||||||
|
this.selectedText = (0, _select2.default)(this.fakeElem);
|
||||||
|
this.copyText();
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: 'removeFake',
|
||||||
|
value: function removeFake() {
|
||||||
|
if (this.fakeHandler) {
|
||||||
|
document.body.removeEventListener('click', this.fakeHandlerCallback);
|
||||||
|
this.fakeHandler = null;
|
||||||
|
this.fakeHandlerCallback = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.fakeElem) {
|
||||||
|
document.body.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;
|
||||||
|
|
||||||
|
try {
|
||||||
|
succeeded = document.execCommand(this.action);
|
||||||
|
} catch (err) {
|
||||||
|
succeeded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: 'clearSelection',
|
||||||
|
value: function clearSelection() {
|
||||||
|
if (this.target) {
|
||||||
|
this.target.blur();
|
||||||
|
}
|
||||||
|
|
||||||
|
window.getSelection().removeAllRanges();
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: 'destroy',
|
||||||
|
value: function destroy() {
|
||||||
|
this.removeFake();
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: 'action',
|
||||||
|
set: function set() {
|
||||||
|
var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy';
|
||||||
|
|
||||||
|
this._action = action;
|
||||||
|
|
||||||
|
if (this._action !== 'copy' && this._action !== 'cut') {
|
||||||
|
throw new Error('Invalid "action" value, use either "copy" or "cut"');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
get: function get() {
|
||||||
|
return this._action;
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: 'target',
|
||||||
|
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');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
get: function get() {
|
||||||
|
return this._target;
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
|
||||||
|
return ClipboardAction;
|
||||||
|
}();
|
||||||
|
|
||||||
|
module.exports = ClipboardAction;
|
||||||
|
});
|
||||||
|
|
||||||
|
},{"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);
|
||||||
|
} else if (typeof exports !== "undefined") {
|
||||||
|
factory(module, require('./clipboard-action'), require('tiny-emitter'), require('good-listener'));
|
||||||
|
} else {
|
||||||
|
var mod = {
|
||||||
|
exports: {}
|
||||||
|
};
|
||||||
|
factory(mod, global.clipboardAction, global.tinyEmitter, global.goodListener);
|
||||||
|
global.clipboard = mod.exports;
|
||||||
|
}
|
||||||
|
})(this, function (module, _clipboardAction, _tinyEmitter, _goodListener) {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var _clipboardAction2 = _interopRequireDefault(_clipboardAction);
|
||||||
|
|
||||||
|
var _tinyEmitter2 = _interopRequireDefault(_tinyEmitter);
|
||||||
|
|
||||||
|
var _goodListener2 = _interopRequireDefault(_goodListener);
|
||||||
|
|
||||||
|
function _interopRequireDefault(obj) {
|
||||||
|
return obj && obj.__esModule ? obj : {
|
||||||
|
default: 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");
|
||||||
|
}
|
||||||
|
|
||||||
|
return call && (typeof call === "object" || typeof call === "function") ? call : self;
|
||||||
|
}
|
||||||
|
|
||||||
|
function _inherits(subClass, superClass) {
|
||||||
|
if (typeof superClass !== "function" && superClass !== null) {
|
||||||
|
throw new TypeError("Super expression must either be null or a function, not " + typeof superClass);
|
||||||
|
}
|
||||||
|
|
||||||
|
subClass.prototype = Object.create(superClass && superClass.prototype, {
|
||||||
|
constructor: {
|
||||||
|
value: subClass,
|
||||||
|
enumerable: false,
|
||||||
|
writable: true,
|
||||||
|
configurable: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
var Clipboard = function (_Emitter) {
|
||||||
|
_inherits(Clipboard, _Emitter);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets the `target` property.
|
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
|
||||||
* @return {String|HTMLElement}
|
* @param {Object} options
|
||||||
*/
|
*/
|
||||||
get: function get() {
|
function Clipboard(trigger, options) {
|
||||||
return this._target;
|
_classCallCheck(this, Clipboard);
|
||||||
|
|
||||||
|
var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this));
|
||||||
|
|
||||||
|
_this.resolveOptions(options);
|
||||||
|
_this.listenClick(trigger);
|
||||||
|
return _this;
|
||||||
}
|
}
|
||||||
}]);
|
|
||||||
|
|
||||||
return ClipboardAction;
|
/**
|
||||||
})();
|
* Defines if attributes would be resolved using internal setter functions
|
||||||
|
* or custom functions that were passed in the constructor.
|
||||||
|
* @param {Object} options
|
||||||
|
*/
|
||||||
|
|
||||||
exports['default'] = ClipboardAction;
|
|
||||||
module.exports = exports['default'];
|
|
||||||
|
|
||||||
},{"select":6}],9:[function(require,module,exports){
|
_createClass(Clipboard, [{
|
||||||
'use strict';
|
key: 'resolveOptions',
|
||||||
|
value: function resolveOptions() {
|
||||||
|
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
||||||
|
|
||||||
exports.__esModule = true;
|
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;
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: 'listenClick',
|
||||||
|
value: function listenClick(trigger) {
|
||||||
|
var _this2 = this;
|
||||||
|
|
||||||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; }
|
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;
|
||||||
|
|
||||||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
|
if (this.clipboardAction) {
|
||||||
|
this.clipboardAction = null;
|
||||||
|
}
|
||||||
|
|
||||||
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
|
this.clipboardAction = new _clipboardAction2.default({
|
||||||
|
action: this.action(trigger),
|
||||||
|
target: this.target(trigger),
|
||||||
|
text: this.text(trigger),
|
||||||
|
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);
|
||||||
|
|
||||||
var _clipboardAction = require('./clipboard-action');
|
if (selector) {
|
||||||
|
return document.querySelector(selector);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: 'defaultText',
|
||||||
|
value: function defaultText(trigger) {
|
||||||
|
return getAttributeValue('text', trigger);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: 'destroy',
|
||||||
|
value: function destroy() {
|
||||||
|
this.listener.destroy();
|
||||||
|
|
||||||
var _clipboardAction2 = _interopRequireDefault(_clipboardAction);
|
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 _tinyEmitter = require('tiny-emitter');
|
var actions = typeof action === 'string' ? [action] : action;
|
||||||
|
var support = !!document.queryCommandSupported;
|
||||||
|
|
||||||
var _tinyEmitter2 = _interopRequireDefault(_tinyEmitter);
|
actions.forEach(function (action) {
|
||||||
|
support = support && !!document.queryCommandSupported(action);
|
||||||
|
});
|
||||||
|
|
||||||
var _goodListener = require('good-listener');
|
return support;
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
|
||||||
var _goodListener2 = _interopRequireDefault(_goodListener);
|
return Clipboard;
|
||||||
|
}(_tinyEmitter2.default);
|
||||||
/**
|
|
||||||
* Base class which takes one or more elements, adds event listeners to them,
|
|
||||||
* and instantiates a new `ClipboardAction` on each click.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var Clipboard = (function (_Emitter) {
|
|
||||||
_inherits(Clipboard, _Emitter);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
|
|
||||||
* @param {Object} options
|
|
||||||
*/
|
|
||||||
|
|
||||||
function Clipboard(trigger, options) {
|
|
||||||
_classCallCheck(this, Clipboard);
|
|
||||||
|
|
||||||
_Emitter.call(this);
|
|
||||||
|
|
||||||
this.resolveOptions(options);
|
|
||||||
this.listenClick(trigger);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to retrieve attribute value.
|
* Helper function to retrieve attribute value.
|
||||||
* @param {String} suffix
|
* @param {String} suffix
|
||||||
* @param {Element} element
|
* @param {Element} element
|
||||||
*/
|
*/
|
||||||
|
function getAttributeValue(suffix, element) {
|
||||||
|
var attribute = 'data-clipboard-' + suffix;
|
||||||
|
|
||||||
/**
|
if (!element.hasAttribute(attribute)) {
|
||||||
* Defines if attributes would be resolved using internal setter functions
|
return;
|
||||||
* or custom functions that were passed in the constructor.
|
|
||||||
* @param {Object} options
|
|
||||||
*/
|
|
||||||
|
|
||||||
Clipboard.prototype.resolveOptions = 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;
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a click event listener to the passed trigger.
|
|
||||||
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
|
|
||||||
*/
|
|
||||||
|
|
||||||
Clipboard.prototype.listenClick = function listenClick(trigger) {
|
|
||||||
var _this = this;
|
|
||||||
|
|
||||||
this.listener = _goodListener2['default'](trigger, 'click', function (e) {
|
|
||||||
return _this.onClick(e);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Defines a new `ClipboardAction` on each click event.
|
|
||||||
* @param {Event} e
|
|
||||||
*/
|
|
||||||
|
|
||||||
Clipboard.prototype.onClick = function onClick(e) {
|
|
||||||
if (this.clipboardAction) {
|
|
||||||
this.clipboardAction = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clipboardAction = new _clipboardAction2['default']({
|
return element.getAttribute(attribute);
|
||||||
action: this.action(e.target),
|
|
||||||
target: this.target(e.target),
|
|
||||||
text: this.text(e.target),
|
|
||||||
trigger: e.target,
|
|
||||||
emitter: this
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default `action` lookup function.
|
|
||||||
* @param {Element} trigger
|
|
||||||
*/
|
|
||||||
|
|
||||||
Clipboard.prototype.defaultAction = function defaultAction(trigger) {
|
|
||||||
return getAttributeValue('action', trigger);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default `target` lookup function.
|
|
||||||
* @param {Element} trigger
|
|
||||||
*/
|
|
||||||
|
|
||||||
Clipboard.prototype.defaultTarget = function defaultTarget(trigger) {
|
|
||||||
var selector = getAttributeValue('target', trigger);
|
|
||||||
|
|
||||||
if (selector) {
|
|
||||||
return document.querySelector(selector);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default `text` lookup function.
|
|
||||||
* @param {Element} trigger
|
|
||||||
*/
|
|
||||||
|
|
||||||
Clipboard.prototype.defaultText = function defaultText(trigger) {
|
|
||||||
return getAttributeValue('text', trigger);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Destroy lifecycle.
|
|
||||||
*/
|
|
||||||
|
|
||||||
Clipboard.prototype.destroy = function destroy() {
|
|
||||||
this.listener.destroy();
|
|
||||||
|
|
||||||
if (this.clipboardAction) {
|
|
||||||
this.clipboardAction.destroy();
|
|
||||||
this.clipboardAction = null;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return Clipboard;
|
|
||||||
})(_tinyEmitter2['default']);
|
|
||||||
|
|
||||||
function getAttributeValue(suffix, element) {
|
|
||||||
var attribute = 'data-clipboard-' + suffix;
|
|
||||||
|
|
||||||
if (!element.hasAttribute(attribute)) {
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return element.getAttribute(attribute);
|
module.exports = Clipboard;
|
||||||
}
|
});
|
||||||
|
|
||||||
exports['default'] = Clipboard;
|
},{"./clipboard-action":7,"good-listener":4,"tiny-emitter":6}]},{},[8])(8)
|
||||||
module.exports = exports['default'];
|
|
||||||
|
|
||||||
},{"./clipboard-action":8,"good-listener":5,"tiny-emitter":7}]},{},[9])(9)
|
|
||||||
});
|
});
|
||||||
4
dist/clipboard.min.js
vendored
4
dist/clipboard.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -3,10 +3,10 @@
|
|||||||
Package.describe({
|
Package.describe({
|
||||||
name: "zenorocha:clipboard",
|
name: "zenorocha:clipboard",
|
||||||
summary: "Modern copy to clipboard. No Flash. Just 2kb.",
|
summary: "Modern copy to clipboard. No Flash. Just 2kb.",
|
||||||
version: "1.5.1",
|
version: "1.6.1",
|
||||||
git: "https://github.com/zenorocha/clipboard.js"
|
git: "https://github.com/zenorocha/clipboard.js"
|
||||||
});
|
});
|
||||||
|
|
||||||
Package.onUse(function(api) {
|
Package.onUse(function(api) {
|
||||||
api.addFiles("dist/clipboard.min.js", "client");
|
api.addFiles("dist/clipboard.js", "client");
|
||||||
});
|
});
|
||||||
|
|||||||
50
package.json
50
package.json
@@ -1,52 +1,48 @@
|
|||||||
{
|
{
|
||||||
"name": "clipboard",
|
"name": "clipboard",
|
||||||
"version": "1.5.2",
|
"version": "1.6.1",
|
||||||
"description": "Modern copy to clipboard. No Flash. Just 2kb",
|
"description": "Modern copy to clipboard. No Flash. Just 2kb",
|
||||||
"repository": "zenorocha/clipboard.js",
|
"repository": "zenorocha/clipboard.js",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"main": "lib/clipboard.js",
|
"main": "lib/clipboard.js",
|
||||||
"browserify": {
|
|
||||||
"transform": [
|
|
||||||
[
|
|
||||||
"babelify",
|
|
||||||
{
|
|
||||||
"loose": "all"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"clipboard",
|
"clipboard",
|
||||||
"copy",
|
"copy",
|
||||||
"cut"
|
"cut"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"good-listener": "^1.1.2",
|
"good-listener": "^1.2.0",
|
||||||
"select": "^1.0.4",
|
"select": "^1.1.2",
|
||||||
"tiny-emitter": "^1.0.0"
|
"tiny-emitter": "^1.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"babel": "^5.8.29",
|
"babel-cli": "^6.5.1",
|
||||||
"babelify": "^6.3.0",
|
"babel-core": "^6.5.2",
|
||||||
"browserify": "^11.2.0",
|
"babel-plugin-transform-es2015-modules-umd": "^6.5.0",
|
||||||
"karma": "^0.13.10",
|
"babel-preset-es2015": "^6.5.0",
|
||||||
"karma-browserify": "^4.4.0",
|
"babelify": "^7.2.0",
|
||||||
|
"bannerify": "Vekat/bannerify#feature-option",
|
||||||
|
"browserify": "^13.0.0",
|
||||||
|
"chai": "^3.4.1",
|
||||||
|
"install": "^0.8.1",
|
||||||
|
"karma": "^1.3.0",
|
||||||
|
"karma-browserify": "^5.0.1",
|
||||||
"karma-chai": "^0.1.0",
|
"karma-chai": "^0.1.0",
|
||||||
"karma-mocha": "^0.2.0",
|
"karma-mocha": "^1.2.0",
|
||||||
"karma-phantomjs-launcher": "^0.2.1",
|
"karma-phantomjs-launcher": "^1.0.0",
|
||||||
"karma-sinon": "^1.0.4",
|
"karma-sinon": "^1.0.4",
|
||||||
"mocha": "^2.3.3",
|
"mocha": "^3.1.2",
|
||||||
"phantomjs-polyfill": "0.0.1",
|
"phantomjs-prebuilt": "^2.1.4",
|
||||||
|
"sinon": "^1.17.2",
|
||||||
"uglify-js": "^2.4.24",
|
"uglify-js": "^2.4.24",
|
||||||
"watchify": "^3.4.0",
|
"watchify": "^3.4.0"
|
||||||
"bannerify": "Vekat/bannerify#feature-option"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm run build-debug && npm run build-min",
|
"build": "npm run build-debug && npm run build-min",
|
||||||
"build-debug": "browserify src/clipboard.js -s Clipboard -p [bannerify --file .banner ] -o dist/clipboard.js",
|
"build-debug": "browserify src/clipboard.js -s Clipboard -t [babelify] -p [bannerify --file .banner ] -o dist/clipboard.js",
|
||||||
"build-min": "uglifyjs dist/clipboard.js --comments '/!/' -m screw_ie8=true -c screw_ie8=true,unused=false -o dist/clipboard.min.js",
|
"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 -o dist/clipboard.js -v",
|
"build-watch": "watchify src/clipboard.js -s Clipboard -t [babelify] -o dist/clipboard.js -v",
|
||||||
"test": "karma start --single-run",
|
"test": "karma start --single-run",
|
||||||
"prepublish": "babel src --out-dir lib --loose all"
|
"prepublish": "babel src --out-dir lib"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
readme.md
28
readme.md
@@ -3,9 +3,9 @@
|
|||||||
[](https://travis-ci.org/zenorocha/clipboard.js)
|
[](https://travis-ci.org/zenorocha/clipboard.js)
|
||||||

|

|
||||||
|
|
||||||
> 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
|
## Why
|
||||||
|
|
||||||
@@ -57,7 +57,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.
|
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
|
```html
|
||||||
<!-- Target -->
|
<!-- Target -->
|
||||||
@@ -75,7 +75,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.
|
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
|
```html
|
||||||
<!-- Target -->
|
<!-- Target -->
|
||||||
@@ -93,7 +93,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.
|
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
|
```html
|
||||||
<!-- Trigger -->
|
<!-- Trigger -->
|
||||||
@@ -125,7 +125,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
|
## Advanced Options
|
||||||
|
|
||||||
@@ -151,7 +151,7 @@ new Clipboard('.btn', {
|
|||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
Also, with 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.
|
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
|
```js
|
||||||
var clipboard = new Clipboard('.btn');
|
var clipboard = new Clipboard('.btn');
|
||||||
@@ -160,17 +160,15 @@ clipboard.destroy();
|
|||||||
|
|
||||||
## Browser Support
|
## 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"> |
|
| <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+ ✔ | 41+ ✔ | 9+ ✔ | 29+ ✔ | Nope ✘ |
|
| 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.
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
@@ -32,18 +32,12 @@ class ClipboardAction {
|
|||||||
* on the existence of `text` and `target` properties.
|
* on the existence of `text` and `target` properties.
|
||||||
*/
|
*/
|
||||||
initSelection() {
|
initSelection() {
|
||||||
if (this.text && this.target) {
|
if (this.text) {
|
||||||
throw new Error('Multiple attributes declared, use either "target" or "text"');
|
|
||||||
}
|
|
||||||
else if (this.text) {
|
|
||||||
this.selectFake();
|
this.selectFake();
|
||||||
}
|
}
|
||||||
else if (this.target) {
|
else if (this.target) {
|
||||||
this.selectTarget();
|
this.selectTarget();
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
throw new Error('Missing required attributes, use either "target" or "text"');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -51,14 +45,27 @@ class ClipboardAction {
|
|||||||
* and makes a selection on it.
|
* and makes a selection on it.
|
||||||
*/
|
*/
|
||||||
selectFake() {
|
selectFake() {
|
||||||
|
const isRTL = document.documentElement.getAttribute('dir') == 'rtl';
|
||||||
|
|
||||||
this.removeFake();
|
this.removeFake();
|
||||||
|
|
||||||
this.fakeHandler = document.body.addEventListener('click', () => this.removeFake());
|
this.fakeHandlerCallback = () => this.removeFake();
|
||||||
|
this.fakeHandler = document.body.addEventListener('click', this.fakeHandlerCallback) || true;
|
||||||
|
|
||||||
this.fakeElem = document.createElement('textarea');
|
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.position = 'absolute';
|
||||||
this.fakeElem.style.left = '-9999px';
|
this.fakeElem.style[ isRTL ? 'right' : 'left' ] = '-9999px';
|
||||||
this.fakeElem.style.top = (window.pageYOffset || document.documentElement.scrollTop) + 'px';
|
// Move element to the same position vertically
|
||||||
|
let yPosition = window.pageYOffset || document.documentElement.scrollTop;
|
||||||
|
this.fakeElem.style.top = yPosition + 'px';
|
||||||
|
|
||||||
this.fakeElem.setAttribute('readonly', '');
|
this.fakeElem.setAttribute('readonly', '');
|
||||||
this.fakeElem.value = this.text;
|
this.fakeElem.value = this.text;
|
||||||
|
|
||||||
@@ -74,8 +81,9 @@ class ClipboardAction {
|
|||||||
*/
|
*/
|
||||||
removeFake() {
|
removeFake() {
|
||||||
if (this.fakeHandler) {
|
if (this.fakeHandler) {
|
||||||
document.body.removeEventListener('click');
|
document.body.removeEventListener('click', this.fakeHandlerCallback);
|
||||||
this.fakeHandler = null;
|
this.fakeHandler = null;
|
||||||
|
this.fakeHandlerCallback = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.fakeElem) {
|
if (this.fakeElem) {
|
||||||
@@ -113,21 +121,12 @@ class ClipboardAction {
|
|||||||
* @param {Boolean} succeeded
|
* @param {Boolean} succeeded
|
||||||
*/
|
*/
|
||||||
handleResult(succeeded) {
|
handleResult(succeeded) {
|
||||||
if (succeeded) {
|
this.emitter.emit(succeeded ? 'success' : 'error', {
|
||||||
this.emitter.emit('success', {
|
action: this.action,
|
||||||
action: this.action,
|
text: this.selectedText,
|
||||||
text: this.selectedText,
|
trigger: this.trigger,
|
||||||
trigger: this.trigger,
|
clearSelection: this.clearSelection.bind(this)
|
||||||
clearSelection: this.clearSelection.bind(this)
|
});
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
this.emitter.emit('error', {
|
|
||||||
action: this.action,
|
|
||||||
trigger: this.trigger,
|
|
||||||
clearSelection: this.clearSelection.bind(this)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -169,6 +168,14 @@ class ClipboardAction {
|
|||||||
set target(target) {
|
set target(target) {
|
||||||
if (target !== undefined) {
|
if (target !== undefined) {
|
||||||
if (target && typeof target === 'object' && target.nodeType === 1) {
|
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;
|
this._target = target;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
@@ -193,4 +200,4 @@ class ClipboardAction {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default ClipboardAction;
|
module.exports = ClipboardAction;
|
||||||
|
|||||||
@@ -42,15 +42,17 @@ class Clipboard extends Emitter {
|
|||||||
* @param {Event} e
|
* @param {Event} e
|
||||||
*/
|
*/
|
||||||
onClick(e) {
|
onClick(e) {
|
||||||
|
const trigger = e.delegateTarget || e.currentTarget;
|
||||||
|
|
||||||
if (this.clipboardAction) {
|
if (this.clipboardAction) {
|
||||||
this.clipboardAction = null;
|
this.clipboardAction = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.clipboardAction = new ClipboardAction({
|
this.clipboardAction = new ClipboardAction({
|
||||||
action : this.action(e.target),
|
action : this.action(trigger),
|
||||||
target : this.target(e.target),
|
target : this.target(trigger),
|
||||||
text : this.text(e.target),
|
text : this.text(trigger),
|
||||||
trigger : e.target,
|
trigger : trigger,
|
||||||
emitter : this
|
emitter : this
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -68,13 +70,29 @@ class Clipboard extends Emitter {
|
|||||||
* @param {Element} trigger
|
* @param {Element} trigger
|
||||||
*/
|
*/
|
||||||
defaultTarget(trigger) {
|
defaultTarget(trigger) {
|
||||||
let selector = getAttributeValue('target', trigger);
|
const selector = getAttributeValue('target', trigger);
|
||||||
|
|
||||||
if (selector) {
|
if (selector) {
|
||||||
return document.querySelector(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.
|
* Default `text` lookup function.
|
||||||
* @param {Element} trigger
|
* @param {Element} trigger
|
||||||
@@ -103,7 +121,7 @@ class Clipboard extends Emitter {
|
|||||||
* @param {Element} element
|
* @param {Element} element
|
||||||
*/
|
*/
|
||||||
function getAttributeValue(suffix, element) {
|
function getAttributeValue(suffix, element) {
|
||||||
let attribute = `data-clipboard-${suffix}`;
|
const attribute = `data-clipboard-${suffix}`;
|
||||||
|
|
||||||
if (!element.hasAttribute(attribute)) {
|
if (!element.hasAttribute(attribute)) {
|
||||||
return;
|
return;
|
||||||
@@ -112,4 +130,4 @@ function getAttributeValue(suffix, element) {
|
|||||||
return element.getAttribute(attribute);
|
return element.getAttribute(attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Clipboard;
|
module.exports = Clipboard;
|
||||||
|
|||||||
@@ -35,27 +35,17 @@ describe('ClipboardAction', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe('#initSelection', () => {
|
describe('#initSelection', () => {
|
||||||
it('should throw an error since both "text" and "target" were passed', done => {
|
it('should set the position right style property', done => {
|
||||||
try {
|
// Set document direction
|
||||||
new ClipboardAction({
|
document.documentElement.setAttribute('dir', 'rtl');
|
||||||
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 => {
|
let clip = new ClipboardAction({
|
||||||
try {
|
emitter: new Emitter(),
|
||||||
new ClipboardAction();
|
text: 'foo'
|
||||||
}
|
});
|
||||||
catch(e) {
|
|
||||||
assert.equal(e.message, 'Missing required attributes, use either "target" or "text"');
|
assert.equal(clip.fakeElem.style.right, '-9999px');
|
||||||
done();
|
done();
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -9,8 +9,14 @@ describe('Clipboard', () => {
|
|||||||
global.button.setAttribute('data-clipboard-text', 'foo');
|
global.button.setAttribute('data-clipboard-text', 'foo');
|
||||||
document.body.appendChild(global.button);
|
document.body.appendChild(global.button);
|
||||||
|
|
||||||
|
global.span = document.createElement('span');
|
||||||
|
global.span.innerHTML = 'bar';
|
||||||
|
|
||||||
|
global.button.appendChild(span);
|
||||||
|
|
||||||
global.event = {
|
global.event = {
|
||||||
target: global.button
|
target: global.button,
|
||||||
|
currentTarget: global.button
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -63,7 +69,15 @@ describe('Clipboard', () => {
|
|||||||
assert.instanceOf(clipboard.clipboardAction, ClipboardAction);
|
assert.instanceOf(clipboard.clipboardAction, ClipboardAction);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throws exception target', done => {
|
it('should use an event\'s currentTarget when not equal to target', () => {
|
||||||
|
let clipboard = new Clipboard('.btn');
|
||||||
|
let bubbledEvent = { target: global.span, currentTarget: global.button };
|
||||||
|
|
||||||
|
clipboard.onClick(bubbledEvent);
|
||||||
|
assert.instanceOf(clipboard.clipboardAction, ClipboardAction);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw an exception when target is invalid', done => {
|
||||||
try {
|
try {
|
||||||
var clipboard = new Clipboard('.btn', {
|
var clipboard = new Clipboard('.btn', {
|
||||||
target: function() {
|
target: function() {
|
||||||
@@ -80,6 +94,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', () => {
|
describe('#destroy', () => {
|
||||||
it('should destroy an existing instance of ClipboardAction', () => {
|
it('should destroy an existing instance of ClipboardAction', () => {
|
||||||
let clipboard = new Clipboard('.btn');
|
let clipboard = new Clipboard('.btn');
|
||||||
|
|||||||
Reference in New Issue
Block a user