Compare commits

...

129 Commits

Author SHA1 Message Date
Zeno Rocha
fdb66d3f16 2.0.2 2018-11-11 09:14:19 -08:00
Zeno Rocha
e0f82241d0 Removes bundle analyzer 2018-11-11 09:10:37 -08:00
Zeno Rocha
4d4c25c505 Updates Webpack CLI 2018-11-11 08:20:47 -08:00
Samuel Oloruntoba
5ef3f1a817 Migrates to Webpack 4 2018-11-11 08:17:25 -08:00
Zeno Rocha
3382ea3d14 2.0.1 2018-04-29 16:41:13 -07:00
Paweł Lesiecki
0fcf8c9460 Update package.json (#532)
Correct module path
2018-04-29 16:14:20 -07:00
Zeno Rocha
e1d571b3f3 Updates docs to use ClipboardJS 2.0 2018-03-01 09:07:10 -08:00
Zeno Rocha
43beb07bac Release v2.0.0 2018-02-28 21:56:32 -08:00
Zeno Rocha
9086f3ed64 Updates demos to use new constructor 2018-02-28 21:46:48 -08:00
Zeno Rocha
750cf124d7 Changes global variable from "Clipboard" to "ClipboardJS"
This was introduced in order to prevent a name conflict with the new `window.Clipboard` native function

For more info check:
* https://w3c.github.io/clipboard-apis/
* ac319a7ed3
2018-02-28 21:45:59 -08:00
Zeno Rocha
d25dcac817 Migrates banner from Browserify to Webpack #372 2018-02-28 20:32:25 -08:00
Fernando Herrero
f7e2f58c96 Create composer.json for instalation with composer (#467)
* Create composer.json for instalation with composer

The installation is much simpler with Composer and https://github.com/RobLoach/component-installer

After merged you can test it:
```
composer config repositories.zenorocha-clipboardjs vcs "https://github.com/zenorocha/clipboard.js/"
composer require zenorocha-clipboardjs:dev-master
```
Before merged you can test using my git fork.

Composer creates a "components" folder that contains the "clipboardjs" whith all files spec in "files" of composer.json.
You can then register the "package" at https://packagist.org In this way the installation would be simpler:
`composer require zenorocha-clipboardjs:dev-master`

* Remove icon
2018-02-28 17:05:21 -08:00
Guillaume Vincent
2d5b2df811 Migrate from Browserify to Webpack (#372)
* Migrate from Browserify to Webpack

close https://github.com/zenorocha/clipboard.js/issues/371

 Author:    Guillaume Vincent <guillaume@oslab.fr>

* remove browserify and associated modules and update tests
2018-02-28 17:03:39 -08:00
Lucas Bento
0c3bce265f Fix GitHub Primer tooltips link (#479) 2017-10-16 07:25:57 -07:00
Zeno Rocha
3c0dfe5a38 Adds docs on tooltips #423 2017-06-30 16:30:30 -03:00
Nathan
f960b0d20a use only ID name for DOM (#431) 2017-06-30 15:52:07 -03:00
Zeno Rocha
b6e6b80ab0 Release v1.7.1 2017-05-29 16:57:52 -07:00
Patrick H. Lauke
a55c9ac513 Move focus to trigger instead of simply blur()ing (#419)
blur() results in a loss/reset of keyboard focus, meaning keyboard users usually get thrown right back to the start of the page. Instead, this moves focus back to the trigger (which had the focus when the trigger was activated).
As with the proposed fix in https://github.com/zenorocha/clipboard.js/pull/418 this obviously results in the focus styles (like the default outline, unless suppressed) being applied to the trigger (see the related PR for rationale and future fix using `:focus-ring`)
2017-05-29 16:54:55 -07:00
Zeno Rocha
39e622456c Release v1.7.0 2017-05-29 14:43:41 -07:00
Zeno Rocha
f8c322f163 Reviews text from #368 2017-05-29 14:20:36 -07:00
Peder Johnsen
f42b57067d Container option (#368)
* Allow container option to fix bugs related to bootstrap modals etc.

* Updated readme to reflect addition of container option

* Name link

* Removed test log

* Remove unwanted whitespace

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

* little fix /2

* test/clipboard.js refactored

* emitter: emitter --> emitter

* Examples in ES6

* es6

* back to original code

* script > npm test

* script > npm test not necessary

* updating modules

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

* better code

* remove dist folder

* Revert "remove dist folder"

This reverts commit 50e726c7a7.

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

* Update dist
2016-06-09 07:54:07 -07:00
Zeno Rocha
a4ab305297 Release v1.5.11 2016-06-08 21:46:20 -07:00
Zeno Rocha
294e9fcb5d Fix fake element position from "fixed" to "absolute" #194 #250 2016-06-08 21:44:54 -07:00
Ali Bozorgkhan
79c3361ca4 Press Ctrl+C to copy -> Press Command+C to copy (#252)
As Safari users are mostly using mac, coping a text needs Command+C instead of Ctrl+C
2016-06-02 15:28:16 -07:00
grabus
c3fefc1fc0 Fix memory leak because of fakeHandlers (#243)
addEventListener always returns undefined by spec
https://www.w3.org/TR/DOM-Level-2-Events/events.html#Events-EventTarget-addEventListener
2016-05-26 11:35:18 -07:00
Zeno Rocha
42bd266f0b Adds basic JSFiddle to help people reproduce their bugs 2016-05-01 16:14:51 -07:00
Zeno Rocha
53a733fcb5 Release v1.5.10 2016-04-03 22:33:02 -07:00
Zeno Rocha
7a5a910bcd Removes error message when target/text attributes are null, undefined, or false 2016-04-03 22:16:22 -07:00
Zeno Rocha
0163f7cb72 Merge pull request #216 from sebastianekstrom/master
Use const instead of let when applicable
2016-04-01 08:48:49 -07:00
Sebastian Ekström
941bdbb16e Use const instead of let when applicable 2016-04-01 13:17:12 +02:00
Zeno Rocha
00d5cc4e96 Updates tagline #210 2016-03-17 09:12:23 -07:00
Zeno Rocha
07a1f8456b Throws error when disabled or readonly attributes are used on target element
Fixes #41
2016-03-01 15:38:25 -08:00
Zeno Rocha
ff3cd2c722 Release v1.5.9 2016-02-26 10:26:32 -08:00
Zeno Rocha
d346f30e5d Merge pull request #190 from heldr/master
update modules and fix #176
2016-02-25 12:19:22 -08:00
Helder Santana
e5fe34c524 WTF?! npm 2016-02-25 15:17:07 -05:00
Zeno Rocha
0a6aace544 Merge pull request #194 from stepancar/patch-1
minor fix for fake elem
2016-02-25 12:06:38 -08:00
Stepan Mikhaylyuk
43d9c11aaf minor fix for fake elem
after call clipboard all page scrolls down. As i understand, its relates to fake element, cause its append to body.
I just set position to 'fixed' and now it works right. I think 'fixed' is more css-style independence then 'absolute'
2016-02-25 15:20:13 +03:00
Helder Santana
90a52149ed fix babel 6 export default behaviour 2016-02-20 01:41:40 -05:00
Helder Santana
83b9d6a84d update modules and fix #176 2016-02-20 01:14:33 -05:00
Zeno Rocha
3d13baa385 Adds issue template 2016-02-18 11:40:00 -08:00
Zeno Rocha
7e37c95121 Release v1.5.8 2016-02-04 11:28:33 -08:00
Zeno Rocha
bc9bcdd678 Adds explanations on textarea styling #143 #179 #180 2016-02-04 11:25:00 -08:00
Zeno Rocha
4c9e29a0dc Resets invisible textarea's paddings and margins #179 2016-02-04 11:09:10 -08:00
speedplane
bd6dc9eb9f Prevent auto-zooming on an iphone by making the text area size appropriate size. Fixes Issue 180. 2016-02-04 11:12:04 -05:00
Zeno Rocha
a88bb77be4 Release v1.5.7 2016-02-03 08:31:41 -08:00
Zeno Rocha
e86dc2caa2 Revert "Upgrades to Babel 6"
This reverts commit ee1eb455f4.
2016-02-03 08:22:35 -08:00
Zeno Rocha
8392a7ba1c Release v1.5.6 2016-02-02 13:44:26 -08:00
RAFIE Younes
31e3622e17 Fixed RTL issue #161 2016-02-02 13:27:11 -08:00
Zeno Rocha
ee1eb455f4 Upgrades to Babel 6 2016-02-02 13:11:25 -08:00
Zeno Rocha
d50f2fb73f Updates good-listener
Fixes #170
2016-02-02 12:52:17 -08:00
Zeno Rocha
c8221c742d Serves the unminified version for Meteor
Fixes #167
2016-01-21 08:21:44 -08:00
Zeno Rocha
efc22cf651 Merge pull request #150 from nettofarah/patch-1
Add a bit more clarity to Invalid Target test
2015-12-17 10:48:14 -08:00
Netto Farah
4b27a72dce Add a bit more clarity to Invalid Target test 2015-12-13 13:11:02 -08:00
Zeno Rocha
c12c610b22 Merge pull request #134 from hmln/minor-readme-change
Fix typo in readme.
2015-11-19 07:21:32 -08:00
hmln
1d772a0cbe Fix typo in readme. 2015-11-19 00:38:20 -03:00
Zeno Rocha
d9254459b7 Merge pull request #131 from oskarcieslik/es2015_minor_fixes
Small ES2015 changes
2015-11-14 12:25:53 -06:00
Oskar Cieslik (rpawfuiml)
2aa163b1d0 Small ES2015 changes ;). 2015-11-13 22:01:59 +01:00
Zeno Rocha
783dc6f3cf Adds devDependencies since peerDependencies does not work anymore 2015-11-13 01:39:19 -08:00
Zeno Rocha
3188ffbce3 Release v1.5.5 2015-11-13 01:20:15 -08:00
Zeno Rocha
5dba68634e Fixes a bug where non-editable elements were being focused 2015-11-13 01:20:02 -08:00
Zeno Rocha
a12a056ef6 Release v1.5.4 2015-11-13 00:25:35 -08:00
Zeno Rocha
cb4301658c Updates delegate package which now exposes e.delegateTarget property #120 2015-11-13 00:24:46 -08:00
Zeno Rocha
5efcdf2810 Merge pull request #120 from pawlufelice/fix_event_target
use event's currentTarget instead of target
2015-11-13 00:20:20 -08:00
Zeno Rocha
72926580c3 Updates meteor version #123 2015-11-12 09:58:10 -08:00
Zeno Rocha
4967f118fe Release v1.5.3 2015-11-12 09:57:35 -08:00
Zeno Rocha
44d59b34a2 Merge pull request #123 from alippai/patch-1
Bump npm version to match the bower version.
2015-11-12 09:57:29 -08:00
Ádám Lippai
9d6375d20e Bump npm version to match the others. 2015-11-10 11:08:35 +01:00
Paul Felice
37136663df use event's currentTarget instead of target 2015-11-06 15:49:21 +01:00
Zeno Rocha
db575bb4df Removes babelify from being a dependency to a devDependency #15 #105 2015-10-28 13:06:14 -07:00
Zeno Rocha
bb6c4c0e7c Release v1.5.2 2015-10-28 12:57:47 -07:00
Zeno Rocha
03ee9829e0 Removes browserify tests 2015-10-28 12:56:52 -07:00
Zeno Rocha
24f6e1f541 Moves babel to devDependencies 2015-10-28 12:56:38 -07:00
Zeno Rocha
5bdfff6375 Ignores unnecessary files on bower and npm 2015-10-28 12:56:21 -07:00
Zeno Rocha
4b73122f81 Provides a transpiled version to npm so browserify and webpack users do not have to transpile by themselves
Fixes #15 #27 #105
2015-10-28 12:54:29 -07:00
Zeno Rocha
171f438f22 Release v1.5.1 2015-10-27 09:32:43 -07:00
Zeno Rocha
42a459402c Release v1.5.0 2015-10-26 23:04:54 -07:00
Zeno Rocha
b26cdb3b41 Allows HTML elements to be passed in the constructor - Fixes #25 2015-10-26 23:02:29 -07:00
Zeno Rocha
57c7fcf9a4 Adds a bunch of demos 2015-10-26 22:53:03 -07:00
Zeno Rocha
6b1f6b22a6 Allows HTML elements to be passed in the constructor - Fixes #25 2015-10-26 01:06:29 -07:00
Zeno Rocha
705e2dbefd Release v1.4.3 2015-10-24 11:41:53 -07:00
Zeno Rocha
fd66d6b51f Fix missing character in readme #101 2015-10-23 09:11:45 -07:00
Zeno Rocha
e0263beb20 Merge pull request #101 from curtisgibby/patch-1
Fix missing character in readme
2015-10-23 09:09:11 -07:00
Curtis Gibby
4d734bc277 Fix missing character in readme 2015-10-23 10:08:09 -06:00
Zeno Rocha
d5015f6313 New home! 2015-10-21 22:35:13 -07:00
Zeno Rocha
7e5beb439a 2015-10-21 10:11:50 -07:00
Zeno Rocha
70b2548a80 Migrates to delegate fork 2015-10-20 12:35:25 -07:00
Zeno Rocha
c6dc01cc29 Moves selection code to a different package 2015-10-19 16:21:38 -07:00
Zeno Rocha
f0245ab701 Merge pull request #95 from rafaelfragosom/arrowfunctions
Replacing anonymous functions with arrow functions to keep the pattern of the tests
2015-10-15 17:27:58 -07:00
Rafael Fragoso
c911ba0f53 Replacing anonymous functions with arrow functions to keep the pattern 2015-10-15 21:24:06 -03:00
Zeno Rocha
d166ff6d96 Adds banner - Fixes #88 2015-10-15 12:49:08 -07:00
Zeno Rocha
f7da00f0ba Update meteor package version #82 2015-10-14 18:38:52 -07:00
Lucas N. Munhoz
c7c2d9fb4f Add Package metadata for Meteor.js 2015-10-14 18:38:52 -07:00
Zeno Rocha
9fb666b365 Merge pull request #80 from heldr/fix_npm3_tree
fix npm3 build updating uglifyjs to version 2
2015-10-13 11:47:38 -07:00
Helder Santana
9ddd3a8017 travis ci tweaks 2015-10-13 14:42:35 -04:00
Helder Santana
8d804fdd42 fix npm3 build updating uglifyjs to version 2 2015-10-13 14:37:26 -04:00
Zeno Rocha
54efeb68e6 Release v1.4.2 2015-10-13 11:12:56 -07:00
Nik Butenko
f56825bf73 Add .npmignore 2015-10-13 09:45:51 -07:00
Zeno Rocha
9377659c9c Fixes discontiguous selection #17 2015-10-13 09:28:11 -07:00
Zeno Rocha
5e43e84d91 Merge pull request #86 from whitj00/patch-1
replace 'an user' with 'a user'
2015-10-10 08:59:47 -07:00
Whit Jackson
6ca2ba514c replace 'an user' with 'a user' 2015-10-09 17:17:37 -07:00
29 changed files with 8630 additions and 854 deletions

3
.babelrc Normal file
View File

@@ -0,0 +1,3 @@
{
"presets": ["env"]
}

View File

@@ -17,3 +17,6 @@ insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[{package.json,bower.json}]
indent_size = 2

15
.github/issue_template.md vendored Normal file
View 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.

3
.gitignore vendored
View File

@@ -1,3 +1,6 @@
lib
npm-debug.log
bower_components
node_modules
yarn-error.log
yarn.lock

7
.npmignore Normal file
View File

@@ -0,0 +1,7 @@
/.*/
/demo/
/test/
/.*
/bower.json
/karma.conf.js
/src

View File

@@ -1,3 +1,4 @@
sudo: false
language: node_js
node_js:
- 0.12
- stable

View File

@@ -1,9 +1,19 @@
{
"name": "clipboard",
"version": "1.4.1",
"description": "Modern copy to clipboard. No Flash. Just 2kb",
"version": "2.0.2",
"description": "Modern copy to clipboard. No Flash. Just 3kb",
"license": "MIT",
"main": "dist/clipboard.js",
"ignore": [
"/.*/",
"/demo/",
"/test/",
"/.*",
"/bower.json",
"/karma.conf.js",
"/src",
"/lib"
],
"keywords": [
"clipboard",
"copy",

25
composer.json Normal file
View File

@@ -0,0 +1,25 @@
{
"name": "zenorocha/clipboardjs",
"description": "Modern copy to clipboard. No Flash. Just 3kb gzipped https://clipboardjs.com",
"type": "component",
"homepage": "https://clipboardjs.com/",
"authors": [
{
"name": "Zeno Rocha",
"url": "http://zenorocha.com/"
}
],
"require": {
"robloach/component-installer": "*"
},
"extra": {
"component": {
"scripts": [
"dist/clipboard.js"
],
"files": [
"dist/clipboard.min.js"
]
}
}
}

View File

@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<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 -->
<div id="btn" data-clipboard-text="1">
<span>Copy</span>
</div>
<!-- 2. Include library -->
<script src="../dist/clipboard.min.js"></script>
<!-- 3. Instantiate clipboard by passing a HTML element -->
<script>
var btn = document.getElementById('btn');
var clipboard = new ClipboardJS(btn);
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
</script>
</body>
</html>

View File

@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<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 -->
<button data-clipboard-text="1">Copy</button>
<button data-clipboard-text="2">Copy</button>
<button data-clipboard-text="3">Copy</button>
<!-- 2. Include library -->
<script src="../dist/clipboard.min.js"></script>
<!-- 3. Instantiate clipboard by passing a list of HTML elements -->
<script>
var btns = document.querySelectorAll('button');
var clipboard = new ClipboardJS(btns);
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
</script>
</body>
</html>

View File

@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<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 -->
<button class="btn" data-clipboard-text="1">Copy</button>
<button class="btn" data-clipboard-text="2">Copy</button>
<button class="btn" data-clipboard-text="3">Copy</button>
<!-- 2. Include library -->
<script src="../dist/clipboard.min.js"></script>
<!-- 3. Instantiate clipboard by passing a string selector -->
<script>
var clipboard = new ClipboardJS('.btn');
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
</script>
</body>
</html>

33
demo/function-target.html Normal file
View File

@@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<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 -->
<button class="btn">Copy</button>
<div>hello</div>
<!-- 2. Include library -->
<script src="../dist/clipboard.min.js"></script>
<!-- 3. Instantiate clipboard -->
<script>
var clipboard = new ClipboardJS('.btn', {
target: function() {
return document.querySelector('div');
}
});
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
</script>
</body>
</html>

32
demo/function-text.html Normal file
View File

@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<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 -->
<button class="btn">Copy</button>
<!-- 2. Include library -->
<script src="../dist/clipboard.min.js"></script>
<!-- 3. Instantiate clipboard -->
<script>
var clipboard = new ClipboardJS('.btn', {
text: function() {
return 'to be or not to be';
}
});
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
</script>
</body>
</html>

29
demo/target-div.html Normal file
View File

@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<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 -->
<div>hello</div>
<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');
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
</script>
</body>
</html>

29
demo/target-input.html Normal file
View File

@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<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 -->
<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');
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
</script>
</body>
</html>

29
demo/target-textarea.html Normal file
View File

@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<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 -->
<textarea id="bar">hello</textarea>
<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');
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
</script>
</body>
</html>

804
dist/clipboard.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,8 +1,10 @@
module.exports = function(karma) {
karma.set({
plugins: ['karma-browserify', 'karma-chai', 'karma-sinon', 'karma-mocha', 'karma-phantomjs-launcher'],
var webpackConfig = require('./webpack.config.js');
frameworks: ['browserify', 'chai', 'sinon', 'mocha'],
module.exports = function (karma) {
karma.set({
plugins: ['karma-webpack', 'karma-chai', 'karma-sinon', 'karma-mocha', 'karma-phantomjs-launcher'],
frameworks: ['chai', 'sinon', 'mocha'],
files: [
'src/**/*.js',
@@ -10,18 +12,20 @@ module.exports = function(karma) {
'./node_modules/phantomjs-polyfill/bind-polyfill.js'
],
exclude: ['test/module-systems.js'],
preprocessors: {
'src/**/*.js' : ['browserify'],
'test/**/*.js': ['browserify']
'src/**/*.js': ['webpack'],
'test/**/*.js': ['webpack']
},
browserify: {
debug: true,
transform: ['babelify']
webpack: {
module: webpackConfig.module,
plugins: webpackConfig.plugins
},
webpackMiddleware: {
stats: 'errors-only'
},
browsers: ['PhantomJS']
});
}
};

7818
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

12
package.js Normal file
View File

@@ -0,0 +1,12 @@
// Package metadata for Meteor.js.
Package.describe({
name: "zenorocha:clipboard",
summary: "Modern copy to clipboard. No Flash. Just 3kb.",
version: "2.0.2",
git: "https://github.com/zenorocha/clipboard.js"
});
Package.onUse(function(api) {
api.addFiles("dist/clipboard.js", "client");
});

View File

@@ -1,51 +1,51 @@
{
"name": "clipboard",
"version": "1.4.1",
"version": "2.0.2",
"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"
}
]
]
},
"module": "dist/clipboard.js",
"keywords": [
"clipboard",
"copy",
"cut"
],
"dependencies": {
"babelify": "^6.3.0",
"browserify": "^11.2.0",
"delegate-events": "^1.1.1",
"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.26.0",
"babel-core": "^6.26.0",
"babel-loader": "^7.1.4",
"babel-preset-env": "^1.6.1",
"chai": "^3.4.1",
"cross-env": "^3.1.4",
"karma": "^1.3.0",
"karma-chai": "^0.1.0",
"karma-mocha": "^0.2.0",
"karma-phantomjs-launcher": "^0.2.1",
"karma-mocha": "^1.2.0",
"karma-phantomjs-launcher": "^1.0.0",
"karma-sinon": "^1.0.4",
"mocha": "^2.3.3",
"phantomjs-polyfill": "0.0.1",
"uglify": "^0.1.5",
"watchify": "^3.4.0"
"karma-webpack": "^2.0.2",
"mocha": "^3.1.2",
"phantomjs-prebuilt": "^2.1.4",
"sinon": "^1.17.2",
"uglifyjs-webpack-plugin": "^1.2.4",
"webpack": "^4.5.0",
"webpack-cli": "^3.1.2"
},
"scripts": {
"build": "npm run build-debug && npm run build-min",
"build-debug": "browserify src/clipboard.js -s Clipboard -o dist/clipboard.js",
"build-min": "uglify -s dist/clipboard.js -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-debug": "webpack --mode=development",
"build-min": "cross-env NODE_ENV=production webpack --mode=production",
"build-watch": "webpack --watch",
"test": "karma start --single-run",
"prepublish": "npm run build"
},
"browserslist": [
"> 1%",
"last 2 versions"
]
}

View File

@@ -1,10 +1,11 @@
# clipboard.js
[![Build Status](http://img.shields.io/travis/zenorocha/clipboard.js/master.svg?style=flat)](https://travis-ci.org/zenorocha/clipboard.js)
![Killing Flash](https://img.shields.io/badge/killing-flash-brightgreen.svg?style=flat)
> Modern copy to clipboard. No Flash. Just 2kb
> Modern copy to clipboard. No Flash. Just 3kb gzipped.
<a href="https://zenorocha.github.io/clipboard.js/"><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
@@ -20,32 +21,20 @@ 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
First, include the script located on the `dist` folder.
First, include the script located on the `dist` folder or load it from [a third-party CDN provider](https://github.com/zenorocha/clipboard.js/wiki/CDN-Providers).
```html
<script src="dist/clipboard.min.js"></script>
```
Or load it from a CDN.
```html
<script src="https://cdn.rawgit.com/zenorocha/clipboard.js/master/dist/clipboard.min.js"></script>
```
Now, you need to instantiate it using a DOM selector. This selector corresponds to the trigger element(s), for example `<button class="btn">`.
Now, you need to instantiate it by [passing a DOM selector](https://github.com/zenorocha/clipboard.js/blob/master/demo/constructor-selector.html#L18), [HTML element](https://github.com/zenorocha/clipboard.js/blob/master/demo/constructor-node.html#L16-L17), or [list of HTML elements](https://github.com/zenorocha/clipboard.js/blob/master/demo/constructor-nodelist.html#L18-L19).
```js
new Clipboard('.btn');
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.
@@ -62,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="https://zenorocha.github.io/clipboard.js/#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 -->
@@ -80,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="https://zenorocha.github.io/clipboard.js/#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 -->
@@ -98,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="https://zenorocha.github.io/clipboard.js/#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 -->
@@ -114,7 +103,7 @@ There are cases where you'd like to show some user feedback or capture what has
That's why we fire custom events such as `success` and `error` for you to listen and implement your custom logic.
```js
var clipboard = new Clipboard('.btn');
var clipboard = new ClipboardJS('.btn');
clipboard.on('success', function(e) {
console.info('Action:', e.action);
@@ -130,7 +119,13 @@ clipboard.on('error', function(e) {
});
```
For a live demonstration, open this [site](https://zenorocha.github.io/clipboard.js/) 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
@@ -139,7 +134,7 @@ If you don't want to modify your HTML, there's a pretty handy imperative API for
For instance, if you want to dynamically set a `target`, you'll need to return a Node.
```js
new Clipboard('.btn', {
new ClipboardJS('.btn', {
target: function(trigger) {
return trigger.nextElementSibling;
}
@@ -149,33 +144,45 @@ new Clipboard('.btn', {
If you want to dynamically set a `text`, you'll return a String.
```js
new Clipboard('.btn', {
new ClipboardJS('.btn', {
text: function(trigger) {
return trigger.getAttribute('aria-label');
}
});
```
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
var clipboard = new Clipboard('.btn');
new ClipboardJS('.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 ClipboardJS('.btn');
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="https://zenorocha.github.io/clipboard.js/assets/images/chrome.png" width="48px" height="48px" alt="Chrome logo"> | <img src="https://zenorocha.github.io/clipboard.js/assets/images/firefox.png" width="48px" height="48px" alt="Firefox logo"> | <img src="https://zenorocha.github.io/clipboard.js/assets/images/ie.png" width="48px" height="48px" alt="Internet Explorer logo"> | <img src="https://zenorocha.github.io/clipboard.js/assets/images/opera.png" width="48px" height="48px" alt="Opera logo"> | <img src="https://zenorocha.github.io/clipboard.js/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 `ClipboardJS.isSupported()`, that way you can hide copy/cut buttons from the UI.
For a live demonstration, open this [site](https://zenorocha.github.io/clipboard.js/) on Safari.
## Bonus
A browser extension that adds a "copy to clipboard" button to every code block on *GitHub, MDN, Gist, StackOverflow, StackExchange, npm, and even Medium.*
Install for [Chrome](https://chrome.google.com/webstore/detail/codecopy/fkbfebkcoelajmhanocgppanfoojcdmg) and [Firefox](https://addons.mozilla.org/en-US/firefox/addon/codecopy/).
## License

View File

@@ -1,3 +1,5 @@
import select from 'select';
/**
* Inner class which performs selection from either `text` or `target`
* properties and then executes copy or cut operations.
@@ -16,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 = '';
}
@@ -30,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"');
}
}
/**
@@ -49,36 +46,49 @@ 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;
this.selectedText = this.text;
document.body.appendChild(this.fakeElem);
this.container.appendChild(this.fakeElem);
this.fakeElem.select();
this.selectedText = select(this.fakeElem);
this.copyText();
}
/**
* Only removes the fake element after another click event, that way
* an user can hit `Ctrl+C` to copy because selection still exists.
* a user can hit `Ctrl+C` to copy because selection still exists.
*/
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;
}
}
@@ -87,19 +97,7 @@ class ClipboardAction {
* Selects the content from element passed on `target` property.
*/
selectTarget() {
if (this.target.nodeName === 'INPUT' || this.target.nodeName === 'TEXTAREA') {
this.target.select();
this.selectedText = this.target.value;
}
else {
let range = document.createRange();
let selection = window.getSelection();
range.selectNodeContents(this.target);
selection.addRange(range);
this.selectedText = selection.toString();
}
this.selectedText = select(this.target);
this.copyText();
}
@@ -124,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();
@@ -180,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 {
@@ -204,4 +201,4 @@ class ClipboardAction {
}
}
export default ClipboardAction;
module.exports = ClipboardAction;

View File

@@ -1,21 +1,21 @@
import ClipboardAction from './clipboard-action';
import Delegate from 'delegate-events';
import Emitter from 'tiny-emitter';
import listen from 'good-listener';
/**
* Base class which takes a selector, delegates a click event to it,
* 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} selector
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
* @param {Object} options
*/
constructor(selector, options) {
constructor(trigger, options) {
super();
this.resolveOptions(options);
this.delegateClick(selector);
this.listenClick(trigger);
}
/**
@@ -24,25 +24,18 @@ class Clipboard extends Emitter {
* @param {Object} options
*/
resolveOptions(options = {}) {
this.action = (typeof options.action === 'function') ? options.action : this.defaultAction;
this.target = (typeof options.target === 'function') ? options.target : this.defaultTarget;
this.text = (typeof options.text === 'function') ? options.text : this.defaultText;
this.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;
}
/**
* Delegates a click event on the passed selector.
* @param {String} selector
* Adds a click event listener to the passed trigger.
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
*/
delegateClick(selector) {
this.binding = Delegate.bind(document.body, selector, 'click', (e) => this.onClick(e));
}
/**
* Undelegates a click event on body.
* @param {String} selector
*/
undelegateClick() {
Delegate.unbind(document.body, 'click', this.binding);
listenClick(trigger) {
this.listener = listen(trigger, 'click', (e) => this.onClick(e));
}
/**
@@ -50,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.delegateTarget),
target : this.target(e.delegateTarget),
text : this.text(e.delegateTarget),
trigger : e.delegateTarget,
emitter : this
action : this.action(trigger),
target : this.target(trigger),
text : this.text(trigger),
container : this.container,
trigger : trigger,
emitter : this
});
}
@@ -76,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
@@ -95,7 +107,7 @@ class Clipboard extends Emitter {
* Destroy lifecycle.
*/
destroy() {
this.undelegateClick();
this.listener.destroy();
if (this.clipboardAction) {
this.clipboardAction.destroy();
@@ -111,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;
@@ -120,4 +132,4 @@ function getAttributeValue(suffix, element) {
return element.getAttribute(attribute);
}
export default Clipboard;
module.exports = Clipboard;

View File

@@ -22,10 +22,12 @@ describe('ClipboardAction', () => {
it('should set base properties', () => {
let clip = new ClipboardAction({
emitter: new Emitter(),
container: document.body,
text: 'foo'
});
assert.property(clip, 'action');
assert.property(clip, 'container');
assert.property(clip, 'emitter');
assert.property(clip, 'target');
assert.property(clip, 'text');
@@ -35,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'
});

View File

@@ -1,6 +1,6 @@
import Clipboard from '../src/clipboard';
import ClipboardAction from '../src/clipboard-action';
import Delegate from 'delegate-events';
import listen from 'good-listener';
describe('Clipboard', () => {
before(() => {
@@ -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 = {
delegateTarget: global.button
target: global.button,
currentTarget: global.button
};
});
@@ -18,9 +24,9 @@ describe('Clipboard', () => {
document.body.innerHTML = '';
});
describe('#resolveOptions', function() {
describe('#resolveOptions', () => {
before(() => {
global.fn = function() {};
global.fn = () => {};
});
it('should set action as a function', () => {
@@ -46,47 +52,26 @@ describe('Clipboard', () => {
assert.equal(global.fn, clipboard.text);
});
});
describe('#delegateClick', function() {
before(() => {
global.spy = sinon.spy(Delegate, 'bind');
it('should set container as an object', () => {
let clipboard = new Clipboard('.btn', {
container: document.body
});
assert.equal(document.body, clipboard.container);
});
after(() => {
global.spy.restore();
});
it('should delegate a click event to the passed selector', () => {
let element = document.body;
let selector = '.btn';
let event = 'click';
let clipboard = new Clipboard(selector);
assert.ok(global.spy.calledOnce);
assert.ok(global.spy.calledWith(element, selector, event));
});
});
describe('#undelegateClick', function() {
before(() => {
global.spy = sinon.spy(Delegate, 'unbind');
});
after(() => {
global.spy.restore();
});
it('should undelegate a click event', () => {
let element = document.body;
let event = 'click';
it('should set container as body by default', () => {
let clipboard = new Clipboard('.btn');
clipboard.undelegateClick();
assert.ok(global.spy.calledOnce);
assert.ok(global.spy.calledWith(element, event));
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);
});
});
@@ -98,13 +83,22 @@ 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;
}
});
clipboard.onClick(global.event);
}
catch(e) {
@@ -114,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');

View File

@@ -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
});

42
webpack.config.js Normal file
View File

@@ -0,0 +1,42 @@
const pkg = require('./package.json');
const path = require('path');
const webpack = require('webpack');
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
Licensed MIT © Zeno Rocha`;
module.exports = {
entry: './src/clipboard.js',
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: {
minimizer: [
new UglifyJSPlugin({
parallel: require('os').cpus().length,
uglifyOptions: {
ie8: false,
keep_fnames: false,
output: {
beautify: false,
comments: false
}
}
})
]
},
plugins: [new webpack.BannerPlugin({ banner })]
};