mirror of
https://github.com/zenorocha/clipboard.js.git
synced 2023-08-10 21:12:48 +03:00
Compare commits
88 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7dea403fbb | ||
|
|
47f64816eb | ||
|
|
801cdd4ee3 | ||
|
|
6f10cf7e12 | ||
|
|
2d5e3d2317 | ||
|
|
c38b4ee76f | ||
|
|
15737fe877 | ||
|
|
c23b30d052 | ||
|
|
4ab89f3d98 | ||
|
|
7b7ce32b65 | ||
|
|
5d3da80640 | ||
|
|
b66010bf77 | ||
|
|
17be1af63e | ||
|
|
eff98406b9 | ||
|
|
dc2b5bf0ea | ||
|
|
16966aac8d | ||
|
|
20b70bdbca | ||
|
|
fd836b82d4 | ||
|
|
0daa6ccd05 | ||
|
|
ddbbc238b6 | ||
|
|
cb1fec4c6a | ||
|
|
b229b550f6 | ||
|
|
b21b99fe5f | ||
|
|
da6b7dd7a3 | ||
|
|
99c1b9488b | ||
|
|
5d7ce2f7f6 | ||
|
|
14ee2e3137 | ||
|
|
9f0e246f64 | ||
|
|
982d5ef906 | ||
|
|
4553dfee2a | ||
|
|
28e9d15d13 | ||
|
|
8718bdd2a2 | ||
|
|
dd74f53217 | ||
|
|
119edb067d | ||
|
|
9e34b45841 | ||
|
|
9e3330084d | ||
|
|
94a9d8d3ea | ||
|
|
6f2b95fb81 | ||
|
|
2b48909ff1 | ||
|
|
5b8db05ad3 | ||
|
|
17fd75cfb6 | ||
|
|
40bf3736c4 | ||
|
|
82e0dca2ea | ||
|
|
e6826488d7 | ||
|
|
ac2deb40e9 | ||
|
|
41c73cb7eb | ||
|
|
b460d6864c | ||
|
|
9870b14e80 | ||
|
|
12a4f43337 | ||
|
|
62eef3e5b0 | ||
|
|
ddb5adc6f8 | ||
|
|
221efae529 | ||
|
|
971834388c | ||
|
|
d8a51544bd | ||
|
|
2239c05b4d | ||
|
|
6efd3ba519 | ||
|
|
d9b90c2f5d | ||
|
|
e5d3f91f35 | ||
|
|
2660565b61 | ||
|
|
35688322f3 | ||
|
|
498dfb9bdf | ||
|
|
b937bd631f | ||
|
|
6479739564 | ||
|
|
43aa5786c9 | ||
|
|
5ea83e4473 | ||
|
|
27ee5bbbd0 | ||
|
|
dd84bc1f8a | ||
|
|
4456d61877 | ||
|
|
26a8d63924 | ||
|
|
fddd2aac5f | ||
|
|
e430d056ad | ||
|
|
289389322e | ||
|
|
894a3bef4a | ||
|
|
780d390856 | ||
|
|
5de8be447f | ||
|
|
132fcd16b1 | ||
|
|
e7f0ff0392 | ||
|
|
393dbe34e0 | ||
|
|
d3fc3c1e7b | ||
|
|
83824fa248 | ||
|
|
ce79f170aa | ||
|
|
4c3a086866 | ||
|
|
20f64d82d0 | ||
|
|
85981026d1 | ||
|
|
6802a86f60 | ||
|
|
3522504d34 | ||
|
|
d17eca050e | ||
|
|
5381600a26 |
10
.babelrc
10
.babelrc
@@ -1,3 +1,11 @@
|
||||
{
|
||||
"presets": ["env"]
|
||||
"presets": [
|
||||
[
|
||||
"@babel/env",
|
||||
{
|
||||
"forceAllTransforms": true,
|
||||
"modules": false
|
||||
}
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
12
.eslintignore
Normal file
12
.eslintignore
Normal file
@@ -0,0 +1,12 @@
|
||||
# Ignore artifacts:
|
||||
dist
|
||||
|
||||
lib
|
||||
npm-debug.log
|
||||
bower_components
|
||||
node_modules
|
||||
yarn-error.log
|
||||
yarn.lock
|
||||
|
||||
src/*.ts
|
||||
/*.js
|
||||
24
.eslintrc.json
Normal file
24
.eslintrc.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2021": true,
|
||||
"mocha": true
|
||||
},
|
||||
"extends": ["airbnb-base", "plugin:prettier/recommended"],
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 12,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": "error",
|
||||
"prefer-const": "off",
|
||||
"camelcase": "off",
|
||||
"no-underscore-dangle": "off",
|
||||
"consistent-return": "off",
|
||||
/* Remove the necessity to use this on classes */
|
||||
"class-methods-use-this": "off",
|
||||
/* Enable variables declarations from shadowing variables declared in the outer scope */
|
||||
"no-shadow": "off"
|
||||
}
|
||||
}
|
||||
57
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
57
.github/ISSUE_TEMPLATE/bug_report.md
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
---
|
||||
name: 🐛 Bug Report
|
||||
about: Submit a bug report to help us improve
|
||||
labels: 'bug, needs triage'
|
||||
---
|
||||
|
||||
<!--
|
||||
|
||||
! PLEASE HELP US HELP YOU !
|
||||
|
||||
Bugs are fixed faster if you include:
|
||||
- a repro repository to inspect the code
|
||||
- an url to see the problem live
|
||||
|
||||
-->
|
||||
|
||||
## 🐛 Bug Report
|
||||
|
||||
> Fork this [JSFiddle](https://jsfiddle.net/zenorocha/5kk0eysw/) and reproduce your issue.
|
||||
|
||||
(A clear and concise description of what the issue is.)
|
||||
|
||||
### Have you read the [Contributing Guidelines on issues](https://github.com/zenorocha/clipboard.js/blob/master/contributing.md)?
|
||||
|
||||
(Write your answer here.)
|
||||
|
||||
### Expected Behaviour
|
||||
|
||||
<!--
|
||||
How did you expect your project to behave?
|
||||
It’s fine if you’re not sure your understanding is correct.
|
||||
Write down what you thought would happen.
|
||||
-->
|
||||
|
||||
I thought that by going to the page '...' and pressing the button '...' then '...' would happen.
|
||||
|
||||
_Tip: Try to use screenshots, gifs, videos, always remember people better understand with a visual way._
|
||||
|
||||
### Actual Behaviour
|
||||
|
||||
Instead of '...', what I saw was that '...' happened instead.
|
||||
|
||||
### To Reproduce
|
||||
|
||||
(Write your steps such as:)
|
||||
|
||||
1. Step 1...
|
||||
1. Step 2...
|
||||
1. Step 3...
|
||||
|
||||
### Browsers Affected
|
||||
|
||||
I tested on all major browsers and only IE 11 does not work.
|
||||
|
||||
### Operational System
|
||||
|
||||
(Place here your Operational System.)
|
||||
13
.github/ISSUE_TEMPLATE/documentation.md
vendored
Normal file
13
.github/ISSUE_TEMPLATE/documentation.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
name: 📚 Documentation
|
||||
about: Report an issue related to documentation
|
||||
labels: 'documentation, needs triage'
|
||||
---
|
||||
|
||||
## 📚 Documentation
|
||||
|
||||
(A clear and concise description of what the issue is.)
|
||||
|
||||
### Have you read the [Contributing Guidelines on issues](https://github.com/zenorocha/clipboard.js/blob/master/contributing.md)?
|
||||
|
||||
(Write your answer here.)
|
||||
26
.github/ISSUE_TEMPLATE/proposal.md
vendored
Normal file
26
.github/ISSUE_TEMPLATE/proposal.md
vendored
Normal file
@@ -0,0 +1,26 @@
|
||||
---
|
||||
name: 💥 Proposal
|
||||
about: Propose a non-trivial change to Clipboard.js
|
||||
labels: 'proposal, needs triage'
|
||||
---
|
||||
|
||||
## 💥 Proposal
|
||||
|
||||
**Is your feature request related to a problem? Please describe**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Are you able to assist to bring the feature to reality?**
|
||||
no | yes, I can...
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
||||
|
||||
### Have you read the [Contributing Guidelines on issues](https://github.com/zenorocha/clipboard.js/blob/master/contributing.md)?
|
||||
|
||||
(Write your answer here.)
|
||||
35
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
35
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
<!--
|
||||
Please make sure to read the Pull Request Guidelines:
|
||||
https://github.com/zenorocha/clipboard.js/blob/master/contributing.md#proposing-pull-requests
|
||||
-->
|
||||
|
||||
<!-- PULL REQUEST TEMPLATE -->
|
||||
<!-- (Update "[ ]" to "[x]" to check a box) -->
|
||||
|
||||
**What kind of change does this PR introduce?** (check at least one)
|
||||
|
||||
- [ ] Bugfix
|
||||
- [ ] Feature
|
||||
- [ ] Code style update
|
||||
- [ ] Refactor
|
||||
- [ ] Build-related changes
|
||||
- [ ] Other, please describe:
|
||||
|
||||
**Does this PR introduce a breaking change?** (check one)
|
||||
|
||||
- [ ] Yes
|
||||
- [ ] No
|
||||
|
||||
If yes, please describe the impact and migration path for existing applications:
|
||||
|
||||
**The PR fulfills these requirements:**
|
||||
|
||||
- [ ] It's submitted to the `dev` branch for v2.x (or to a previous version branch), _not_ the `master` branch
|
||||
- [ ] When resolving a specific issue, it's referenced in the PR's title (e.g. `fix #xxx[,#xxx]`, where "xxx" is the issue number)
|
||||
- [ ] New/updated tests are included
|
||||
|
||||
If adding a **new feature**, the PR's description includes:
|
||||
|
||||
- [ ] A convincing reason for adding this feature (to avoid wasting your time, it's best to open a suggestion issue first and wait for approval before working on it)
|
||||
|
||||
**Other information:**
|
||||
15
.github/issue_template.md
vendored
15
.github/issue_template.md
vendored
@@ -1,15 +0,0 @@
|
||||
### 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.
|
||||
21
.github/stale.yml
vendored
Normal file
21
.github/stale.yml
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 60
|
||||
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
|
||||
# Issues with these labels will never be considered stale
|
||||
exemptLabels:
|
||||
- pinned
|
||||
|
||||
# Label to use when marking an issue as stale
|
||||
staleLabel: stale
|
||||
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
47
.github/workflows/publish.yml
vendored
Normal file
47
.github/workflows/publish.yml
vendored
Normal file
@@ -0,0 +1,47 @@
|
||||
# This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages
|
||||
|
||||
name: publish
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [created]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14
|
||||
- run: npm ci
|
||||
- run: npm test
|
||||
|
||||
publish-npm:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14
|
||||
registry-url: https://registry.npmjs.org/
|
||||
- run: npm ci
|
||||
- run: npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{secrets.npm_token}}
|
||||
|
||||
publish-gpr:
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: 14
|
||||
registry-url: https://npm.pkg.github.com/
|
||||
- run: npm ci
|
||||
- run: npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
32
.github/workflows/test.js.yml
vendored
Normal file
32
.github/workflows/test.js.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x, 12.x, 14.x, 15.x]
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
# For now is not possible to target LTS verssions =/ check progress here https://github.com/actions/setup-node/issues/26
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm ci
|
||||
- run: npm run build --if-present
|
||||
- run: npm run lint
|
||||
- run: npm test
|
||||
|
||||
1
.husky/.gitignore
vendored
Normal file
1
.husky/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
_
|
||||
4
.husky/pre-commit
Executable file
4
.husky/pre-commit
Executable file
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
npx --no-install lint-staged
|
||||
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"
|
||||
}
|
||||
@@ -1,4 +0,0 @@
|
||||
sudo: false
|
||||
language: node_js
|
||||
node_js:
|
||||
- stable
|
||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) Zeno Rocha
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "clipboard",
|
||||
"version": "2.0.3",
|
||||
"version": "2.0.6",
|
||||
"description": "Modern copy to clipboard. No Flash. Just 3kb",
|
||||
"license": "MIT",
|
||||
"main": "dist/clipboard.js",
|
||||
@@ -14,9 +14,5 @@
|
||||
"/src",
|
||||
"/lib"
|
||||
],
|
||||
"keywords": [
|
||||
"clipboard",
|
||||
"copy",
|
||||
"cut"
|
||||
]
|
||||
"keywords": ["clipboard", "copy", "cut"]
|
||||
}
|
||||
|
||||
@@ -6,11 +6,11 @@
|
||||
"authors": [
|
||||
{
|
||||
"name": "Zeno Rocha",
|
||||
"url": "http://zenorocha.com/"
|
||||
"homepage": "http://zenorocha.com/"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"robloach/component-installer": "*"
|
||||
"oomphinc/composer-installers-extender": "*"
|
||||
},
|
||||
"extra": {
|
||||
"component": {
|
||||
|
||||
@@ -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,14 +1,14 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>constructor-node</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
<div id="btn" data-clipboard-text="1">
|
||||
<span>Copy</span>
|
||||
<span>Copy</span>
|
||||
</div>
|
||||
|
||||
<!-- 2. Include library -->
|
||||
@@ -16,16 +16,16 @@
|
||||
|
||||
<!-- 3. Instantiate clipboard by passing a HTML element -->
|
||||
<script>
|
||||
var btn = document.getElementById('btn');
|
||||
var clipboard = new ClipboardJS(btn);
|
||||
var btn = document.getElementById('btn');
|
||||
var clipboard = new ClipboardJS(btn);
|
||||
|
||||
clipboard.on('success', function(e) {
|
||||
clipboard.on('success', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
});
|
||||
|
||||
clipboard.on('error', function(e) {
|
||||
clipboard.on('error', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>constructor-nodelist</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
<button data-clipboard-text="1">Copy</button>
|
||||
<button data-clipboard-text="2">Copy</button>
|
||||
@@ -16,16 +16,16 @@
|
||||
|
||||
<!-- 3. Instantiate clipboard by passing a list of HTML elements -->
|
||||
<script>
|
||||
var btns = document.querySelectorAll('button');
|
||||
var clipboard = new ClipboardJS(btns);
|
||||
var btns = document.querySelectorAll('button');
|
||||
var clipboard = new ClipboardJS(btns);
|
||||
|
||||
clipboard.on('success', function(e) {
|
||||
clipboard.on('success', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
});
|
||||
|
||||
clipboard.on('error', function(e) {
|
||||
clipboard.on('error', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>constructor-selector</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
<button class="btn" data-clipboard-text="1">Copy</button>
|
||||
<button class="btn" data-clipboard-text="2">Copy</button>
|
||||
@@ -16,15 +16,15 @@
|
||||
|
||||
<!-- 3. Instantiate clipboard by passing a string selector -->
|
||||
<script>
|
||||
var clipboard = new ClipboardJS('.btn');
|
||||
var clipboard = new ClipboardJS('.btn');
|
||||
|
||||
clipboard.on('success', function(e) {
|
||||
clipboard.on('success', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
});
|
||||
|
||||
clipboard.on('error', function(e) {
|
||||
clipboard.on('error', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>function-target</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
<button class="btn">Copy</button>
|
||||
<div>hello</div>
|
||||
@@ -15,19 +15,19 @@
|
||||
|
||||
<!-- 3. Instantiate clipboard -->
|
||||
<script>
|
||||
var clipboard = new ClipboardJS('.btn', {
|
||||
target: function() {
|
||||
return document.querySelector('div');
|
||||
}
|
||||
});
|
||||
var clipboard = new ClipboardJS('.btn', {
|
||||
target: function () {
|
||||
return document.querySelector('div');
|
||||
},
|
||||
});
|
||||
|
||||
clipboard.on('success', function(e) {
|
||||
clipboard.on('success', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
});
|
||||
|
||||
clipboard.on('error', function(e) {
|
||||
clipboard.on('error', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>function-text</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
</head>
|
||||
<body>
|
||||
<!-- 1. Define some markup -->
|
||||
<button class="btn">Copy</button>
|
||||
|
||||
@@ -14,19 +14,19 @@
|
||||
|
||||
<!-- 3. Instantiate clipboard -->
|
||||
<script>
|
||||
var clipboard = new ClipboardJS('.btn', {
|
||||
text: function() {
|
||||
return 'to be or not to be';
|
||||
}
|
||||
});
|
||||
var clipboard = new ClipboardJS('.btn', {
|
||||
text: function () {
|
||||
return 'to be or not to be';
|
||||
},
|
||||
});
|
||||
|
||||
clipboard.on('success', function(e) {
|
||||
clipboard.on('success', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
});
|
||||
|
||||
clipboard.on('error', function(e) {
|
||||
clipboard.on('error', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,29 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>target-div</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<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>
|
||||
|
||||
<!-- 3. Instantiate clipboard -->
|
||||
<script>
|
||||
var clipboard = new ClipboardJS('.btn');
|
||||
var clipboard = new ClipboardJS('.btn');
|
||||
|
||||
clipboard.on('success', function(e) {
|
||||
clipboard.on('success', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
});
|
||||
|
||||
clipboard.on('error', function(e) {
|
||||
clipboard.on('error', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,29 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>target-input</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<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>
|
||||
|
||||
<!-- 3. Instantiate clipboard -->
|
||||
<script>
|
||||
var clipboard = new ClipboardJS('.btn');
|
||||
var clipboard = new ClipboardJS('.btn');
|
||||
|
||||
clipboard.on('success', function(e) {
|
||||
clipboard.on('success', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
});
|
||||
|
||||
clipboard.on('error', function(e) {
|
||||
clipboard.on('error', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -1,29 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<title>target-textarea</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
</head>
|
||||
<body>
|
||||
<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>
|
||||
|
||||
<!-- 3. Instantiate clipboard -->
|
||||
<script>
|
||||
var clipboard = new ClipboardJS('.btn');
|
||||
var clipboard = new ClipboardJS('.btn');
|
||||
|
||||
clipboard.on('success', function(e) {
|
||||
clipboard.on('success', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
});
|
||||
|
||||
clipboard.on('error', function(e) {
|
||||
clipboard.on('error', function (e) {
|
||||
console.log(e);
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
1500
dist/clipboard.js
vendored
1500
dist/clipboard.js
vendored
File diff suppressed because it is too large
Load Diff
8
dist/clipboard.min.js
vendored
8
dist/clipboard.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,31 +1,36 @@
|
||||
var webpackConfig = require('./webpack.config.js');
|
||||
|
||||
module.exports = function (karma) {
|
||||
karma.set({
|
||||
plugins: ['karma-webpack', 'karma-chai', 'karma-sinon', 'karma-mocha', 'karma-phantomjs-launcher'],
|
||||
karma.set({
|
||||
plugins: [
|
||||
'karma-webpack',
|
||||
'karma-chai',
|
||||
'karma-sinon',
|
||||
'karma-mocha',
|
||||
'karma-chrome-launcher',
|
||||
],
|
||||
|
||||
frameworks: ['chai', 'sinon', 'mocha'],
|
||||
frameworks: ['chai', 'sinon', 'mocha', 'webpack'],
|
||||
|
||||
files: [
|
||||
'src/**/*.js',
|
||||
'test/**/*.js',
|
||||
'./node_modules/phantomjs-polyfill/bind-polyfill.js'
|
||||
],
|
||||
files: [
|
||||
{ pattern: 'src/**/*.js', watched: false },
|
||||
{ pattern: 'test/**/*.js', watched: false },
|
||||
],
|
||||
|
||||
preprocessors: {
|
||||
'src/**/*.js': ['webpack'],
|
||||
'test/**/*.js': ['webpack']
|
||||
},
|
||||
preprocessors: {
|
||||
'src/**/*.js': ['webpack'],
|
||||
'test/**/*.js': ['webpack'],
|
||||
},
|
||||
|
||||
webpack: {
|
||||
module: webpackConfig.module,
|
||||
plugins: webpackConfig.plugins
|
||||
},
|
||||
webpack: {
|
||||
module: webpackConfig.module,
|
||||
plugins: webpackConfig.plugins,
|
||||
},
|
||||
|
||||
webpackMiddleware: {
|
||||
stats: 'errors-only'
|
||||
},
|
||||
webpackMiddleware: {
|
||||
stats: 'errors-only',
|
||||
},
|
||||
|
||||
browsers: ['PhantomJS']
|
||||
});
|
||||
browsers: ['ChromeHeadless'],
|
||||
});
|
||||
};
|
||||
|
||||
13219
package-lock.json
generated
13219
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.js
12
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.3",
|
||||
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");
|
||||
Package.onUse(function (api) {
|
||||
api.addFiles('dist/clipboard.js', 'client');
|
||||
});
|
||||
|
||||
55
package.json
55
package.json
@@ -1,10 +1,12 @@
|
||||
{
|
||||
"name": "clipboard",
|
||||
"version": "2.0.3",
|
||||
"version": "2.0.8",
|
||||
"description": "Modern copy to clipboard. No Flash. Just 2kb",
|
||||
"homepage": "https://clipboardjs.com",
|
||||
"repository": "zenorocha/clipboard.js",
|
||||
"license": "MIT",
|
||||
"main": "dist/clipboard.js",
|
||||
"types": "src/clipboard.d.ts",
|
||||
"keywords": [
|
||||
"clipboard",
|
||||
"copy",
|
||||
@@ -16,35 +18,46 @@
|
||||
"tiny-emitter": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"babel-cli": "^6.26.0",
|
||||
"babel-core": "^6.26.0",
|
||||
"babel-loader": "^7.1.4",
|
||||
"babel-preset-env": "^1.6.1",
|
||||
"@babel/core": "^7.12.10",
|
||||
"@babel/preset-env": "^7.12.11",
|
||||
"babel-loader": "^8.2.2",
|
||||
"chai": "^4.2.0",
|
||||
"cross-env": "^5.2.0",
|
||||
"karma": "^3.1.1",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^7.20.0",
|
||||
"eslint-config-airbnb-base": "^14.2.1",
|
||||
"eslint-config-prettier": "^7.2.0",
|
||||
"eslint-plugin-import": "^2.22.1",
|
||||
"eslint-plugin-prettier": "^3.3.1",
|
||||
"husky": "^5.0.9",
|
||||
"karma": "^6.0.0",
|
||||
"karma-chai": "^0.1.0",
|
||||
"karma-mocha": "^1.2.0",
|
||||
"karma-phantomjs-launcher": "^1.0.0",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-mocha": "^2.0.1",
|
||||
"karma-sinon": "^1.0.4",
|
||||
"karma-webpack": "^3.0.5",
|
||||
"mocha": "^5.2.0",
|
||||
"phantomjs-prebuilt": "^2.1.4",
|
||||
"sinon": "^7.1.1",
|
||||
"uglifyjs-webpack-plugin": "^2.0.1",
|
||||
"webpack": "^4.5.0",
|
||||
"webpack-cli": "^3.1.2"
|
||||
"karma-webpack": "^5.0.0-alpha.5",
|
||||
"lint-staged": "^10.5.3",
|
||||
"mocha": "^8.2.1",
|
||||
"prettier": "2.2.1",
|
||||
"sinon": "^9.2.3",
|
||||
"tsd": "^0.7.2",
|
||||
"uglifyjs-webpack-plugin": "^2.2.0",
|
||||
"webpack": "^5.15.0",
|
||||
"webpack-cli": "^4.4.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test:types": "tsd",
|
||||
"build": "npm run build-debug && npm run build-min",
|
||||
"build-debug": "webpack",
|
||||
"build-min": "cross-env NODE_ENV=production webpack",
|
||||
"build-watch": "webpack --watch",
|
||||
"test": "karma start --single-run",
|
||||
"prepublish": "npm run build"
|
||||
"prepublish": "npm run build",
|
||||
"lint": "eslint --ext .js src/"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%",
|
||||
"last 2 versions"
|
||||
]
|
||||
"lint-staged": {
|
||||
"*.{js,css,md}": [
|
||||
"prettier --write",
|
||||
"eslint --fix"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
59
readme.md
59
readme.md
@@ -1,6 +1,6 @@
|
||||
# clipboard.js
|
||||
|
||||
[](https://travis-ci.org/zenorocha/clipboard.js)
|
||||

|
||||

|
||||
|
||||
> Modern copy to clipboard. No Flash. Just 3kb gzipped.
|
||||
@@ -39,7 +39,7 @@ new ClipboardJS('.btn');
|
||||
|
||||
Internally, we need to fetch all elements that matches with your selector and attach event listeners for each one. But guess what? If you have hundreds of matches, this operation can consume a lot of memory.
|
||||
|
||||
For this reason we use [event delegation](http://stackoverflow.com/questions/1687296/what-is-dom-event-delegation) which replaces multiple event listeners with just a single listener. After all, [#perfmatters](https://twitter.com/hashtag/perfmatters).
|
||||
For this reason we use [event delegation](https://stackoverflow.com/questions/1687296/what-is-dom-event-delegation) which replaces multiple event listeners with just a single listener. After all, [#perfmatters](https://twitter.com/hashtag/perfmatters).
|
||||
|
||||
# Usage
|
||||
|
||||
@@ -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>
|
||||
```
|
||||
|
||||
@@ -77,7 +77,7 @@ If you omit this attribute, `copy` will be used by default.
|
||||
|
||||
<!-- Trigger -->
|
||||
<button class="btn" data-clipboard-action="cut" data-clipboard-target="#bar">
|
||||
Cut to clipboard
|
||||
Cut to clipboard
|
||||
</button>
|
||||
```
|
||||
|
||||
@@ -91,8 +91,11 @@ 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">
|
||||
Copy to clipboard
|
||||
<button
|
||||
class="btn"
|
||||
data-clipboard-text="Just because you can doesn't mean you should — clipboard.js"
|
||||
>
|
||||
Copy to clipboard
|
||||
</button>
|
||||
```
|
||||
|
||||
@@ -105,17 +108,17 @@ That's why we fire custom events such as `success` and `error` for you to listen
|
||||
```js
|
||||
var clipboard = new ClipboardJS('.btn');
|
||||
|
||||
clipboard.on('success', function(e) {
|
||||
console.info('Action:', e.action);
|
||||
console.info('Text:', e.text);
|
||||
console.info('Trigger:', e.trigger);
|
||||
clipboard.on('success', function (e) {
|
||||
console.info('Action:', e.action);
|
||||
console.info('Text:', e.text);
|
||||
console.info('Trigger:', e.trigger);
|
||||
|
||||
e.clearSelection();
|
||||
e.clearSelection();
|
||||
});
|
||||
|
||||
clipboard.on('error', function(e) {
|
||||
console.error('Action:', e.action);
|
||||
console.error('Trigger:', e.trigger);
|
||||
clipboard.on('error', function (e) {
|
||||
console.error('Action:', e.action);
|
||||
console.error('Trigger:', e.trigger);
|
||||
});
|
||||
```
|
||||
|
||||
@@ -125,7 +128,7 @@ For a live demonstration, go to this [site](https://clipboardjs.com/) and open y
|
||||
|
||||
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.
|
||||
The tooltips you see on the [demo site](https://clipboardjs.com/) were built using [GitHub's Primer](https://primer.style/css/components/tooltips). You may want to check that out if you're looking for a similar look and feel.
|
||||
|
||||
## Advanced Options
|
||||
|
||||
@@ -135,9 +138,9 @@ For instance, if you want to dynamically set a `target`, you'll need to return a
|
||||
|
||||
```js
|
||||
new ClipboardJS('.btn', {
|
||||
target: function(trigger) {
|
||||
return trigger.nextElementSibling;
|
||||
}
|
||||
target: function (trigger) {
|
||||
return trigger.nextElementSibling;
|
||||
},
|
||||
});
|
||||
```
|
||||
|
||||
@@ -145,9 +148,9 @@ If you want to dynamically set a `text`, you'll return a String.
|
||||
|
||||
```js
|
||||
new ClipboardJS('.btn', {
|
||||
text: function(trigger) {
|
||||
return trigger.getAttribute('aria-label');
|
||||
}
|
||||
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'),
|
||||
});
|
||||
```
|
||||
|
||||
@@ -168,11 +171,11 @@ 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 first one is [supported by all browsers](http://caniuse.com/#search=selection) while 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](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+ ✔ |
|
||||
| :-------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------: |
|
||||
| 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,10 +183,10 @@ 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/).
|
||||
|
||||
## License
|
||||
|
||||
[MIT License](http://zenorocha.mit-license.org/) © Zeno Rocha
|
||||
[MIT License](https://zenorocha.mit-license.org/) © Zeno Rocha
|
||||
|
||||
@@ -5,200 +5,217 @@ import select from 'select';
|
||||
* properties and then executes copy or cut operations.
|
||||
*/
|
||||
class ClipboardAction {
|
||||
/**
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(options) {
|
||||
this.resolveOptions(options);
|
||||
this.initSelection();
|
||||
/**
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(options) {
|
||||
this.resolveOptions(options);
|
||||
this.initSelection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines base properties passed from constructor.
|
||||
* @param {Object} options
|
||||
*/
|
||||
resolveOptions(options = {}) {
|
||||
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 = '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides which selection strategy is going to be applied based
|
||||
* on the existence of `text` and `target` properties.
|
||||
*/
|
||||
initSelection() {
|
||||
if (this.text) {
|
||||
this.selectFake();
|
||||
} else if (this.target) {
|
||||
this.selectTarget();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a fake textarea element, sets its value from `text` property,
|
||||
*/
|
||||
createFakeElement() {
|
||||
const isRTL = document.documentElement.getAttribute('dir') === 'rtl';
|
||||
|
||||
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
|
||||
let yPosition = window.pageYOffset || document.documentElement.scrollTop;
|
||||
this.fakeElem.style.top = `${yPosition}px`;
|
||||
|
||||
this.fakeElem.setAttribute('readonly', '');
|
||||
this.fakeElem.value = this.text;
|
||||
|
||||
return this.fakeElem;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get's the value of fakeElem,
|
||||
* and makes a selection on it.
|
||||
*/
|
||||
selectFake() {
|
||||
const fakeElem = this.createFakeElement();
|
||||
|
||||
this.fakeHandlerCallback = () => this.removeFake();
|
||||
|
||||
this.fakeHandler =
|
||||
this.container.addEventListener('click', this.fakeHandlerCallback) ||
|
||||
true;
|
||||
|
||||
this.container.appendChild(fakeElem);
|
||||
|
||||
this.selectedText = select(fakeElem);
|
||||
|
||||
this.copyText();
|
||||
|
||||
this.removeFake();
|
||||
}
|
||||
|
||||
/**
|
||||
* Only removes the fake element after another click event, that way
|
||||
* a user can hit `Ctrl+C` to copy because selection still exists.
|
||||
*/
|
||||
removeFake() {
|
||||
if (this.fakeHandler) {
|
||||
this.container.removeEventListener('click', this.fakeHandlerCallback);
|
||||
this.fakeHandler = null;
|
||||
this.fakeHandlerCallback = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines base properties passed from constructor.
|
||||
* @param {Object} options
|
||||
*/
|
||||
resolveOptions(options = {}) {
|
||||
this.action = options.action;
|
||||
this.container = options.container;
|
||||
this.emitter = options.emitter;
|
||||
this.target = options.target;
|
||||
this.text = options.text;
|
||||
this.trigger = options.trigger;
|
||||
if (this.fakeElem) {
|
||||
this.container.removeChild(this.fakeElem);
|
||||
this.fakeElem = null;
|
||||
}
|
||||
}
|
||||
|
||||
this.selectedText = '';
|
||||
/**
|
||||
* Selects the content from element passed on `target` property.
|
||||
*/
|
||||
selectTarget() {
|
||||
this.selectedText = select(this.target);
|
||||
this.copyText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the copy operation based on the current selection.
|
||||
*/
|
||||
copyText() {
|
||||
let succeeded;
|
||||
|
||||
try {
|
||||
succeeded = document.execCommand(this.action);
|
||||
} catch (err) {
|
||||
succeeded = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides which selection strategy is going to be applied based
|
||||
* on the existence of `text` and `target` properties.
|
||||
*/
|
||||
initSelection() {
|
||||
if (this.text) {
|
||||
this.selectFake();
|
||||
}
|
||||
else if (this.target) {
|
||||
this.selectTarget();
|
||||
}
|
||||
this.handleResult(succeeded);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires an event based on the copy operation result.
|
||||
* @param {Boolean} succeeded
|
||||
*/
|
||||
handleResult(succeeded) {
|
||||
this.emitter.emit(succeeded ? 'success' : 'error', {
|
||||
action: this.action,
|
||||
text: this.selectedText,
|
||||
trigger: this.trigger,
|
||||
clearSelection: this.clearSelection.bind(this),
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves focus away from `target` and back to the trigger, removes current selection.
|
||||
*/
|
||||
clearSelection() {
|
||||
if (this.trigger) {
|
||||
this.trigger.focus();
|
||||
}
|
||||
document.activeElement.blur();
|
||||
window.getSelection().removeAllRanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a fake textarea element, sets its value from `text` property,
|
||||
* and makes a selection on it.
|
||||
*/
|
||||
selectFake() {
|
||||
const isRTL = document.documentElement.getAttribute('dir') == 'rtl';
|
||||
/**
|
||||
* Sets the `action` to be performed which can be either 'copy' or 'cut'.
|
||||
* @param {String} action
|
||||
*/
|
||||
set action(action = 'copy') {
|
||||
this._action = action;
|
||||
|
||||
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[ 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;
|
||||
|
||||
this.container.appendChild(this.fakeElem);
|
||||
|
||||
this.selectedText = select(this.fakeElem);
|
||||
this.copyText();
|
||||
if (this._action !== 'copy' && this._action !== 'cut') {
|
||||
throw new Error('Invalid "action" value, use either "copy" or "cut"');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only removes the fake element after another click event, that way
|
||||
* a user can hit `Ctrl+C` to copy because selection still exists.
|
||||
*/
|
||||
removeFake() {
|
||||
if (this.fakeHandler) {
|
||||
this.container.removeEventListener('click', this.fakeHandlerCallback);
|
||||
this.fakeHandler = null;
|
||||
this.fakeHandlerCallback = null;
|
||||
/**
|
||||
* Gets the `action` property.
|
||||
* @return {String}
|
||||
*/
|
||||
get action() {
|
||||
return this._action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the `target` property using an element
|
||||
* that will be have its content copied.
|
||||
* @param {Element} target
|
||||
*/
|
||||
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.fakeElem) {
|
||||
this.container.removeChild(this.fakeElem);
|
||||
this.fakeElem = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects the content from element passed on `target` property.
|
||||
*/
|
||||
selectTarget() {
|
||||
this.selectedText = select(this.target);
|
||||
this.copyText();
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the copy operation based on the current selection.
|
||||
*/
|
||||
copyText() {
|
||||
let succeeded;
|
||||
|
||||
try {
|
||||
succeeded = document.execCommand(this.action);
|
||||
}
|
||||
catch (err) {
|
||||
succeeded = false;
|
||||
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.handleResult(succeeded);
|
||||
this._target = target;
|
||||
} else {
|
||||
throw new Error('Invalid "target" value, use a valid Element');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fires an event based on the copy operation result.
|
||||
* @param {Boolean} succeeded
|
||||
*/
|
||||
handleResult(succeeded) {
|
||||
this.emitter.emit(succeeded ? 'success' : 'error', {
|
||||
action: this.action,
|
||||
text: this.selectedText,
|
||||
trigger: this.trigger,
|
||||
clearSelection: this.clearSelection.bind(this)
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Gets the `target` property.
|
||||
* @return {String|HTMLElement}
|
||||
*/
|
||||
get target() {
|
||||
return this._target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves focus away from `target` and back to the trigger, removes current selection.
|
||||
*/
|
||||
clearSelection() {
|
||||
if (this.trigger) {
|
||||
this.trigger.focus();
|
||||
}
|
||||
|
||||
window.getSelection().removeAllRanges();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the `action` to be performed which can be either 'copy' or 'cut'.
|
||||
* @param {String} action
|
||||
*/
|
||||
set action(action = 'copy') {
|
||||
this._action = action;
|
||||
|
||||
if (this._action !== 'copy' && this._action !== 'cut') {
|
||||
throw new Error('Invalid "action" value, use either "copy" or "cut"');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `action` property.
|
||||
* @return {String}
|
||||
*/
|
||||
get action() {
|
||||
return this._action;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the `target` property using an element
|
||||
* that will be have its content copied.
|
||||
* @param {Element} target
|
||||
*/
|
||||
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 {
|
||||
throw new Error('Invalid "target" value, use a valid Element');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the `target` property.
|
||||
* @return {String|HTMLElement}
|
||||
*/
|
||||
get target() {
|
||||
return this._target;
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy lifecycle.
|
||||
*/
|
||||
destroy() {
|
||||
this.removeFake();
|
||||
}
|
||||
/**
|
||||
* Destroy lifecycle.
|
||||
*/
|
||||
destroy() {
|
||||
this.removeFake();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ClipboardAction;
|
||||
export default ClipboardAction;
|
||||
|
||||
84
src/clipboard.d.ts
vendored
Normal file
84
src/clipboard.d.ts
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
/// <reference lib="dom"/>
|
||||
|
||||
type Action = 'cut' | 'copy';
|
||||
type Response = 'success' | 'error';
|
||||
|
||||
type Options = {
|
||||
text?: string;
|
||||
action?: Action;
|
||||
target?: Element;
|
||||
container?: Element;
|
||||
};
|
||||
|
||||
/**
|
||||
* Base class which takes one or more elements, adds event listeners to them,
|
||||
* and instantiates a new `ClipboardAction` on each click.
|
||||
*/
|
||||
declare class ClipboardJS {
|
||||
constructor(
|
||||
selector: string | Element | NodeListOf<Element>,
|
||||
options?: ClipboardJS.Options
|
||||
);
|
||||
|
||||
/**
|
||||
* Subscribes to events that indicate the result of a copy/cut operation.
|
||||
* @param type Event type ('success' or 'error').
|
||||
* @param handler Callback function.
|
||||
*/
|
||||
on(type: Response, handler: (e: ClipboardJS.Event) => void): this;
|
||||
|
||||
on(type: string, handler: (...args: any[]) => void): this;
|
||||
|
||||
/**
|
||||
* Clears all event bindings.
|
||||
*/
|
||||
destroy(): void;
|
||||
|
||||
/**
|
||||
* Checks if clipboard.js is supported
|
||||
*/
|
||||
static isSupported(): boolean;
|
||||
}
|
||||
|
||||
declare namespace ClipboardJS {
|
||||
interface Options {
|
||||
/**
|
||||
* Overwrites default command ('cut' or 'copy').
|
||||
* @param elem Current element
|
||||
*/
|
||||
action?(elem: Element): Action;
|
||||
|
||||
/**
|
||||
* Overwrites default target input element.
|
||||
* @param elem Current element
|
||||
* @returns <input> element to use.
|
||||
*/
|
||||
target?(elem: Element): Element;
|
||||
|
||||
/**
|
||||
* Returns the explicit text to copy.
|
||||
* @param elem Current element
|
||||
* @returns Text to be copied.
|
||||
*/
|
||||
text?(elem: Element): string;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
container?: Element;
|
||||
}
|
||||
|
||||
interface Event {
|
||||
action: string;
|
||||
text: string;
|
||||
trigger: Element;
|
||||
clearSelection(): void;
|
||||
}
|
||||
}
|
||||
|
||||
export = ClipboardJS;
|
||||
|
||||
export as namespace ClipboardJS;
|
||||
251
src/clipboard.js
251
src/clipboard.js
@@ -1,121 +1,6 @@
|
||||
import ClipboardAction from './clipboard-action';
|
||||
import Emitter from 'tiny-emitter';
|
||||
import listen from 'good-listener';
|
||||
|
||||
/**
|
||||
* Base class which takes one or more elements, adds event listeners to them,
|
||||
* and instantiates a new `ClipboardAction` on each click.
|
||||
*/
|
||||
class Clipboard extends Emitter {
|
||||
/**
|
||||
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(trigger, options) {
|
||||
super();
|
||||
|
||||
this.resolveOptions(options);
|
||||
this.listenClick(trigger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines if attributes would be resolved using internal setter functions
|
||||
* or custom functions that were passed in the constructor.
|
||||
* @param {Object} options
|
||||
*/
|
||||
resolveOptions(options = {}) {
|
||||
this.action = (typeof options.action === 'function') ? options.action : this.defaultAction;
|
||||
this.target = (typeof options.target === 'function') ? options.target : this.defaultTarget;
|
||||
this.text = (typeof options.text === 'function') ? options.text : this.defaultText;
|
||||
this.container = (typeof options.container === 'object') ? options.container : document.body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a click event listener to the passed trigger.
|
||||
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
|
||||
*/
|
||||
listenClick(trigger) {
|
||||
this.listener = listen(trigger, 'click', (e) => this.onClick(e));
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a new `ClipboardAction` on each click event.
|
||||
* @param {Event} e
|
||||
*/
|
||||
onClick(e) {
|
||||
const trigger = e.delegateTarget || e.currentTarget;
|
||||
|
||||
if (this.clipboardAction) {
|
||||
this.clipboardAction = null;
|
||||
}
|
||||
|
||||
this.clipboardAction = new ClipboardAction({
|
||||
action : this.action(trigger),
|
||||
target : this.target(trigger),
|
||||
text : this.text(trigger),
|
||||
container : this.container,
|
||||
trigger : trigger,
|
||||
emitter : this
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Default `action` lookup function.
|
||||
* @param {Element} trigger
|
||||
*/
|
||||
defaultAction(trigger) {
|
||||
return getAttributeValue('action', trigger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default `target` lookup function.
|
||||
* @param {Element} trigger
|
||||
*/
|
||||
defaultTarget(trigger) {
|
||||
const selector = getAttributeValue('target', trigger);
|
||||
|
||||
if (selector) {
|
||||
return document.querySelector(selector);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the support of the given action, or all actions if no action is
|
||||
* given.
|
||||
* @param {String} [action]
|
||||
*/
|
||||
static isSupported(action = ['copy', 'cut']) {
|
||||
const actions = (typeof action === 'string') ? [action] : action;
|
||||
let support = !!document.queryCommandSupported;
|
||||
|
||||
actions.forEach((action) => {
|
||||
support = support && !!document.queryCommandSupported(action);
|
||||
});
|
||||
|
||||
return support;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default `text` lookup function.
|
||||
* @param {Element} trigger
|
||||
*/
|
||||
defaultText(trigger) {
|
||||
return getAttributeValue('text', trigger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy lifecycle.
|
||||
*/
|
||||
destroy() {
|
||||
this.listener.destroy();
|
||||
|
||||
if (this.clipboardAction) {
|
||||
this.clipboardAction.destroy();
|
||||
this.clipboardAction = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
import ClipboardAction from './clipboard-action';
|
||||
|
||||
/**
|
||||
* Helper function to retrieve attribute value.
|
||||
@@ -123,13 +8,135 @@ class Clipboard extends Emitter {
|
||||
* @param {Element} element
|
||||
*/
|
||||
function getAttributeValue(suffix, element) {
|
||||
const attribute = `data-clipboard-${suffix}`;
|
||||
const attribute = `data-clipboard-${suffix}`;
|
||||
|
||||
if (!element.hasAttribute(attribute)) {
|
||||
return;
|
||||
}
|
||||
if (!element.hasAttribute(attribute)) {
|
||||
return;
|
||||
}
|
||||
|
||||
return element.getAttribute(attribute);
|
||||
return element.getAttribute(attribute);
|
||||
}
|
||||
|
||||
module.exports = Clipboard;
|
||||
/**
|
||||
* Base class which takes one or more elements, adds event listeners to them,
|
||||
* and instantiates a new `ClipboardAction` on each click.
|
||||
*/
|
||||
class Clipboard extends Emitter {
|
||||
/**
|
||||
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
|
||||
* @param {Object} options
|
||||
*/
|
||||
constructor(trigger, options) {
|
||||
super();
|
||||
|
||||
this.resolveOptions(options);
|
||||
this.listenClick(trigger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines if attributes would be resolved using internal setter functions
|
||||
* or custom functions that were passed in the constructor.
|
||||
* @param {Object} options
|
||||
*/
|
||||
resolveOptions(options = {}) {
|
||||
this.action =
|
||||
typeof options.action === 'function'
|
||||
? options.action
|
||||
: this.defaultAction;
|
||||
this.target =
|
||||
typeof options.target === 'function'
|
||||
? options.target
|
||||
: this.defaultTarget;
|
||||
this.text =
|
||||
typeof options.text === 'function' ? options.text : this.defaultText;
|
||||
this.container =
|
||||
typeof options.container === 'object' ? options.container : document.body;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a click event listener to the passed trigger.
|
||||
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
|
||||
*/
|
||||
listenClick(trigger) {
|
||||
this.listener = listen(trigger, 'click', (e) => this.onClick(e));
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a new `ClipboardAction` on each click event.
|
||||
* @param {Event} e
|
||||
*/
|
||||
onClick(e) {
|
||||
const trigger = e.delegateTarget || e.currentTarget;
|
||||
|
||||
if (this.clipboardAction) {
|
||||
this.clipboardAction = null;
|
||||
}
|
||||
|
||||
this.clipboardAction = new ClipboardAction({
|
||||
action: this.action(trigger),
|
||||
target: this.target(trigger),
|
||||
text: this.text(trigger),
|
||||
container: this.container,
|
||||
trigger,
|
||||
emitter: this,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Default `action` lookup function.
|
||||
* @param {Element} trigger
|
||||
*/
|
||||
defaultAction(trigger) {
|
||||
return getAttributeValue('action', trigger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default `target` lookup function.
|
||||
* @param {Element} trigger
|
||||
*/
|
||||
defaultTarget(trigger) {
|
||||
const selector = getAttributeValue('target', trigger);
|
||||
|
||||
if (selector) {
|
||||
return document.querySelector(selector);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the support of the given action, or all actions if no action is
|
||||
* given.
|
||||
* @param {String} [action]
|
||||
*/
|
||||
static isSupported(action = ['copy', 'cut']) {
|
||||
const actions = typeof action === 'string' ? [action] : action;
|
||||
let support = !!document.queryCommandSupported;
|
||||
|
||||
actions.forEach((action) => {
|
||||
support = support && !!document.queryCommandSupported(action);
|
||||
});
|
||||
|
||||
return support;
|
||||
}
|
||||
|
||||
/**
|
||||
* Default `text` lookup function.
|
||||
* @param {Element} trigger
|
||||
*/
|
||||
defaultText(trigger) {
|
||||
return getAttributeValue('text', trigger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy lifecycle.
|
||||
*/
|
||||
destroy() {
|
||||
this.listener.destroy();
|
||||
|
||||
if (this.clipboardAction) {
|
||||
this.clipboardAction.destroy();
|
||||
this.clipboardAction = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Clipboard;
|
||||
|
||||
4
src/clipboard.test-d.ts
Normal file
4
src/clipboard.test-d.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
import { expectType } from 'tsd';
|
||||
import Clipboard from './clipboard';
|
||||
|
||||
expectType<Clipboard>(new Clipboard('.btn'));
|
||||
@@ -1,243 +1,248 @@
|
||||
import ClipboardAction from '../src/clipboard-action';
|
||||
import Emitter from 'tiny-emitter';
|
||||
import ClipboardAction from '../src/clipboard-action';
|
||||
|
||||
describe('ClipboardAction', () => {
|
||||
before(() => {
|
||||
global.input = document.createElement('input');
|
||||
global.input.setAttribute('id', 'input');
|
||||
global.input.setAttribute('value', 'abc');
|
||||
document.body.appendChild(global.input);
|
||||
before(() => {
|
||||
global.input = document.createElement('input');
|
||||
global.input.setAttribute('id', 'input');
|
||||
global.input.setAttribute('value', 'abc');
|
||||
document.body.appendChild(global.input);
|
||||
|
||||
global.paragraph = document.createElement('p');
|
||||
global.paragraph.setAttribute('id', 'paragraph');
|
||||
global.paragraph.textContent = 'abc';
|
||||
document.body.appendChild(global.paragraph);
|
||||
global.paragraph = document.createElement('p');
|
||||
global.paragraph.setAttribute('id', 'paragraph');
|
||||
global.paragraph.textContent = 'abc';
|
||||
document.body.appendChild(global.paragraph);
|
||||
});
|
||||
|
||||
after(() => {
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
describe('#resolveOptions', () => {
|
||||
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');
|
||||
assert.property(clip, 'trigger');
|
||||
assert.property(clip, 'selectedText');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#initSelection', () => {
|
||||
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',
|
||||
});
|
||||
|
||||
const el = clip.createFakeElement();
|
||||
|
||||
assert.equal(el.style.right, '-9999px');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#set action', () => {
|
||||
it('should throw an error since "action" is invalid', (done) => {
|
||||
try {
|
||||
let clip = new ClipboardAction({
|
||||
text: 'foo',
|
||||
action: 'paste',
|
||||
});
|
||||
} 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) => {
|
||||
try {
|
||||
let clip = new ClipboardAction({
|
||||
target: document.querySelector('#foo'),
|
||||
});
|
||||
} catch (e) {
|
||||
assert.equal(e.message, 'Invalid "target" value, use a valid Element');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('#selectText', () => {
|
||||
it('should create a fake element and select its value', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
text: 'blah',
|
||||
});
|
||||
|
||||
const el = clip.createFakeElement();
|
||||
|
||||
assert.equal(clip.selectedText, el.value);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#removeFake', () => {
|
||||
it('should remove a temporary fake element', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
text: 'blah',
|
||||
});
|
||||
|
||||
clip.removeFake();
|
||||
|
||||
assert.equal(clip.fakeElem, null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#selectTarget', () => {
|
||||
it('should select text from editable element', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#input'),
|
||||
});
|
||||
|
||||
assert.equal(clip.selectedText, clip.target.value);
|
||||
});
|
||||
|
||||
it('should select text from non-editable element', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#paragraph'),
|
||||
});
|
||||
|
||||
assert.equal(clip.selectedText, clip.target.textContent);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#copyText', () => {
|
||||
before(() => {
|
||||
global.stub = sinon.stub(document, 'execCommand');
|
||||
});
|
||||
|
||||
after(() => {
|
||||
document.body.innerHTML = '';
|
||||
global.stub.restore();
|
||||
});
|
||||
|
||||
describe('#resolveOptions', () => {
|
||||
it('should set base properties', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
text: 'foo'
|
||||
});
|
||||
it('should fire a success event on browsers that support copy command', (done) => {
|
||||
global.stub.returns(true);
|
||||
|
||||
assert.property(clip, 'action');
|
||||
assert.property(clip, 'container');
|
||||
assert.property(clip, 'emitter');
|
||||
assert.property(clip, 'target');
|
||||
assert.property(clip, 'text');
|
||||
assert.property(clip, 'trigger');
|
||||
assert.property(clip, 'selectedText');
|
||||
});
|
||||
let emitter = new Emitter();
|
||||
|
||||
emitter.on('success', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
let clip = new ClipboardAction({
|
||||
emitter,
|
||||
target: document.querySelector('#input'),
|
||||
});
|
||||
});
|
||||
|
||||
describe('#initSelection', () => {
|
||||
it('should set the position right style property', done => {
|
||||
// Set document direction
|
||||
document.documentElement.setAttribute('dir', 'rtl');
|
||||
it('should fire an error event on browsers that support copy command', (done) => {
|
||||
global.stub.returns(false);
|
||||
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
text: 'foo'
|
||||
});
|
||||
let emitter = new Emitter();
|
||||
|
||||
assert.equal(clip.fakeElem.style.right, '-9999px');
|
||||
done();
|
||||
});
|
||||
emitter.on('error', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
let clip = new ClipboardAction({
|
||||
emitter,
|
||||
target: document.querySelector('#input'),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#handleResult', () => {
|
||||
it('should fire a success event with certain properties', (done) => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#input'),
|
||||
});
|
||||
|
||||
clip.emitter.on('success', (e) => {
|
||||
assert.property(e, 'action');
|
||||
assert.property(e, 'text');
|
||||
assert.property(e, 'trigger');
|
||||
assert.property(e, 'clearSelection');
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
clip.handleResult(true);
|
||||
});
|
||||
|
||||
describe('#set action', () => {
|
||||
it('should throw an error since "action" is invalid', done => {
|
||||
try {
|
||||
new ClipboardAction({
|
||||
text: 'foo',
|
||||
action: 'paste'
|
||||
});
|
||||
}
|
||||
catch(e) {
|
||||
assert.equal(e.message, 'Invalid "action" value, use either "copy" or "cut"');
|
||||
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'),
|
||||
});
|
||||
|
||||
clip.emitter.on('error', (e) => {
|
||||
assert.property(e, 'action');
|
||||
assert.property(e, 'trigger');
|
||||
assert.property(e, 'clearSelection');
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
clip.handleResult(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#set target', () => {
|
||||
it('should throw an error since "target" do not match any element', done => {
|
||||
try {
|
||||
new ClipboardAction({
|
||||
target: document.querySelector('#foo')
|
||||
});
|
||||
}
|
||||
catch(e) {
|
||||
assert.equal(e.message, 'Invalid "target" value, use a valid Element');
|
||||
done();
|
||||
}
|
||||
});
|
||||
describe('#clearSelection', () => {
|
||||
it('should remove focus from target and text selection', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#input'),
|
||||
});
|
||||
|
||||
clip.clearSelection();
|
||||
|
||||
let selectedElem = document.activeElement;
|
||||
let selectedText = window.getSelection().toString();
|
||||
|
||||
assert.equal(selectedElem, document.body);
|
||||
assert.equal(selectedText, '');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#selectText', () => {
|
||||
it('should create a fake element and select its value', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
text: 'blah'
|
||||
});
|
||||
describe('#destroy', () => {
|
||||
it('should destroy an existing fake element', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
text: 'blah',
|
||||
});
|
||||
|
||||
assert.equal(clip.selectedText, clip.fakeElem.value);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#removeFake', () => {
|
||||
it('should remove a temporary fake element', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
text: 'blah'
|
||||
});
|
||||
|
||||
clip.removeFake();
|
||||
|
||||
assert.equal(clip.fakeElem, null);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#selectTarget', () => {
|
||||
it('should select text from editable element', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#input')
|
||||
});
|
||||
|
||||
assert.equal(clip.selectedText, clip.target.value);
|
||||
});
|
||||
|
||||
it('should select text from non-editable element', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#paragraph')
|
||||
});
|
||||
|
||||
assert.equal(clip.selectedText, clip.target.textContent);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#copyText', () => {
|
||||
before(() => {
|
||||
global.stub = sinon.stub(document, 'execCommand');
|
||||
});
|
||||
|
||||
after(() => {
|
||||
global.stub.restore();
|
||||
});
|
||||
|
||||
it('should fire a success event on browsers that support copy command', done => {
|
||||
global.stub.returns(true);
|
||||
|
||||
let emitter = new Emitter();
|
||||
|
||||
emitter.on('success', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
let clip = new ClipboardAction({
|
||||
emitter,
|
||||
target: document.querySelector('#input')
|
||||
});
|
||||
});
|
||||
|
||||
it('should fire an error event on browsers that support copy command', done => {
|
||||
global.stub.returns(false);
|
||||
|
||||
let emitter = new Emitter();
|
||||
|
||||
emitter.on('error', () => {
|
||||
done();
|
||||
});
|
||||
|
||||
let clip = new ClipboardAction({
|
||||
emitter,
|
||||
target: document.querySelector('#input')
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('#handleResult', () => {
|
||||
it('should fire a success event with certain properties', done => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#input')
|
||||
});
|
||||
|
||||
clip.emitter.on('success', (e) => {
|
||||
assert.property(e, 'action');
|
||||
assert.property(e, 'text');
|
||||
assert.property(e, 'trigger');
|
||||
assert.property(e, 'clearSelection');
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
clip.handleResult(true);
|
||||
});
|
||||
|
||||
it('should fire a error event with certain properties', done => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#input')
|
||||
});
|
||||
|
||||
clip.emitter.on('error', (e) => {
|
||||
assert.property(e, 'action');
|
||||
assert.property(e, 'trigger');
|
||||
assert.property(e, 'clearSelection');
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
clip.handleResult(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#clearSelection', () => {
|
||||
it('should remove focus from target and text selection', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
target: document.querySelector('#input')
|
||||
});
|
||||
|
||||
clip.clearSelection();
|
||||
|
||||
let selectedElem = document.activeElement;
|
||||
let selectedText = window.getSelection().toString();
|
||||
|
||||
assert.equal(selectedElem, document.body);
|
||||
assert.equal(selectedText, '');
|
||||
});
|
||||
});
|
||||
|
||||
describe('#destroy', () => {
|
||||
it('should destroy an existing fake element', () => {
|
||||
let clip = new ClipboardAction({
|
||||
emitter: new Emitter(),
|
||||
container: document.body,
|
||||
text: 'blah'
|
||||
});
|
||||
|
||||
clip.selectFake();
|
||||
clip.destroy();
|
||||
|
||||
assert.equal(clip.fakeElem, null);
|
||||
});
|
||||
clip.selectFake();
|
||||
clip.destroy();
|
||||
|
||||
assert.equal(clip.fakeElem, null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,132 +1,133 @@
|
||||
import Clipboard from '../src/clipboard';
|
||||
import ClipboardAction from '../src/clipboard-action';
|
||||
import listen from 'good-listener';
|
||||
|
||||
describe('Clipboard', () => {
|
||||
before(() => {
|
||||
global.button = document.createElement('button');
|
||||
global.button.setAttribute('class', 'btn');
|
||||
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,
|
||||
currentTarget: global.button,
|
||||
};
|
||||
});
|
||||
|
||||
after(() => {
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
describe('#resolveOptions', () => {
|
||||
before(() => {
|
||||
global.button = document.createElement('button');
|
||||
global.button.setAttribute('class', 'btn');
|
||||
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,
|
||||
currentTarget: global.button
|
||||
};
|
||||
global.fn = () => {};
|
||||
});
|
||||
|
||||
after(() => {
|
||||
document.body.innerHTML = '';
|
||||
it('should set action as a function', () => {
|
||||
let clipboard = new Clipboard('.btn', {
|
||||
action: global.fn,
|
||||
});
|
||||
|
||||
assert.equal(global.fn, clipboard.action);
|
||||
});
|
||||
|
||||
describe('#resolveOptions', () => {
|
||||
before(() => {
|
||||
global.fn = () => {};
|
||||
});
|
||||
it('should set target as a function', () => {
|
||||
let clipboard = new Clipboard('.btn', {
|
||||
target: global.fn,
|
||||
});
|
||||
|
||||
it('should set action as a function', () => {
|
||||
let clipboard = new Clipboard('.btn', {
|
||||
action: global.fn
|
||||
});
|
||||
|
||||
assert.equal(global.fn, clipboard.action);
|
||||
});
|
||||
|
||||
it('should set target as a function', () => {
|
||||
let clipboard = new Clipboard('.btn', {
|
||||
target: global.fn
|
||||
});
|
||||
|
||||
assert.equal(global.fn, clipboard.target);
|
||||
});
|
||||
|
||||
it('should set text as a function', () => {
|
||||
let clipboard = new Clipboard('.btn', {
|
||||
text: global.fn
|
||||
});
|
||||
|
||||
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);
|
||||
});
|
||||
assert.equal(global.fn, clipboard.target);
|
||||
});
|
||||
|
||||
describe('#listenClick', () => {
|
||||
it('should add a click event listener to the passed selector', () => {
|
||||
let clipboard = new Clipboard('.btn');
|
||||
assert.isObject(clipboard.listener);
|
||||
});
|
||||
it('should set text as a function', () => {
|
||||
let clipboard = new Clipboard('.btn', {
|
||||
text: global.fn,
|
||||
});
|
||||
|
||||
assert.equal(global.fn, clipboard.text);
|
||||
});
|
||||
|
||||
describe('#onClick', () => {
|
||||
it('should create a new instance of ClipboardAction', () => {
|
||||
let clipboard = new Clipboard('.btn');
|
||||
it('should set container as an object', () => {
|
||||
let clipboard = new Clipboard('.btn', {
|
||||
container: document.body,
|
||||
});
|
||||
|
||||
clipboard.onClick(global.event);
|
||||
assert.instanceOf(clipboard.clipboardAction, ClipboardAction);
|
||||
});
|
||||
|
||||
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 {
|
||||
const clipboard = new Clipboard('.btn', {
|
||||
target() {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
clipboard.onClick(global.event);
|
||||
}
|
||||
catch(e) {
|
||||
assert.equal(e.message, 'Invalid "target" value, use a valid Element');
|
||||
done();
|
||||
}
|
||||
});
|
||||
assert.equal(document.body, clipboard.container);
|
||||
});
|
||||
|
||||
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 set container as body by default', () => {
|
||||
let clipboard = new Clipboard('.btn');
|
||||
|
||||
it('should return the support of the cut and copy actions', () => {
|
||||
assert.equal(Clipboard.isSupported(), false);
|
||||
});
|
||||
assert.equal(document.body, clipboard.container);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#listenClick', () => {
|
||||
it('should add a click event listener to the passed selector', () => {
|
||||
let clipboard = new Clipboard('.btn');
|
||||
assert.isObject(clipboard.listener);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#onClick', () => {
|
||||
it('should create a new instance of ClipboardAction', () => {
|
||||
let clipboard = new Clipboard('.btn');
|
||||
|
||||
clipboard.onClick(global.event);
|
||||
assert.instanceOf(clipboard.clipboardAction, ClipboardAction);
|
||||
});
|
||||
|
||||
describe('#destroy', () => {
|
||||
it('should destroy an existing instance of ClipboardAction', () => {
|
||||
let clipboard = new Clipboard('.btn');
|
||||
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(global.event);
|
||||
clipboard.destroy();
|
||||
|
||||
assert.equal(clipboard.clipboardAction, null);
|
||||
});
|
||||
clipboard.onClick(bubbledEvent);
|
||||
assert.instanceOf(clipboard.clipboardAction, ClipboardAction);
|
||||
});
|
||||
|
||||
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) {
|
||||
assert.equal(e.message, 'Invalid "target" value, use a valid Element');
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('#static isSupported', () => {
|
||||
it('should return the support of the given action', () => {
|
||||
assert.equal(Clipboard.isSupported('copy'), true);
|
||||
assert.equal(Clipboard.isSupported('cut'), true);
|
||||
});
|
||||
|
||||
it('should return the support of the cut and copy actions', () => {
|
||||
assert.equal(Clipboard.isSupported(), true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#destroy', () => {
|
||||
it('should destroy an existing instance of ClipboardAction', () => {
|
||||
let clipboard = new Clipboard('.btn');
|
||||
|
||||
clipboard.onClick(global.event);
|
||||
clipboard.destroy();
|
||||
|
||||
assert.equal(clipboard.clipboardAction, null);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -6,39 +6,41 @@ const UglifyJSPlugin = require('uglifyjs-webpack-plugin');
|
||||
const production = process.env.NODE_ENV === 'production' || false;
|
||||
|
||||
const banner = `clipboard.js v${pkg.version}
|
||||
https://zenorocha.github.io/clipboard.js
|
||||
https://clipboardjs.com/
|
||||
|
||||
Licensed MIT © Zeno Rocha`;
|
||||
|
||||
module.exports = {
|
||||
entry: './src/clipboard.js',
|
||||
mode: 'production',
|
||||
output: {
|
||||
filename: production ? 'clipboard.min.js' : 'clipboard.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
library: 'ClipboardJS',
|
||||
libraryTarget: 'umd'
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader'}
|
||||
]
|
||||
},
|
||||
optimization: {
|
||||
minimize: production,
|
||||
minimizer: [
|
||||
new UglifyJSPlugin({
|
||||
parallel: require('os').cpus().length,
|
||||
uglifyOptions: {
|
||||
ie8: false,
|
||||
keep_fnames: false,
|
||||
output: {
|
||||
beautify: false,
|
||||
comments: (node, {value, type}) => type == 'comment2' && value.startsWith('!')
|
||||
}
|
||||
}
|
||||
})
|
||||
]
|
||||
},
|
||||
plugins: [new webpack.BannerPlugin({ banner })]
|
||||
entry: './src/clipboard.js',
|
||||
mode: 'production',
|
||||
target: ['web', 'es5'],
|
||||
output: {
|
||||
filename: production ? 'clipboard.min.js' : 'clipboard.js',
|
||||
path: path.resolve(__dirname, 'dist'),
|
||||
library: 'ClipboardJS',
|
||||
globalObject: 'this',
|
||||
libraryExport: 'default',
|
||||
libraryTarget: 'umd',
|
||||
},
|
||||
module: {
|
||||
rules: [{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }],
|
||||
},
|
||||
optimization: {
|
||||
minimize: production,
|
||||
minimizer: [
|
||||
new UglifyJSPlugin({
|
||||
parallel: require('os').cpus().length,
|
||||
uglifyOptions: {
|
||||
ie8: false,
|
||||
keep_fnames: false,
|
||||
output: {
|
||||
beautify: false,
|
||||
comments: (node, { value, type }) =>
|
||||
type == 'comment2' && value.startsWith('!'),
|
||||
},
|
||||
},
|
||||
}),
|
||||
],
|
||||
},
|
||||
plugins: [new webpack.BannerPlugin({ banner })],
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user