mirror of
https://github.com/zenorocha/clipboard.js.git
synced 2023-08-10 21:12:48 +03:00
Merge pull request #721 from zenorocha/feature-prettier
This commit is contained in:
commit
221efae529
@ -7,7 +7,7 @@ root = true
|
||||
[*]
|
||||
# Change these settings to your own preference
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
indent_size = 2
|
||||
|
||||
# We recommend you to keep these unchanged
|
||||
end_of_line = lf
|
||||
|
9
.prettierignore
Normal file
9
.prettierignore
Normal file
@ -0,0 +1,9 @@
|
||||
# Ignore artifacts:
|
||||
dist
|
||||
|
||||
lib
|
||||
npm-debug.log
|
||||
bower_components
|
||||
node_modules
|
||||
yarn-error.log
|
||||
yarn.lock
|
9
.prettierrc.json
Normal file
9
.prettierrc.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"printWidth": 80,
|
||||
"tabWidth": 2,
|
||||
"semi": true,
|
||||
"singleQuote": true,
|
||||
"trailingComma": "es5",
|
||||
"bracketSpacing": true,
|
||||
"arrowParens": "always"
|
||||
}
|
@ -14,9 +14,5 @@
|
||||
"/src",
|
||||
"/lib"
|
||||
],
|
||||
"keywords": [
|
||||
"clipboard",
|
||||
"copy",
|
||||
"cut"
|
||||
]
|
||||
"keywords": ["clipboard", "copy", "cut"]
|
||||
}
|
||||
|
@ -24,5 +24,6 @@ Implement your bug fix or feature, write tests to cover it and make sure all tes
|
||||
Documentation is extremely important and takes a fair deal of time and effort to write and keep updated. Please submit any and all improvements you can make to the repository's docs.
|
||||
|
||||
## Known issues
|
||||
|
||||
If you're using npm@3 you'll probably face some issues related to peerDependencies.
|
||||
https://github.com/npm/npm/issues/9204
|
||||
|
@ -1,9 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta charset="UTF-8" />
|
||||
<title>constructor-node</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
|
@ -1,9 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta charset="UTF-8" />
|
||||
<title>constructor-nodelist</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
|
@ -1,9 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta charset="UTF-8" />
|
||||
<title>constructor-selector</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
|
@ -1,9 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta charset="UTF-8" />
|
||||
<title>function-target</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
@ -18,7 +18,7 @@
|
||||
var clipboard = new ClipboardJS('.btn', {
|
||||
target: function () {
|
||||
return document.querySelector('div');
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
clipboard.on('success', function (e) {
|
||||
|
@ -1,9 +1,9 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta charset="UTF-8" />
|
||||
<title>function-text</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
@ -17,7 +17,7 @@
|
||||
var clipboard = new ClipboardJS('.btn', {
|
||||
text: function () {
|
||||
return 'to be or not to be';
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
clipboard.on('success', function (e) {
|
||||
|
@ -1,14 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta charset="UTF-8" />
|
||||
<title>target-div</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
<div>hello</div>
|
||||
<button class="btn" data-clipboard-action="copy" data-clipboard-target="div">Copy</button>
|
||||
<button
|
||||
class="btn"
|
||||
data-clipboard-action="copy"
|
||||
data-clipboard-target="div"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
|
||||
<!-- 2. Include library -->
|
||||
<script src="../dist/clipboard.min.js"></script>
|
||||
|
@ -1,14 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta charset="UTF-8" />
|
||||
<title>target-input</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
<input id="foo" type="text" value="hello">
|
||||
<button class="btn" data-clipboard-action="copy" data-clipboard-target="#foo">Copy</button>
|
||||
<input id="foo" type="text" value="hello" />
|
||||
<button
|
||||
class="btn"
|
||||
data-clipboard-action="copy"
|
||||
data-clipboard-target="#foo"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
|
||||
<!-- 2. Include library -->
|
||||
<script src="../dist/clipboard.min.js"></script>
|
||||
|
@ -1,14 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta charset="UTF-8" />
|
||||
<title>target-textarea</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
<textarea id="bar">hello</textarea>
|
||||
<button class="btn" data-clipboard-action="cut" data-clipboard-target="#bar">Cut</button>
|
||||
<button
|
||||
class="btn"
|
||||
data-clipboard-action="cut"
|
||||
data-clipboard-target="#bar"
|
||||
>
|
||||
Cut
|
||||
</button>
|
||||
|
||||
<!-- 2. Include library -->
|
||||
<script src="../dist/clipboard.min.js"></script>
|
||||
|
56
dist/clipboard.js
vendored
56
dist/clipboard.js
vendored
@ -71,7 +71,7 @@ var ClipboardAction = /*#__PURE__*/function () {
|
||||
this.target = options.target;
|
||||
this.text = options.text;
|
||||
this.trigger = options.trigger;
|
||||
this.selectedText = '';
|
||||
this.selectedText = "";
|
||||
}
|
||||
/**
|
||||
* Decides which selection strategy is going to be applied based
|
||||
@ -97,28 +97,28 @@ var ClipboardAction = /*#__PURE__*/function () {
|
||||
value: function selectFake() {
|
||||
var _this = this;
|
||||
|
||||
var isRTL = document.documentElement.getAttribute('dir') == 'rtl';
|
||||
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.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.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.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
|
||||
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 = "".concat(yPosition, "px");
|
||||
this.fakeElem.setAttribute('readonly', '');
|
||||
this.fakeElem.setAttribute("readonly", "");
|
||||
this.fakeElem.value = this.text;
|
||||
this.container.appendChild(this.fakeElem);
|
||||
this.selectedText = select_default()(this.fakeElem);
|
||||
@ -133,7 +133,7 @@ var ClipboardAction = /*#__PURE__*/function () {
|
||||
key: "removeFake",
|
||||
value: function removeFake() {
|
||||
if (this.fakeHandler) {
|
||||
this.container.removeEventListener('click', this.fakeHandlerCallback);
|
||||
this.container.removeEventListener("click", this.fakeHandlerCallback);
|
||||
this.fakeHandler = null;
|
||||
this.fakeHandlerCallback = null;
|
||||
}
|
||||
@ -178,7 +178,7 @@ var ClipboardAction = /*#__PURE__*/function () {
|
||||
}, {
|
||||
key: "handleResult",
|
||||
value: function handleResult(succeeded) {
|
||||
this.emitter.emit(succeeded ? 'success' : 'error', {
|
||||
this.emitter.emit(succeeded ? "success" : "error", {
|
||||
action: this.action,
|
||||
text: this.selectedText,
|
||||
trigger: this.trigger,
|
||||
@ -216,10 +216,10 @@ var ClipboardAction = /*#__PURE__*/function () {
|
||||
}, {
|
||||
key: "action",
|
||||
set: function set() {
|
||||
var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy';
|
||||
var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : "copy";
|
||||
this._action = action;
|
||||
|
||||
if (this._action !== 'copy' && this._action !== 'cut') {
|
||||
if (this._action !== "copy" && this._action !== "cut") {
|
||||
throw new Error('Invalid "action" value, use either "copy" or "cut"');
|
||||
}
|
||||
}
|
||||
@ -241,12 +241,12 @@ var ClipboardAction = /*#__PURE__*/function () {
|
||||
key: "target",
|
||||
set: function set(target) {
|
||||
if (target !== undefined) {
|
||||
if (target && _typeof(target) === 'object' && target.nodeType === 1) {
|
||||
if (this.action === 'copy' && target.hasAttribute('disabled')) {
|
||||
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'))) {
|
||||
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');
|
||||
}
|
||||
|
||||
@ -340,10 +340,10 @@ var Clipboard = /*#__PURE__*/function (_Emitter) {
|
||||
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;
|
||||
this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;
|
||||
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 = clipboard_typeof(options.container) === "object" ? options.container : document.body;
|
||||
}
|
||||
/**
|
||||
* Adds a click event listener to the passed trigger.
|
||||
@ -355,7 +355,7 @@ var Clipboard = /*#__PURE__*/function (_Emitter) {
|
||||
value: function listenClick(trigger) {
|
||||
var _this2 = this;
|
||||
|
||||
this.listener = listen_default()(trigger, 'click', function (e) {
|
||||
this.listener = listen_default()(trigger, "click", function (e) {
|
||||
return _this2.onClick(e);
|
||||
});
|
||||
}
|
||||
@ -390,7 +390,7 @@ var Clipboard = /*#__PURE__*/function (_Emitter) {
|
||||
}, {
|
||||
key: "defaultAction",
|
||||
value: function defaultAction(trigger) {
|
||||
return getAttributeValue('action', trigger);
|
||||
return getAttributeValue("action", trigger);
|
||||
}
|
||||
/**
|
||||
* Default `target` lookup function.
|
||||
@ -400,7 +400,7 @@ var Clipboard = /*#__PURE__*/function (_Emitter) {
|
||||
}, {
|
||||
key: "defaultTarget",
|
||||
value: function defaultTarget(trigger) {
|
||||
var selector = getAttributeValue('target', trigger);
|
||||
var selector = getAttributeValue("target", trigger);
|
||||
|
||||
if (selector) {
|
||||
return document.querySelector(selector);
|
||||
@ -420,7 +420,7 @@ var Clipboard = /*#__PURE__*/function (_Emitter) {
|
||||
* @param {Element} trigger
|
||||
*/
|
||||
value: function defaultText(trigger) {
|
||||
return getAttributeValue('text', trigger);
|
||||
return getAttributeValue("text", trigger);
|
||||
}
|
||||
/**
|
||||
* Destroy lifecycle.
|
||||
@ -439,8 +439,8 @@ var Clipboard = /*#__PURE__*/function (_Emitter) {
|
||||
}], [{
|
||||
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 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);
|
||||
|
@ -1,25 +1,25 @@
|
||||
var webpackConfig = require("./webpack.config.js");
|
||||
var webpackConfig = require('./webpack.config.js');
|
||||
|
||||
module.exports = function (karma) {
|
||||
karma.set({
|
||||
plugins: [
|
||||
"karma-webpack",
|
||||
"karma-chai",
|
||||
"karma-sinon",
|
||||
"karma-mocha",
|
||||
"karma-chrome-launcher",
|
||||
'karma-webpack',
|
||||
'karma-chai',
|
||||
'karma-sinon',
|
||||
'karma-mocha',
|
||||
'karma-chrome-launcher',
|
||||
],
|
||||
|
||||
frameworks: ["chai", "sinon", "mocha", "webpack"],
|
||||
frameworks: ['chai', 'sinon', 'mocha', 'webpack'],
|
||||
|
||||
files: [
|
||||
{ pattern: "src/**/*.js", watched: false },
|
||||
{ pattern: "test/**/*.js", watched: false },
|
||||
{ pattern: 'src/**/*.js', watched: false },
|
||||
{ pattern: 'test/**/*.js', watched: false },
|
||||
],
|
||||
|
||||
preprocessors: {
|
||||
"src/**/*.js": ["webpack"],
|
||||
"test/**/*.js": ["webpack"],
|
||||
'src/**/*.js': ['webpack'],
|
||||
'test/**/*.js': ['webpack'],
|
||||
},
|
||||
|
||||
webpack: {
|
||||
@ -28,9 +28,9 @@ module.exports = function (karma) {
|
||||
},
|
||||
|
||||
webpackMiddleware: {
|
||||
stats: "errors-only",
|
||||
stats: 'errors-only',
|
||||
},
|
||||
|
||||
browsers: ["ChromeHeadless"],
|
||||
browsers: ['ChromeHeadless'],
|
||||
});
|
||||
};
|
||||
|
7772
package-lock.json
generated
7772
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.js
10
package.js
@ -1,12 +1,12 @@
|
||||
// Package metadata for Meteor.js.
|
||||
|
||||
Package.describe({
|
||||
name: "zenorocha:clipboard",
|
||||
summary: "Modern copy to clipboard. No Flash. Just 3kb.",
|
||||
version: "2.0.6",
|
||||
git: "https://github.com/zenorocha/clipboard.js"
|
||||
name: 'zenorocha:clipboard',
|
||||
summary: 'Modern copy to clipboard. No Flash. Just 3kb.',
|
||||
version: '2.0.6',
|
||||
git: 'https://github.com/zenorocha/clipboard.js',
|
||||
});
|
||||
|
||||
Package.onUse(function (api) {
|
||||
api.addFiles("dist/clipboard.js", "client");
|
||||
api.addFiles('dist/clipboard.js', 'client');
|
||||
});
|
||||
|
11
package.json
11
package.json
@ -22,13 +22,16 @@
|
||||
"babel-loader": "^8.2.2",
|
||||
"chai": "^4.2.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"husky": "^4.3.8",
|
||||
"karma": "^6.0.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-mocha": "^2.0.1",
|
||||
"karma-sinon": "^1.0.4",
|
||||
"karma-webpack": "^5.0.0-alpha.5",
|
||||
"lint-staged": "^10.5.3",
|
||||
"mocha": "^8.2.1",
|
||||
"prettier": "2.2.1",
|
||||
"sinon": "^9.2.3",
|
||||
"uglifyjs-webpack-plugin": "^2.2.0",
|
||||
"webpack": "^5.15.0",
|
||||
@ -41,5 +44,13 @@
|
||||
"build-watch": "webpack --watch",
|
||||
"test": "karma start --single-run",
|
||||
"prepublish": "npm run build"
|
||||
},
|
||||
"husky": {
|
||||
"hooks": {
|
||||
"pre-commit": "lint-staged"
|
||||
}
|
||||
},
|
||||
"lint-staged": {
|
||||
"*.{js,css,md}": "prettier --write"
|
||||
}
|
||||
}
|
||||
|
19
readme.md
19
readme.md
@ -55,11 +55,11 @@ The value you include on this attribute needs to match another's element selecto
|
||||
|
||||
```html
|
||||
<!-- Target -->
|
||||
<input id="foo" value="https://github.com/zenorocha/clipboard.js.git">
|
||||
<input id="foo" value="https://github.com/zenorocha/clipboard.js.git" />
|
||||
|
||||
<!-- Trigger -->
|
||||
<button class="btn" data-clipboard-target="#foo">
|
||||
<img src="assets/clippy.svg" alt="Copy to clipboard">
|
||||
<img src="assets/clippy.svg" alt="Copy to clipboard" />
|
||||
</button>
|
||||
```
|
||||
|
||||
@ -91,7 +91,10 @@ Truth is, you don't even need another element to copy its content from. You can
|
||||
|
||||
```html
|
||||
<!-- Trigger -->
|
||||
<button class="btn" data-clipboard-text="Just because you can doesn't mean you should — clipboard.js">
|
||||
<button
|
||||
class="btn"
|
||||
data-clipboard-text="Just because you can doesn't mean you should — clipboard.js"
|
||||
>
|
||||
Copy to clipboard
|
||||
</button>
|
||||
```
|
||||
@ -137,7 +140,7 @@ For instance, if you want to dynamically set a `target`, you'll need to return a
|
||||
new ClipboardJS('.btn', {
|
||||
target: function (trigger) {
|
||||
return trigger.nextElementSibling;
|
||||
}
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
@ -147,7 +150,7 @@ If you want to dynamically set a `text`, you'll return a String.
|
||||
new ClipboardJS('.btn', {
|
||||
text: function (trigger) {
|
||||
return trigger.getAttribute('aria-label');
|
||||
}
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
@ -155,7 +158,7 @@ For use in Bootstrap Modals or with any other library that changes the focus you
|
||||
|
||||
```js
|
||||
new ClipboardJS('.btn', {
|
||||
container: document.getElementById('modal')
|
||||
container: document.getElementById('modal'),
|
||||
});
|
||||
```
|
||||
|
||||
@ -171,7 +174,7 @@ clipboard.destroy();
|
||||
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](https://caniuse.com/#search=selection) while the second one is supported in the following browsers.
|
||||
|
||||
| <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+ ✔ |
|
||||
|
||||
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.
|
||||
@ -180,7 +183,7 @@ You can also check if clipboard.js is supported or not by running `ClipboardJS.i
|
||||
|
||||
## 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.*
|
||||
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/).
|
||||
|
||||
|
@ -35,8 +35,7 @@ class ClipboardAction {
|
||||
initSelection() {
|
||||
if (this.text) {
|
||||
this.selectFake();
|
||||
}
|
||||
else if (this.target) {
|
||||
} else if (this.target) {
|
||||
this.selectTarget();
|
||||
}
|
||||
}
|
||||
@ -51,7 +50,9 @@ class ClipboardAction {
|
||||
this.removeFake();
|
||||
|
||||
this.fakeHandlerCallback = () => this.removeFake();
|
||||
this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true;
|
||||
this.fakeHandler =
|
||||
this.container.addEventListener('click', this.fakeHandlerCallback) ||
|
||||
true;
|
||||
|
||||
this.fakeElem = document.createElement('textarea');
|
||||
// Prevent zooming on iOS
|
||||
@ -109,8 +110,7 @@ class ClipboardAction {
|
||||
|
||||
try {
|
||||
succeeded = document.execCommand(this.action);
|
||||
}
|
||||
catch (err) {
|
||||
} catch (err) {
|
||||
succeeded = false;
|
||||
}
|
||||
|
||||
@ -126,7 +126,7 @@ class ClipboardAction {
|
||||
action: this.action,
|
||||
text: this.selectedText,
|
||||
trigger: this.trigger,
|
||||
clearSelection: this.clearSelection.bind(this)
|
||||
clearSelection: this.clearSelection.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
@ -170,16 +170,22 @@ class ClipboardAction {
|
||||
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');
|
||||
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');
|
||||
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 {
|
||||
} else {
|
||||
throw new Error('Invalid "target" value, use a valid Element');
|
||||
}
|
||||
}
|
||||
|
@ -24,10 +24,18 @@ 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.container = (typeof options.container === 'object') ? options.container : document.body;
|
||||
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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,7 +63,7 @@ class Clipboard extends Emitter {
|
||||
text: this.text(trigger),
|
||||
container: this.container,
|
||||
trigger: trigger,
|
||||
emitter : this
|
||||
emitter: this,
|
||||
});
|
||||
}
|
||||
|
||||
@ -85,7 +93,7 @@ class Clipboard extends Emitter {
|
||||
* @param {String} [action]
|
||||
*/
|
||||
static isSupported(action = ['copy', 'cut']) {
|
||||
const actions = (typeof action === 'string') ? [action] : action;
|
||||
const actions = typeof action === 'string' ? [action] : action;
|
||||
let support = !!document.queryCommandSupported;
|
||||
|
||||
actions.forEach((action) => {
|
||||
@ -116,7 +124,6 @@ class Clipboard extends Emitter {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Helper function to retrieve attribute value.
|
||||
* @param {String} suffix
|
||||
|
@ -23,7 +23,7 @@ describe('ClipboardAction', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
text: 'foo'
|
||||
text: 'foo',
|
||||
});
|
||||
|
||||
assert.property(clip, 'action');
|
||||
@ -37,14 +37,14 @@ describe('ClipboardAction', () => {
|
||||
});
|
||||
|
||||
describe('#initSelection', () => {
|
||||
it('should set the position right style property', 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'
|
||||
text: 'foo',
|
||||
});
|
||||
|
||||
assert.equal(clip.fakeElem.style.right, '-9999px');
|
||||
@ -53,28 +53,29 @@ describe('ClipboardAction', () => {
|
||||
});
|
||||
|
||||
describe('#set action', () => {
|
||||
it('should throw an error since "action" is invalid', done => {
|
||||
it('should throw an error since "action" is invalid', (done) => {
|
||||
try {
|
||||
new ClipboardAction({
|
||||
text: 'foo',
|
||||
action: 'paste'
|
||||
action: 'paste',
|
||||
});
|
||||
}
|
||||
catch(e) {
|
||||
assert.equal(e.message, 'Invalid "action" value, use either "copy" or "cut"');
|
||||
} catch (e) {
|
||||
assert.equal(
|
||||
e.message,
|
||||
'Invalid "action" value, use either "copy" or "cut"'
|
||||
);
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('#set target', () => {
|
||||
it('should throw an error since "target" do not match any element', done => {
|
||||
it('should throw an error since "target" do not match any element', (done) => {
|
||||
try {
|
||||
new ClipboardAction({
|
||||
target: document.querySelector('#foo')
|
||||
target: document.querySelector('#foo'),
|
||||
});
|
||||
}
|
||||
catch(e) {
|
||||
} catch (e) {
|
||||
assert.equal(e.message, 'Invalid "target" value, use a valid Element');
|
||||
done();
|
||||
}
|
||||
@ -86,7 +87,7 @@ describe('ClipboardAction', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
text: 'blah'
|
||||
text: 'blah',
|
||||
});
|
||||
|
||||
assert.equal(clip.selectedText, clip.fakeElem.value);
|
||||
@ -98,7 +99,7 @@ describe('ClipboardAction', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
text: 'blah'
|
||||
text: 'blah',
|
||||
});
|
||||
|
||||
clip.removeFake();
|
||||
@ -112,7 +113,7 @@ describe('ClipboardAction', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#input')
|
||||
target: document.querySelector('#input'),
|
||||
});
|
||||
|
||||
assert.equal(clip.selectedText, clip.target.value);
|
||||
@ -122,7 +123,7 @@ describe('ClipboardAction', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#paragraph')
|
||||
target: document.querySelector('#paragraph'),
|
||||
});
|
||||
|
||||
assert.equal(clip.selectedText, clip.target.textContent);
|
||||
@ -138,7 +139,7 @@ describe('ClipboardAction', () => {
|
||||
global.stub.restore();
|
||||
});
|
||||
|
||||
it('should fire a success event on browsers that support copy command', done => {
|
||||
it('should fire a success event on browsers that support copy command', (done) => {
|
||||
global.stub.returns(true);
|
||||
|
||||
let emitter = new Emitter();
|
||||
@ -149,11 +150,11 @@ describe('ClipboardAction', () => {
|
||||
|
||||
let clip = new ClipboardAction({
|
||||
emitter,
|
||||
target: document.querySelector('#input')
|
||||
target: document.querySelector('#input'),
|
||||
});
|
||||
});
|
||||
|
||||
it('should fire an error event on browsers that support copy command', done => {
|
||||
it('should fire an error event on browsers that support copy command', (done) => {
|
||||
global.stub.returns(false);
|
||||
|
||||
let emitter = new Emitter();
|
||||
@ -164,17 +165,17 @@ describe('ClipboardAction', () => {
|
||||
|
||||
let clip = new ClipboardAction({
|
||||
emitter,
|
||||
target: document.querySelector('#input')
|
||||
target: document.querySelector('#input'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#handleResult', () => {
|
||||
it('should fire a success event with certain properties', done => {
|
||||
it('should fire a success event with certain properties', (done) => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#input')
|
||||
target: document.querySelector('#input'),
|
||||
});
|
||||
|
||||
clip.emitter.on('success', (e) => {
|
||||
@ -189,11 +190,11 @@ describe('ClipboardAction', () => {
|
||||
clip.handleResult(true);
|
||||
});
|
||||
|
||||
it('should fire a error event with certain properties', done => {
|
||||
it('should fire a error event with certain properties', (done) => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#input')
|
||||
target: document.querySelector('#input'),
|
||||
});
|
||||
|
||||
clip.emitter.on('error', (e) => {
|
||||
@ -213,7 +214,7 @@ describe('ClipboardAction', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#input')
|
||||
target: document.querySelector('#input'),
|
||||
});
|
||||
|
||||
clip.clearSelection();
|
||||
@ -231,7 +232,7 @@ describe('ClipboardAction', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
text: 'blah'
|
||||
text: 'blah',
|
||||
});
|
||||
|
||||
clip.selectFake();
|
||||
|
@ -16,7 +16,7 @@ describe('Clipboard', () => {
|
||||
|
||||
global.event = {
|
||||
target: global.button,
|
||||
currentTarget: global.button
|
||||
currentTarget: global.button,
|
||||
};
|
||||
});
|
||||
|
||||
@ -31,7 +31,7 @@ describe('Clipboard', () => {
|
||||
|
||||
it('should set action as a function', () => {
|
||||
let clipboard = new Clipboard('.btn', {
|
||||
action: global.fn
|
||||
action: global.fn,
|
||||
});
|
||||
|
||||
assert.equal(global.fn, clipboard.action);
|
||||
@ -39,7 +39,7 @@ describe('Clipboard', () => {
|
||||
|
||||
it('should set target as a function', () => {
|
||||
let clipboard = new Clipboard('.btn', {
|
||||
target: global.fn
|
||||
target: global.fn,
|
||||
});
|
||||
|
||||
assert.equal(global.fn, clipboard.target);
|
||||
@ -47,7 +47,7 @@ describe('Clipboard', () => {
|
||||
|
||||
it('should set text as a function', () => {
|
||||
let clipboard = new Clipboard('.btn', {
|
||||
text: global.fn
|
||||
text: global.fn,
|
||||
});
|
||||
|
||||
assert.equal(global.fn, clipboard.text);
|
||||
@ -55,7 +55,7 @@ describe('Clipboard', () => {
|
||||
|
||||
it('should set container as an object', () => {
|
||||
let clipboard = new Clipboard('.btn', {
|
||||
container: document.body
|
||||
container: document.body,
|
||||
});
|
||||
|
||||
assert.equal(document.body, clipboard.container);
|
||||
@ -83,25 +83,27 @@ describe('Clipboard', () => {
|
||||
assert.instanceOf(clipboard.clipboardAction, ClipboardAction);
|
||||
});
|
||||
|
||||
it('should use an event\'s currentTarget when not equal to target', () => {
|
||||
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 };
|
||||
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 => {
|
||||
it('should throw an exception when target is invalid', (done) => {
|
||||
try {
|
||||
const clipboard = new Clipboard('.btn', {
|
||||
target() {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
clipboard.onClick(global.event);
|
||||
}
|
||||
catch(e) {
|
||||
} catch (e) {
|
||||
assert.equal(e.message, 'Invalid "target" value, use a valid Element');
|
||||
done();
|
||||
}
|
||||
|
@ -19,12 +19,10 @@ module.exports = {
|
||||
library: 'ClipboardJS',
|
||||
globalObject: 'this',
|
||||
libraryExport: 'default',
|
||||
libraryTarget: 'umd'
|
||||
libraryTarget: 'umd',
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader'}
|
||||
]
|
||||
rules: [{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }],
|
||||
},
|
||||
optimization: {
|
||||
minimize: production,
|
||||
@ -36,11 +34,12 @@ module.exports = {
|
||||
keep_fnames: false,
|
||||
output: {
|
||||
beautify: false,
|
||||
comments: (node, {value, type}) => type == 'comment2' && value.startsWith('!')
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
comments: (node, { value, type }) =>
|
||||
type == 'comment2' && value.startsWith('!'),
|
||||
},
|
||||
plugins: [new webpack.BannerPlugin({ banner })]
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
plugins: [new webpack.BannerPlugin({ banner })],
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user