mirror of
https://github.com/zenorocha/clipboard.js.git
synced 2023-08-10 21:12:48 +03:00
Compare commits
91 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0c3bce265f | ||
|
|
3c0dfe5a38 | ||
|
|
f960b0d20a | ||
|
|
b6e6b80ab0 | ||
|
|
a55c9ac513 | ||
|
|
39e622456c | ||
|
|
f8c322f163 | ||
|
|
f42b57067d | ||
|
|
4065080a17 | ||
|
|
5ab50475e0 | ||
|
|
e1394b3b8c | ||
|
|
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 | ||
|
|
bb6c4c0e7c | ||
|
|
03ee9829e0 | ||
|
|
24f6e1f541 | ||
|
|
5bdfff6375 | ||
|
|
4b73122f81 | ||
|
|
171f438f22 |
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
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
lib
|
||||
npm-debug.log
|
||||
bower_components
|
||||
node_modules
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
/.*
|
||||
/bower.json
|
||||
/karma.conf.js
|
||||
/src
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "clipboard",
|
||||
"version": "1.5.0",
|
||||
"version": "1.7.1",
|
||||
"description": "Modern copy to clipboard. No Flash. Just 2kb",
|
||||
"license": "MIT",
|
||||
"main": "dist/clipboard.js",
|
||||
@@ -10,7 +10,9 @@
|
||||
"/test/",
|
||||
"/.*",
|
||||
"/bower.json",
|
||||
"/karma.conf.js"
|
||||
"/karma.conf.js",
|
||||
"/src",
|
||||
"/lib"
|
||||
],
|
||||
"keywords": [
|
||||
"clipboard",
|
||||
|
||||
@@ -3,10 +3,13 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>constructor-node</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
<button id="btn" data-clipboard-text="1">Copy</button>
|
||||
<div id="btn" data-clipboard-text="1">
|
||||
<span>Copy</span>
|
||||
</div>
|
||||
|
||||
<!-- 2. Include library -->
|
||||
<script src="../dist/clipboard.min.js"></script>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>constructor-nodelist</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>constructor-selector</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>function-target</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>function-text</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>target-div</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>target-input</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>target-textarea</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
|
||||
883
dist/clipboard.js
vendored
883
dist/clipboard.js
vendored
File diff suppressed because it is too large
Load Diff
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({
|
||||
name: "zenorocha:clipboard",
|
||||
summary: "Modern copy to clipboard. No Flash. Just 2kb.",
|
||||
version: "1.5.0",
|
||||
version: "1.7.1",
|
||||
git: "https://github.com/zenorocha/clipboard.js"
|
||||
});
|
||||
|
||||
Package.onUse(function(api) {
|
||||
api.addFiles("dist/clipboard.min.js", "client");
|
||||
api.addFiles("dist/clipboard.js", "client");
|
||||
});
|
||||
|
||||
61
package.json
61
package.json
@@ -1,53 +1,48 @@
|
||||
{
|
||||
"name": "clipboard",
|
||||
"version": "1.5.0",
|
||||
"version": "1.7.1",
|
||||
"description": "Modern copy to clipboard. No Flash. Just 2kb",
|
||||
"repository": "zenorocha/clipboard.js",
|
||||
"license": "MIT",
|
||||
"main": "dist/clipboard.js",
|
||||
"browser": "src/clipboard.js",
|
||||
"browserify": {
|
||||
"transform": [
|
||||
[
|
||||
"babelify",
|
||||
{
|
||||
"loose": "all"
|
||||
}
|
||||
]
|
||||
]
|
||||
},
|
||||
"main": "lib/clipboard.js",
|
||||
"keywords": [
|
||||
"clipboard",
|
||||
"copy",
|
||||
"cut"
|
||||
],
|
||||
"dependencies": {
|
||||
"babelify": "^6.3.0",
|
||||
"browserify": "^11.2.0",
|
||||
"good-listener": "^1.1.2",
|
||||
"select": "^1.0.3",
|
||||
"tiny-emitter": "^1.0.0"
|
||||
"good-listener": "^1.2.2",
|
||||
"select": "^1.1.2",
|
||||
"tiny-emitter": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"karma": "^0.13.10",
|
||||
"karma-browserify": "^4.4.0",
|
||||
"babel-cli": "^6.24.1",
|
||||
"babel-core": "^6.24.1",
|
||||
"babel-plugin-transform-es2015-modules-umd": "^6.24.1",
|
||||
"babel-preset-es2015": "^6.24.1",
|
||||
"babelify": "^7.3.0",
|
||||
"bannerify": "Vekat/bannerify#feature-option",
|
||||
"browserify": "^14.3.0",
|
||||
"chai": "^3.5.0",
|
||||
"install": "^0.9.6",
|
||||
"karma": "^1.6.0",
|
||||
"karma-browserify": "^5.1.1",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-mocha": "^0.2.0",
|
||||
"karma-phantomjs-launcher": "^0.2.1",
|
||||
"karma-sinon": "^1.0.4",
|
||||
"mocha": "^2.3.3",
|
||||
"phantomjs-polyfill": "0.0.1",
|
||||
"uglify-js": "^2.4.24",
|
||||
"watchify": "^3.4.0",
|
||||
"bannerify": "Vekat/bannerify#feature-option"
|
||||
"karma-mocha": "^1.3.0",
|
||||
"karma-phantomjs-launcher": "^1.0.4",
|
||||
"karma-sinon": "^1.0.5",
|
||||
"mocha": "^3.3.0",
|
||||
"phantomjs-prebuilt": "^2.1.14",
|
||||
"sinon": "^2.2.0",
|
||||
"uglify-js": "^2.8.22",
|
||||
"watchify": "^3.9.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "npm run build-debug && npm run build-min",
|
||||
"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-watch": "watchify src/clipboard.js -s Clipboard -o dist/clipboard.js -v",
|
||||
"test": "npm run test-browser && npm run test-server",
|
||||
"test-browser": "karma start --single-run",
|
||||
"test-server": "mocha test/module-systems.js"
|
||||
"build-watch": "watchify src/clipboard.js -s Clipboard -t [babelify] -o dist/clipboard.js -v",
|
||||
"test": "karma start --single-run",
|
||||
"prepublish": "babel src --out-dir lib"
|
||||
}
|
||||
}
|
||||
|
||||
54
readme.md
54
readme.md
@@ -3,9 +3,9 @@
|
||||
[](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
|
||||
|
||||
@@ -21,13 +21,7 @@ You can get it on npm.
|
||||
npm install clipboard --save
|
||||
```
|
||||
|
||||
Or bower, too.
|
||||
|
||||
```
|
||||
bower install clipboard --save
|
||||
```
|
||||
|
||||
If you're not into package management, just [download a ZIP](https://github.com/zenorocha/clipboard.js/archive/master.zip) file.
|
||||
Or if you're not into package management, just [download a ZIP](https://github.com/zenorocha/clipboard.js/archive/master.zip) file.
|
||||
|
||||
## Setup
|
||||
|
||||
@@ -57,7 +51,7 @@ A pretty common use case is to copy content from another element. You can do tha
|
||||
|
||||
The value you include on this attribute needs to match another's element selector.
|
||||
|
||||
<a href="http://clipboardjs.com/#example-target"><img width="473" alt="example-2" src="https://cloud.githubusercontent.com/assets/398893/9983467/a4946aaa-5fb1-11e5-9780-f09fcd7ca6c8.png"></a>
|
||||
<a href="https://clipboardjs.com/#example-target"><img width="473" alt="example-2" src="https://cloud.githubusercontent.com/assets/398893/9983467/a4946aaa-5fb1-11e5-9780-f09fcd7ca6c8.png"></a>
|
||||
|
||||
```html
|
||||
<!-- Target -->
|
||||
@@ -75,7 +69,7 @@ Additionally, you can define a `data-clipboard-action` attribute to specify if y
|
||||
|
||||
If you omit this attribute, `copy` will be used by default.
|
||||
|
||||
<a href="http://clipboardjs.com/#example-action"><img width="473" alt="example-3" src="https://cloud.githubusercontent.com/assets/398893/10000358/7df57b9c-6050-11e5-9cd1-fbc51d2fd0a7.png"></a>
|
||||
<a href="https://clipboardjs.com/#example-action"><img width="473" alt="example-3" src="https://cloud.githubusercontent.com/assets/398893/10000358/7df57b9c-6050-11e5-9cd1-fbc51d2fd0a7.png"></a>
|
||||
|
||||
```html
|
||||
<!-- Target -->
|
||||
@@ -93,7 +87,7 @@ As you may expect, the `cut` action only works on `<input>` or `<textarea>` elem
|
||||
|
||||
Truth is, you don't even need another element to copy its content from. You can just include a `data-clipboard-text` attribute in your trigger element.
|
||||
|
||||
<a href="http://clipboardjs.com/#example-text"><img width="147" alt="example-1" src="https://cloud.githubusercontent.com/assets/398893/10000347/6e16cf8c-6050-11e5-9883-1c5681f9ec45.png"></a>
|
||||
<a href="https://clipboardjs.com/#example-text"><img width="147" alt="example-1" src="https://cloud.githubusercontent.com/assets/398893/10000347/6e16cf8c-6050-11e5-9883-1c5681f9ec45.png"></a>
|
||||
|
||||
```html
|
||||
<!-- Trigger -->
|
||||
@@ -125,7 +119,13 @@ clipboard.on('error', function(e) {
|
||||
});
|
||||
```
|
||||
|
||||
For a live demonstration, open this [site](http://clipboardjs.com/) and just your console :)
|
||||
For a live demonstration, go to this [site](https://clipboardjs.com/) and open your console.
|
||||
|
||||
## Tooltips
|
||||
|
||||
Each application has different design needs, that's why clipboard.js does not include any CSS or built-in tooltip solution.
|
||||
|
||||
The tooltips you see on the [demo site](https://clipboardjs.com/) were built using [GitHub's Primer](https://github.com/primer/primer-css/tree/master/modules/primer-tooltips). You may want to check that out if you're looking for a similar look and feel.
|
||||
|
||||
## Advanced Options
|
||||
|
||||
@@ -151,7 +151,15 @@ 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.
|
||||
For use in Bootstrap Modals or with any other library that changes the focus you'll want to set the focused element as the `container` value.
|
||||
|
||||
```js
|
||||
new Clipboard('.btn', {
|
||||
container: document.getElementById('modal')
|
||||
});
|
||||
```
|
||||
|
||||
Also, if you are working with single page apps, you may want to manage the lifecycle of the DOM more precisely. Here's how you clean up the events and objects that we create.
|
||||
|
||||
```js
|
||||
var clipboard = new Clipboard('.btn');
|
||||
@@ -160,17 +168,21 @@ clipboard.destroy();
|
||||
|
||||
## Browser Support
|
||||
|
||||
This library relies on both [Selection](https://developer.mozilla.org/en-US/docs/Web/API/Selection) and [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand) APIs. The second one is supported in the following browsers.
|
||||
This library relies on both [Selection](https://developer.mozilla.org/en-US/docs/Web/API/Selection) and [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand) APIs. The first one is [supported by all browsers](http://caniuse.com/#search=selection) while the second one is supported in the following browsers.
|
||||
|
||||
| <img src="http://clipboardjs.com/assets/images/chrome.png" width="48px" height="48px" alt="Chrome logo"> | <img src="http://clipboardjs.com/assets/images/firefox.png" width="48px" height="48px" alt="Firefox logo"> | <img src="http://clipboardjs.com/assets/images/ie.png" width="48px" height="48px" alt="Internet Explorer logo"> | <img src="http://clipboardjs.com/assets/images/opera.png" width="48px" height="48px" alt="Opera logo"> | <img src="http://clipboardjs.com/assets/images/safari.png" width="48px" height="48px" alt="Safari logo"> |
|
||||
|:---:|:---:|:---:|:---:|:---:|
|
||||
| 42+ ✔ | 41+ ✔ | 9+ ✔ | 29+ ✔ | Nope ✘ |
|
||||
| <img src="https://clipboardjs.com/assets/images/chrome.png" width="48px" height="48px" alt="Chrome logo"> | <img src="https://clipboardjs.com/assets/images/edge.png" width="48px" height="48px" alt="Edge logo"> | <img src="https://clipboardjs.com/assets/images/firefox.png" width="48px" height="48px" alt="Firefox logo"> | <img src="https://clipboardjs.com/assets/images/ie.png" width="48px" height="48px" alt="Internet Explorer logo"> | <img src="https://clipboardjs.com/assets/images/opera.png" width="48px" height="48px" alt="Opera logo"> | <img src="https://clipboardjs.com/assets/images/safari.png" width="48px" height="48px" alt="Safari logo"> |
|
||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
||||
| 42+ ✔ | 12+ ✔ | 41+ ✔ | 9+ ✔ | 29+ ✔ | 10+ ✔ |
|
||||
|
||||
Although copy/cut operations with [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand) aren't supported on Safari yet (including mobile), it gracefully degrades because [Selection](https://developer.mozilla.org/en-US/docs/Web/API/Selection) is supported.
|
||||
The good news is that clipboard.js gracefully degrades if you need to support older browsers. All you have to do is show a tooltip saying `Copied!` when `success` event is called and `Press Ctrl+C to copy` when `error` event is called because the text is already selected.
|
||||
|
||||
That means you can show a tooltip saying `Copied!` when `success` event is called and `Press Ctrl+C to copy` when `error` event is called because the text is already selected.
|
||||
You can also check if clipboard.js is supported or not by running `Clipboard.isSupported()`, that way you can hide copy/cut buttons from the UI.
|
||||
|
||||
For a live demonstration, open this [site](http://clipboardjs.com) on Safari.
|
||||
## Bonus
|
||||
|
||||
A browser extension that adds a "copy to clipboard" button to every code block on *GitHub, MDN, Gist, StackOverflow, StackExchange, npm, and even Medium.*
|
||||
|
||||
Install for [Chrome](https://chrome.google.com/webstore/detail/codecopy/fkbfebkcoelajmhanocgppanfoojcdmg) and [Firefox](https://addons.mozilla.org/en-US/firefox/addon/codecopy/).
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -18,11 +18,12 @@ class ClipboardAction {
|
||||
* @param {Object} options
|
||||
*/
|
||||
resolveOptions(options = {}) {
|
||||
this.action = options.action;
|
||||
this.emitter = options.emitter;
|
||||
this.target = options.target;
|
||||
this.text = options.text;
|
||||
this.trigger = options.trigger;
|
||||
this.action = options.action;
|
||||
this.container = options.container;
|
||||
this.emitter = options.emitter;
|
||||
this.target = options.target;
|
||||
this.text = options.text;
|
||||
this.trigger = options.trigger;
|
||||
|
||||
this.selectedText = '';
|
||||
}
|
||||
@@ -32,18 +33,12 @@ class ClipboardAction {
|
||||
* on the existence of `text` and `target` properties.
|
||||
*/
|
||||
initSelection() {
|
||||
if (this.text && this.target) {
|
||||
throw new Error('Multiple attributes declared, use either "target" or "text"');
|
||||
}
|
||||
else if (this.text) {
|
||||
if (this.text) {
|
||||
this.selectFake();
|
||||
}
|
||||
else if (this.target) {
|
||||
this.selectTarget();
|
||||
}
|
||||
else {
|
||||
throw new Error('Missing required attributes, use either "target" or "text"');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -51,18 +46,31 @@ class ClipboardAction {
|
||||
* and makes a selection on it.
|
||||
*/
|
||||
selectFake() {
|
||||
const isRTL = document.documentElement.getAttribute('dir') == 'rtl';
|
||||
|
||||
this.removeFake();
|
||||
|
||||
this.fakeHandler = document.body.addEventListener('click', () => this.removeFake());
|
||||
this.fakeHandlerCallback = () => this.removeFake();
|
||||
this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true;
|
||||
|
||||
this.fakeElem = document.createElement('textarea');
|
||||
// Prevent zooming on iOS
|
||||
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.left = '-9999px';
|
||||
this.fakeElem.style.top = (window.pageYOffset || document.documentElement.scrollTop) + 'px';
|
||||
this.fakeElem.style[ isRTL ? 'right' : 'left' ] = '-9999px';
|
||||
// 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.value = this.text;
|
||||
|
||||
document.body.appendChild(this.fakeElem);
|
||||
this.container.appendChild(this.fakeElem);
|
||||
|
||||
this.selectedText = select(this.fakeElem);
|
||||
this.copyText();
|
||||
@@ -74,12 +82,13 @@ class ClipboardAction {
|
||||
*/
|
||||
removeFake() {
|
||||
if (this.fakeHandler) {
|
||||
document.body.removeEventListener('click');
|
||||
this.container.removeEventListener('click', this.fakeHandlerCallback);
|
||||
this.fakeHandler = null;
|
||||
this.fakeHandlerCallback = null;
|
||||
}
|
||||
|
||||
if (this.fakeElem) {
|
||||
document.body.removeChild(this.fakeElem);
|
||||
this.container.removeChild(this.fakeElem);
|
||||
this.fakeElem = null;
|
||||
}
|
||||
}
|
||||
@@ -113,29 +122,20 @@ class ClipboardAction {
|
||||
* @param {Boolean} succeeded
|
||||
*/
|
||||
handleResult(succeeded) {
|
||||
if (succeeded) {
|
||||
this.emitter.emit('success', {
|
||||
action: this.action,
|
||||
text: this.selectedText,
|
||||
trigger: this.trigger,
|
||||
clearSelection: this.clearSelection.bind(this)
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.emitter.emit('error', {
|
||||
action: this.action,
|
||||
trigger: this.trigger,
|
||||
clearSelection: this.clearSelection.bind(this)
|
||||
});
|
||||
}
|
||||
this.emitter.emit(succeeded ? 'success' : 'error', {
|
||||
action: this.action,
|
||||
text: this.selectedText,
|
||||
trigger: this.trigger,
|
||||
clearSelection: this.clearSelection.bind(this)
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes current selection and focus from `target` element.
|
||||
* Moves focus away from `target` and back to the trigger, removes current selection.
|
||||
*/
|
||||
clearSelection() {
|
||||
if (this.target) {
|
||||
this.target.blur();
|
||||
if (this.trigger) {
|
||||
this.trigger.focus();
|
||||
}
|
||||
|
||||
window.getSelection().removeAllRanges();
|
||||
@@ -169,6 +169,14 @@ class ClipboardAction {
|
||||
set target(target) {
|
||||
if (target !== undefined) {
|
||||
if (target && typeof target === 'object' && target.nodeType === 1) {
|
||||
if (this.action === 'copy' && target.hasAttribute('disabled')) {
|
||||
throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');
|
||||
}
|
||||
|
||||
if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {
|
||||
throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');
|
||||
}
|
||||
|
||||
this._target = target;
|
||||
}
|
||||
else {
|
||||
@@ -193,4 +201,4 @@ class ClipboardAction {
|
||||
}
|
||||
}
|
||||
|
||||
export default ClipboardAction;
|
||||
module.exports = ClipboardAction;
|
||||
|
||||
@@ -24,9 +24,10 @@ class Clipboard extends Emitter {
|
||||
* @param {Object} options
|
||||
*/
|
||||
resolveOptions(options = {}) {
|
||||
this.action = (typeof options.action === 'function') ? options.action : this.defaultAction;
|
||||
this.target = (typeof options.target === 'function') ? options.target : this.defaultTarget;
|
||||
this.text = (typeof options.text === 'function') ? options.text : this.defaultText;
|
||||
this.action = (typeof options.action === 'function') ? options.action : this.defaultAction;
|
||||
this.target = (typeof options.target === 'function') ? options.target : this.defaultTarget;
|
||||
this.text = (typeof options.text === 'function') ? options.text : this.defaultText;
|
||||
this.container = (typeof options.container === 'object') ? options.container : document.body;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -42,16 +43,19 @@ class Clipboard extends Emitter {
|
||||
* @param {Event} e
|
||||
*/
|
||||
onClick(e) {
|
||||
const trigger = e.delegateTarget || e.currentTarget;
|
||||
|
||||
if (this.clipboardAction) {
|
||||
this.clipboardAction = null;
|
||||
}
|
||||
|
||||
this.clipboardAction = new ClipboardAction({
|
||||
action : this.action(e.target),
|
||||
target : this.target(e.target),
|
||||
text : this.text(e.target),
|
||||
trigger : e.target,
|
||||
emitter : this
|
||||
action : this.action(trigger),
|
||||
target : this.target(trigger),
|
||||
text : this.text(trigger),
|
||||
container : this.container,
|
||||
trigger : trigger,
|
||||
emitter : this
|
||||
});
|
||||
}
|
||||
|
||||
@@ -68,13 +72,29 @@ class Clipboard extends Emitter {
|
||||
* @param {Element} trigger
|
||||
*/
|
||||
defaultTarget(trigger) {
|
||||
let selector = getAttributeValue('target', trigger);
|
||||
const selector = getAttributeValue('target', trigger);
|
||||
|
||||
if (selector) {
|
||||
return document.querySelector(selector);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the support of the given action, or all actions if no action is
|
||||
* given.
|
||||
* @param {String} [action]
|
||||
*/
|
||||
static isSupported(action = ['copy', 'cut']) {
|
||||
const actions = (typeof action === 'string') ? [action] : action;
|
||||
let support = !!document.queryCommandSupported;
|
||||
|
||||
actions.forEach((action) => {
|
||||
support = support && !!document.queryCommandSupported(action);
|
||||
});
|
||||
|
||||
return support;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default `text` lookup function.
|
||||
* @param {Element} trigger
|
||||
@@ -103,7 +123,7 @@ class Clipboard extends Emitter {
|
||||
* @param {Element} element
|
||||
*/
|
||||
function getAttributeValue(suffix, element) {
|
||||
let attribute = `data-clipboard-${suffix}`;
|
||||
const attribute = `data-clipboard-${suffix}`;
|
||||
|
||||
if (!element.hasAttribute(attribute)) {
|
||||
return;
|
||||
@@ -112,4 +132,4 @@ function getAttributeValue(suffix, element) {
|
||||
return element.getAttribute(attribute);
|
||||
}
|
||||
|
||||
export default Clipboard;
|
||||
module.exports = Clipboard;
|
||||
|
||||
@@ -22,10 +22,12 @@ describe('ClipboardAction', () => {
|
||||
it('should set base properties', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
text: 'foo'
|
||||
});
|
||||
|
||||
assert.property(clip, 'action');
|
||||
assert.property(clip, 'container');
|
||||
assert.property(clip, 'emitter');
|
||||
assert.property(clip, 'target');
|
||||
assert.property(clip, 'text');
|
||||
@@ -35,27 +37,18 @@ describe('ClipboardAction', () => {
|
||||
});
|
||||
|
||||
describe('#initSelection', () => {
|
||||
it('should throw an error since both "text" and "target" were passed', done => {
|
||||
try {
|
||||
new ClipboardAction({
|
||||
text: 'foo',
|
||||
target: document.querySelector('#input')
|
||||
});
|
||||
}
|
||||
catch(e) {
|
||||
assert.equal(e.message, 'Multiple attributes declared, use either "target" or "text"');
|
||||
done();
|
||||
}
|
||||
});
|
||||
it('should set the position right style property', done => {
|
||||
// Set document direction
|
||||
document.documentElement.setAttribute('dir', 'rtl');
|
||||
|
||||
it('should throw an error since neither "text" nor "target" were passed', done => {
|
||||
try {
|
||||
new ClipboardAction();
|
||||
}
|
||||
catch(e) {
|
||||
assert.equal(e.message, 'Missing required attributes, use either "target" or "text"');
|
||||
done();
|
||||
}
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
text: 'foo'
|
||||
});
|
||||
|
||||
assert.equal(clip.fakeElem.style.right, '-9999px');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -92,6 +85,7 @@ describe('ClipboardAction', () => {
|
||||
it('should create a fake element and select its value', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
text: 'blah'
|
||||
});
|
||||
|
||||
@@ -103,6 +97,7 @@ describe('ClipboardAction', () => {
|
||||
it('should remove a temporary fake element', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
text: 'blah'
|
||||
});
|
||||
|
||||
@@ -116,6 +111,7 @@ describe('ClipboardAction', () => {
|
||||
it('should select text from editable element', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#input')
|
||||
});
|
||||
|
||||
@@ -125,6 +121,7 @@ describe('ClipboardAction', () => {
|
||||
it('should select text from non-editable element', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#paragraph')
|
||||
});
|
||||
|
||||
@@ -151,7 +148,7 @@ describe('ClipboardAction', () => {
|
||||
});
|
||||
|
||||
let clip = new ClipboardAction({
|
||||
emitter: emitter,
|
||||
emitter,
|
||||
target: document.querySelector('#input')
|
||||
});
|
||||
});
|
||||
@@ -166,7 +163,7 @@ describe('ClipboardAction', () => {
|
||||
});
|
||||
|
||||
let clip = new ClipboardAction({
|
||||
emitter: emitter,
|
||||
emitter,
|
||||
target: document.querySelector('#input')
|
||||
});
|
||||
});
|
||||
@@ -176,6 +173,7 @@ describe('ClipboardAction', () => {
|
||||
it('should fire a success event with certain properties', done => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#input')
|
||||
});
|
||||
|
||||
@@ -194,6 +192,7 @@ describe('ClipboardAction', () => {
|
||||
it('should fire a error event with certain properties', done => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#input')
|
||||
});
|
||||
|
||||
@@ -213,6 +212,7 @@ describe('ClipboardAction', () => {
|
||||
it('should remove focus from target and text selection', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#input')
|
||||
});
|
||||
|
||||
@@ -230,6 +230,7 @@ describe('ClipboardAction', () => {
|
||||
it('should destroy an existing fake element', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
text: 'blah'
|
||||
});
|
||||
|
||||
|
||||
@@ -9,8 +9,14 @@ describe('Clipboard', () => {
|
||||
global.button.setAttribute('data-clipboard-text', 'foo');
|
||||
document.body.appendChild(global.button);
|
||||
|
||||
global.span = document.createElement('span');
|
||||
global.span.innerHTML = 'bar';
|
||||
|
||||
global.button.appendChild(span);
|
||||
|
||||
global.event = {
|
||||
target: global.button
|
||||
target: global.button,
|
||||
currentTarget: global.button
|
||||
};
|
||||
});
|
||||
|
||||
@@ -20,7 +26,7 @@ describe('Clipboard', () => {
|
||||
|
||||
describe('#resolveOptions', () => {
|
||||
before(() => {
|
||||
global.fn = function() {};
|
||||
global.fn = () => {};
|
||||
});
|
||||
|
||||
it('should set action as a function', () => {
|
||||
@@ -46,6 +52,20 @@ describe('Clipboard', () => {
|
||||
|
||||
assert.equal(global.fn, clipboard.text);
|
||||
});
|
||||
|
||||
it('should set container as an object', () => {
|
||||
let clipboard = new Clipboard('.btn', {
|
||||
container: document.body
|
||||
});
|
||||
|
||||
assert.equal(document.body, clipboard.container);
|
||||
});
|
||||
|
||||
it('should set container as body by default', () => {
|
||||
let clipboard = new Clipboard('.btn');
|
||||
|
||||
assert.equal(document.body, clipboard.container);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#listenClick', () => {
|
||||
@@ -63,10 +83,18 @@ describe('Clipboard', () => {
|
||||
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 {
|
||||
var clipboard = new Clipboard('.btn', {
|
||||
target: function() {
|
||||
const clipboard = new Clipboard('.btn', {
|
||||
target() {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
@@ -80,6 +108,17 @@ describe('Clipboard', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('#static isSupported', () => {
|
||||
it('should return the support of the given action', () => {
|
||||
assert.equal(Clipboard.isSupported('copy'), false);
|
||||
assert.equal(Clipboard.isSupported('cut'), false);
|
||||
});
|
||||
|
||||
it('should return the support of the cut and copy actions', () => {
|
||||
assert.equal(Clipboard.isSupported(), false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#destroy', () => {
|
||||
it('should destroy an existing instance of ClipboardAction', () => {
|
||||
let clipboard = new Clipboard('.btn');
|
||||
|
||||
@@ -1,15 +0,0 @@
|
||||
var assert = require('chai').assert;
|
||||
var browserify = require('browserify');
|
||||
|
||||
describe('CommonJS', function() {
|
||||
it('should import the lib in a commonjs env without babel', function(done) {
|
||||
browserify('./dist/clipboard.js').bundle(function(err) {
|
||||
assert.equal(err, null);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('AMD', function() {
|
||||
// TODO: Write test case
|
||||
});
|
||||
Reference in New Issue
Block a user