Compare commits

...

339 Commits

Author SHA1 Message Date
vitormalencar
7dea403fbb 2.0.8 2021-03-10 21:28:10 +01:00
Vitor Alencar
47f64816eb Merge pull request #741 from zenorocha/feat-fix-webpack-target
feat adding es5 as target for webpack compilation
2021-03-10 17:29:00 +01:00
Vitor Alencar
801cdd4ee3 Merge pull request #736 from zenorocha/feat-update-type-definitions
updating type definitions
2021-03-10 16:39:23 +01:00
vitormalencar
6f10cf7e12 feat adding es5 as target for webpack compilation 2021-03-09 11:38:00 +01:00
vitormalencar
2d5e3d2317 updating type definitons 2021-03-07 14:15:36 +01:00
vitormalencar
c38b4ee76f 2.0.7 2021-03-06 13:51:09 +01:00
Vitor Alencar
15737fe877 Merge pull request #733 from zenorocha/feature-732-removing-dom-el 2021-03-01 14:07:29 +01:00
vitormalencar
c23b30d052 feat : add blank line between class members 2021-02-25 16:29:36 +01:00
vitormalencar
4ab89f3d98 feat: merge with master 2021-02-25 16:25:24 +01:00
vitormalencar
7b7ce32b65 Merge branch 'master' of github.com:zenorocha/clipboard.js into feature-732-removing-dom-el
* 'master' of github.com:zenorocha/clipboard.js:
  refactor(workflows): remove unused lint file
  feat(workflows): add lint code job
  chore(eslint): add comments and new rules
  chore(deps): remove sort-package-json
  refactor: remove eslint ignore rules comments
  ci(lint): create a ci workflow
  chore(clipboard): remove linter bugs
  chore(deps): add dependencies and new scripts
  chore(test): remove linter bugs
  chore(linter): add linter configuration
  feat(eslint): add linter configuration
  chore(deps): add linter
  updating naming
  adding deploy action
2021-02-25 16:21:53 +01:00
vitormalencar
5d3da80640 feat: updating test coverage 2021-02-25 16:16:42 +01:00
vitormalencar
b66010bf77 feat: decoupling create fakeElemet logic 2021-02-25 16:16:12 +01:00
Vitor Alencar
17be1af63e Merge pull request #727 from zenorocha/feature-npm-deploy 2021-02-25 15:00:21 +01:00
vitormalencar
eff98406b9 updating tests 2021-02-24 22:47:58 +01:00
vitormalencar
dc2b5bf0ea removing element after selection 2021-02-24 22:47:49 +01:00
Vitor Alencar
16966aac8d Merge pull request #731 from r3nanp/master 2021-02-23 10:27:38 +01:00
r3nanp
20b70bdbca refactor(workflows): remove unused lint file 2021-02-20 12:58:24 -03:00
r3nanp
fd836b82d4 feat(workflows): add lint code job 2021-02-16 19:05:21 -03:00
r3nanp
0daa6ccd05 chore(eslint): add comments and new rules 2021-02-16 19:03:28 -03:00
r3nanp
ddbbc238b6 chore(deps): remove sort-package-json 2021-02-16 19:02:02 -03:00
r3nanp
cb1fec4c6a refactor: remove eslint ignore rules comments 2021-02-16 19:01:13 -03:00
r3nanp
b229b550f6 ci(lint): create a ci workflow 2021-02-13 11:04:34 -03:00
r3nanp
b21b99fe5f chore(clipboard): remove linter bugs 2021-02-13 10:56:07 -03:00
r3nanp
da6b7dd7a3 chore(deps): add dependencies and new scripts 2021-02-13 10:55:16 -03:00
r3nanp
99c1b9488b chore(test): remove linter bugs 2021-02-13 10:53:22 -03:00
r3nanp
5d7ce2f7f6 chore(linter): add linter configuration 2021-02-13 10:52:08 -03:00
r3nanp
14ee2e3137 feat(eslint): add linter configuration 2021-02-13 09:55:38 -03:00
r3nanp
9f0e246f64 chore(deps): add linter 2021-02-13 09:47:15 -03:00
Vitor Alencar
982d5ef906 Merge pull request #730 from zenorocha/helderburato-patch-1
Fix readme.md badge
2021-02-12 18:16:40 +01:00
Helder Burato Berto
4553dfee2a Fix readme.md badge 2021-02-12 00:39:27 +00:00
Helder Burato Berto
28e9d15d13 Merge pull request #729 from zenorocha/chore-upgrade-husky
Chore upgrade husky
2021-02-11 23:25:41 +00:00
Helder Burato Berto
8718bdd2a2 chore(husky): add pre-commit
It triggers lint-staged via package manager
2021-02-11 15:45:35 +00:00
Helder Burato Berto
dd74f53217 chore(packages): add husky v5.x.x 2021-02-11 15:44:57 +00:00
Vitor Alencar
119edb067d Merge pull request #726 from zenorocha/feature-travis-migration 2021-02-11 15:12:54 +01:00
vitormalencar
9e34b45841 updating naming 2021-02-08 18:40:39 +01:00
vitormalencar
9e3330084d adding deploy action 2021-02-07 12:50:33 +01:00
vitormalencar
94a9d8d3ea updating readme 2021-02-07 12:25:17 +01:00
vitormalencar
6f2b95fb81 removing travis config 2021-02-07 12:25:05 +01:00
Vitor Alencar
2b48909ff1 Update test.js.yml 2021-02-07 11:44:29 +01:00
Vitor Alencar
5b8db05ad3 Create test.js.yml
Adding GH Action
2021-02-07 11:31:44 +01:00
Helder Burato Berto
17fd75cfb6 Merge pull request #723 from zenorocha/docs-gh-templates
Add GH templates
2021-01-28 19:40:39 +00:00
Helder Burato Berto
40bf3736c4 Add Operational System to bug template 2021-01-28 14:36:50 +00:00
Helder Burato Berto
82e0dca2ea Add pull request template 2021-01-28 14:30:22 +00:00
Helder Burato Berto
e6826488d7 Add issues template
It adds templates to the following types:
- Bugs
- Proposals
- Documentation
2021-01-26 21:28:48 +00:00
Helder Burato Berto
ac2deb40e9 Remove old issue template 2021-01-26 21:28:23 +00:00
Vitor Alencar
41c73cb7eb Merge pull request #722 from zenorocha/feature-typescript 2021-01-26 12:33:43 +01:00
vitormalencar
b460d6864c update type for emitter 2021-01-24 16:53:30 +01:00
vitormalencar
9870b14e80 Add initial type definitions 2021-01-22 15:59:50 +01:00
vitormalencar
12a4f43337 Add types entry and tsd dependency 2021-01-22 15:59:23 +01:00
Beto Muniz
62eef3e5b0 Merge pull request #720 from zenorocha/small-enhancements
Remove unnecessary code and add node version control
2021-01-21 17:11:55 -03:00
Beto Muniz
ddb5adc6f8 Merge branch 'master' into small-enhancements
* master:
  update code style
  update prettier config
  update dependencies
  update code style
  Add prettier husky and  lint-staged
  Add prettier config file
  add prettier config files
2021-01-21 14:25:36 -03:00
Vitor Alencar
221efae529 Merge pull request #721 from zenorocha/feature-prettier 2021-01-21 17:32:52 +01:00
vitormalencar
971834388c update code style 2021-01-21 11:45:33 +01:00
vitormalencar
d8a51544bd update prettier config 2021-01-21 11:44:11 +01:00
vitormalencar
2239c05b4d update dependencies 2021-01-20 17:33:16 +01:00
vitormalencar
6efd3ba519 Merge branch 'master' of github.com:zenorocha/clipboard.js into feature-prettier
* 'master' of github.com:zenorocha/clipboard.js:
  Update dev dependencies.
2021-01-20 17:26:47 +01:00
Beto Muniz
d9b90c2f5d Add node version control to the project 2021-01-20 12:58:11 -03:00
Beto Muniz
e5d3f91f35 Remove unnecessary dependency 2021-01-20 12:57:58 -03:00
vitormalencar
2660565b61 update code style 2021-01-20 16:55:03 +01:00
vitormalencar
35688322f3 Add prettier husky and lint-staged 2021-01-20 16:32:40 +01:00
vitormalencar
498dfb9bdf Add prettier config file 2021-01-20 16:31:18 +01:00
vitormalencar
b937bd631f add prettier config files 2021-01-19 19:06:14 +01:00
Beto Muniz
6479739564 Merge pull request #719 from zenorocha/update-devdeps
Update dev dependencies.
2021-01-19 15:02:14 -03:00
Beto Muniz
43aa5786c9 Update dev dependencies. 2021-01-19 12:42:06 -03:00
Beto Muniz
5ea83e4473 Merge pull request #703 from realityking/babel7
Upgrade babel to version 7
2021-01-19 11:08:05 -03:00
dependabot[bot]
27ee5bbbd0 Bump ini from 1.3.5 to 1.3.8
Bumps [ini](https://github.com/isaacs/ini) from 1.3.5 to 1.3.8.
- [Release notes](https://github.com/isaacs/ini/releases)
- [Commits](https://github.com/isaacs/ini/compare/v1.3.5...v1.3.8)

Signed-off-by: dependabot[bot] <support@github.com>
2021-01-19 04:34:05 -08:00
Christian Oliff
dd84bc1f8a Add homepage to package.json 2021-01-19 04:31:03 -08:00
Rouven Weßling
4456d61877 Upgrade babel to version 7 2020-10-04 15:20:54 +02:00
dependabot[bot]
26a8d63924 Bump acorn from 6.4.0 to 6.4.1
Bumps [acorn](https://github.com/acornjs/acorn) from 6.4.0 to 6.4.1.
- [Release notes](https://github.com/acornjs/acorn/releases)
- [Commits](https://github.com/acornjs/acorn/compare/6.4.0...6.4.1)

Signed-off-by: dependabot[bot] <support@github.com>
2020-03-14 17:11:32 -07:00
Zeno Rocha
fddd2aac5f 2.0.6 2020-03-04 22:26:34 -08:00
Zeno Rocha
e430d056ad Fix "isSupported" behavior - Closes #666 2020-03-04 22:24:24 -08:00
Zeno Rocha
289389322e 2.0.5 2020-03-03 18:20:40 -08:00
LeuisKen
894a3bef4a chore: make babel not transform es modules. 2020-03-03 18:09:24 -08:00
LeuisKen
780d390856 chore: fix es6 export issue. 2020-03-03 18:09:24 -08:00
LeuisKen
5de8be447f fix: change export from commonjs to es module for import from src usage. 2020-03-03 18:09:24 -08:00
Zeno Rocha
132fcd16b1 Add Stale Bot 2020-02-29 12:18:50 -08:00
Zeno Rocha
e7f0ff0392 Fix composer deprecated package #659 2020-02-29 10:03:07 -08:00
domecardillo
393dbe34e0 replaced invalid property url with homepage in composer.json authors array 2020-02-29 09:55:42 -08:00
Zeno Rocha
d3fc3c1e7b Regenerate package-lock and dist files after PhantomJS removal 2020-02-29 09:55:14 -08:00
ossdev
83824fa248 ClipboardJS: Removed PhantomJS Dependency
Added karma-chrome-launcher dependency

Signed-off-by: ossdev <ossdev@puresoftware.com>
2020-02-29 09:53:41 -08:00
ryanmcdonough
ce79f170aa Create LICENSE 2019-12-27 20:56:56 -03:00
Christian Oliff
4c3a086866 HTTPS relevant links 2019-12-27 20:56:29 -03:00
Bjorn Hansen
20f64d82d0 Update broken link to Primer Tooltops doc 2019-04-18 14:23:49 -07:00
Christian Oliff
85981026d1 update URL in banner 2019-03-05 11:03:02 -08:00
Rouven Weßling
6802a86f60 Set babel-preset-env into UglifyJS compatability mode 2018-11-14 20:16:46 -08:00
Rouven Weßling
3522504d34 Remove the unused dev dependency babel-cli 2018-11-14 20:16:46 -08:00
Zeno Rocha
d17eca050e 2.0.4 2018-11-12 20:42:07 -08:00
kovenliao
5381600a26 fix webpack umd build (#599) 2018-11-12 20:35:19 -08:00
Zeno Rocha
5be63e28dd 2.0.3 2018-11-11 18:56:51 -08:00
Rouven Weßling
adc669df06 Always run webpack in production mode. (#595)
Otherwise horribly inefficent code that evals(!) strings of code is generated.
2018-11-11 18:53:43 -08:00
Rouven Weßling
b57e6d019f Don't claim to support ESM 2018-11-11 18:51:00 -08:00
Rouven Weßling
3d005b547e Maintain banner in minified file 2018-11-11 18:49:23 -08:00
Zeno Rocha
cf9e8fd7ce Updates devDependencies 2018-11-11 09:58:44 -08:00
Zeno Rocha
f1d99de5d3 Removes .npmignore file 2018-11-11 09:48:15 -08:00
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
Zeno Rocha
b3ad81570e Release v1.4.1 2015-10-09 09:08:02 +02:00
Zeno Rocha
17aedf5221 Merge pull request #74 from calvincorreli/patch-1
Fix for scrolling to top in unsupported firefox
2015-10-09 09:01:19 +02:00
Zeno Rocha
e14d92e2f4 Merge pull request #78 from arve0/watch_standalone
script build-watch should build standalone like build-debug
2015-10-07 22:06:00 +02:00
arve0
b7b2259dfb script build-watch should build standalone like build-debug 2015-10-06 15:55:21 +02:00
Calvin Correli
c5b416b108 Fix for scrolling to top in unsupported firefox
My previous fix didn't actually work. Instead of scrolling to the bottom it would scroll to the top, because it turns out document.body.scrollTop always returns 0 in Firefox.

This should work in most browsers.

See here: http://help.dottoro.com/ljnvjiow.php
2015-10-05 11:54:20 -04:00
Zeno Rocha
019b021624 Release v1.4.0 2015-10-03 19:17:42 -07:00
Zeno Rocha
8e56ee61c4 Moves advanced section to the bottom 2015-10-03 19:16:09 -07:00
Zeno Rocha
83a8effff9 Adds docs for #24 2015-10-03 19:11:29 -07:00
Kevin Malakoff
e5a6797c82 Got rid of ES7 notation and built library 2015-10-03 19:06:29 -07:00
Zeno Rocha
8dc4e2e132 Adds destroy method for ClipboardAction too #24 2015-10-03 19:05:12 -07:00
Zeno Rocha
0c24503214 Source formatting and testing for #24 2015-10-03 19:04:37 -07:00
Zeno Rocha
cc9d562580 Renames "initialize" method to "onClick" #51 2015-10-03 16:44:47 -07:00
Zeno Rocha
8fa31029ac Merge branch 'pr-63' 2015-10-03 16:25:34 -07:00
Zeno Rocha
c16137511c Source formatting #63 2015-10-03 16:25:20 -07:00
Vitor Cortez
15a66df290 Edit ´getAttributeValue´ to a function declaration
Favoring function declaration over a function expression in this case to follow style guidelines.
2015-10-03 16:25:20 -07:00
Vitor Cortez
ff4755fe4c Add helper method to dry default set functions 2015-10-03 16:25:20 -07:00
Zeno Rocha
0b9a0402b9 Source formatting #63 2015-10-03 16:25:06 -07:00
Zeno Rocha
b3fcd15a8e Merge pull request #57 from calvincorreli/patch-1
Prevent FF < 41 scrolling to bottom of  page
2015-10-03 15:51:24 -07:00
Zeno Rocha
d28418675b Rename CONTRIBUTING.md to contributing.md 2015-10-03 15:49:26 -07:00
Zeno Rocha
fab7b16bb8 Rename README.md to readme.md 2015-10-03 15:48:56 -07:00
Zeno Rocha
e4de506885 Merge pull request #55 from jaydson/contributing
Adding CONTRIBUTING guide
2015-10-03 15:46:37 -07:00
Zeno Rocha
3bdb55900d Merge pull request #65 from arve0/bower_main
point to dist in bower.json, fixes #64
2015-10-03 07:40:27 -07:00
arve0
71705c6431 point to dist in bower.json, fixes #64 2015-10-03 16:39:30 +02:00
Vitor Cortez
c666150278 Edit ´getAttributeValue´ to a function declaration
Favoring function declaration over a function expression in this case to follow style guidelines.
2015-10-02 20:46:02 -03:00
Vitor Cortez
cead303e53 Add helper method to dry default set functions 2015-10-02 19:56:10 -03:00
Zeno Rocha
b0d54c46fe Adds link to CDN file #30 #53 2015-10-02 14:59:27 -07:00
Calvin Correli
5c8af54b8a Prevent FF < 41 scrolling to bottom of page
In Firefox < 41, the selecting fakeElem.select() would cause the browser to scroll to the bottom of the page. 

By positioning fakeElem at the current scroll position, but still way out to the left, that no longer happens.
2015-10-02 09:32:16 -04:00
Zeno Rocha
a4c8bc5bf0 Merge pull request #48 from tzoky07/master
Added "build-watch" command. Added ".git" and "dist" directories to .gitignore
2015-10-01 12:07:13 -07:00
tzoky07
5147086c48 Added build-watch script using watchify. Also added the dev dependency
Signed-off-by: tzoky07 <alex.gaman@yahoo.com>
2015-10-01 21:55:49 +03:00
Jaydson Gomes
99899441c8 Known issues section 2015-10-01 14:43:01 -03:00
Jaydson Gomes
51309716cd Adding CONTRIBUTING guide 2015-10-01 14:39:42 -03:00
Zeno Rocha
c70a91a67e Moves browserify and babelify into dependencies #15 2015-10-01 08:35:17 -07:00
Zeno Rocha
14c5962816 Merge pull request #50 from JoshSpears3/npm-updates
Updated browserify and karma-browserify to fix npm install error
2015-10-01 07:46:37 -07:00
JoshSpears3
29d5127362 Updated browserify and karma-browserify to fix npm install error 2015-10-01 09:12:48 -05:00
Zeno Rocha
02c44d4a17 Merge pull request #44 from squarejaw/cleanup
Remove unused import and add missing semicolons
2015-09-30 19:45:30 -07:00
Bryan Bess
4d1fa1ba75 Remove unused import and add missing semicolons 2015-09-30 20:55:35 -05:00
Zeno Rocha
0abb217253 Merge pull request #38 from max-cross/master
Changed "fakeElem" element type.
2015-09-30 15:57:52 -07:00
Zeno Rocha
0085cbc49b Merge pull request #40 from webglider/patch-1
Update README.md
2015-09-30 11:15:18 -07:00
Maxim Lebedinets
e4f3fb226c Modified comments and changed element type for "ClipboardAction" class; 2015-09-30 20:52:48 +03:00
midhul varma
c3738cd899 Update README.md 2015-09-30 23:22:13 +05:30
Maxim Lebedinets
c041e2a8f0 changed "fakeElem" element type. 2015-09-30 20:22:45 +03:00
Zeno Rocha
1b062f72f5 Merge pull request #34 from mathiasbynens/lol
Use HTTPS URL
2015-09-30 07:37:45 -07:00
Mathias Bynens
fb3cb46d7c Use HTTPS URL 2015-09-30 16:37:04 +02:00
Zeno Rocha
1539bba290 Release v1.3.1 2015-09-29 22:35:01 -07:00
Zeno Rocha
3c414a6b2e Renames npm scripts #19 2015-09-29 22:33:39 -07:00
Zeno Rocha
aeec3fd520 Source formatting #19 2015-09-29 22:28:15 -07:00
Mauricio Soares
4534fc4ca0 Adds test for browserify
This commit adds tests to make sure that the browserify bundle will work in the dist file of clipboard.js

This commit adds the mocha and chai modules, since karma doesn't work well with node only tests.

Also splited tests tasks in package.json and updated .gitignore
2015-09-29 22:16:32 -07:00
Mauricio Soares
623614a4e0 Adds commonjs support
These configs in package.json enables the dist file to be required in commonjs envs without babel.
2015-09-29 22:16:32 -07:00
Zeno Rocha
a5e29bd420 Removes code from #constructor to separate functions 2015-09-29 22:14:26 -07:00
Zeno Rocha
3394f59691 Returns undefined instead of null if attributes does not exists #21 2015-09-29 18:24:33 -07:00
Jory Graham
d66aab1124 Default options for ClipboardAction too 2015-09-29 20:23:05 -04:00
Jory Graham
902c730a4d Use undefined for default parameters 2015-09-29 20:09:17 -04:00
rspecht
14baab7386 using default parameters instead the 'or' approach 2015-09-29 18:36:07 -03:00
Zeno Rocha
b5bc00f2e4 Release v1.3.0 2015-09-29 10:17:15 -07:00
Zeno Rocha
ffb2b3fcd9 Merge pull request #16 from mauriciosoares/feature/umd-support
Adds UMD support
2015-09-29 10:14:23 -07:00
Zeno Rocha
a4a68d8774 Merge pull request #14 from yannickoo/patch-2
Fix anchor links to demo
2015-09-29 08:01:32 -07:00
Mauricio Soares
05a807e2fb Adds UMD support
Using the --standalone option from browserify it automatically wrap your code into a UMD module.
2015-09-29 11:09:28 -03:00
Yannick
0102dd6453 fixes anchor links in readme file 2015-09-29 10:23:37 +02:00
Zeno Rocha
84d1949718 Merge pull request #11 from SpazzMarticus/master
Added unminified dist/clipboard.js
2015-09-29 00:00:18 -07:00
Zeno Rocha
b80f9f8aae Release v1.2.0 2015-09-28 23:56:45 -07:00
Zeno Rocha
2aff9ab55a Adds advanced usage docs 2015-09-28 23:54:56 -07:00
Zeno Rocha
194bf6aeb3 Source formatting 2015-09-28 23:54:34 -07:00
SpazzMarticus
fe6c408e48 Built dist/clipboard.js via npm run publish 2015-09-29 08:45:29 +02:00
SpazzMarticus
1d74794565 Publish-script builds dist/clipboard.js and minifies it to dist/clipboard.min.js 2015-09-29 08:43:23 +02:00
Zeno Rocha
1f61e16eb5 Fails silently in favor of speed 2015-09-28 21:59:18 -07:00
Zeno Rocha
775e4b898d Source formatting 2015-09-28 21:56:29 -07:00
Zeno Rocha
56bac2ce09 Release v1.1.0 2015-09-28 21:38:36 -07:00
Eduardo Lundgren
f34bf8eabe Update README.md 2015-09-29 01:28:59 -03:00
Eduardo Lundgren
b842987292 Adds support to set action/target/text via function 2015-09-29 01:15:21 -03:00
Eduardo Lundgren
beab7bc087 Changes target to support selector instead of id 2015-09-28 23:37:58 -03:00
Zeno Rocha
1ce64f39a2 Merge pull request #6 from mauriciosoares/rename-data-attributes
Rename data attributes to prefix "clipboard"
2015-09-28 10:12:53 -07:00
Mauricio Soares
40e6ac9674 run publish command 2015-09-28 14:08:34 -03:00
Mauricio Soares
157b0fb5a2 Rename data-attributes to prefix "clipboard"
This PR renames all the data-attributes for data-clipboard-X, this is due the possibility of conflict with projects that already uses these data-attributes.
2015-09-28 14:06:22 -03:00
Zeno Rocha
d5a4ba1ff0 Updates headline 2015-09-27 16:55:31 -07:00
Zeno Rocha
a9c50a74fa Adds Travis CI 2015-09-27 16:40:50 -07:00
Zeno Rocha
b0e118f750 Improves test coverage 2015-09-27 11:53:52 -07:00
Zeno Rocha
467684333f Moves from Node's require to ES6's import syntax 2015-09-26 17:31:18 -07:00
Zeno Rocha
1acd23049e Asserts error messages on tests 2015-09-26 09:26:54 -07:00
Zeno Rocha
abeee82bdc Throws error if either data-target or data-text were passed and throws error if neither data-target nor data-text were passed too 2015-09-26 09:25:15 -07:00
Zeno Rocha
ce7b9652c7 Includes .bind(this) polyfill for PhantomJS 2015-09-26 09:08:01 -07:00
Zeno Rocha
bb60a866b2 Fixes tests 2015-09-26 07:32:27 -07:00
Zeno Rocha
e3f69de585 Renames host argument to emitter 2015-09-26 07:31:59 -07:00
Zeno Rocha
aa6cc8e4df Merge branch 'master' of https://github.com/jaydson/clipboard.js into jaydson-master 2015-09-25 00:39:30 -07:00
Zeno Rocha
8d2fb2c08b Updates documentation 2015-09-25 00:31:45 -07:00
Zeno Rocha
1ac258dea5 Moves to a better delegate library 2015-09-24 22:23:15 -07:00
Zeno Rocha
34c798851d Only removes the fake element after another click event, that way an user can hit Ctrl+C to copy because selection still exists 2015-09-24 21:25:37 -07:00
Zeno Rocha
076e3b8a64 Destroys the previous ClipboardAction instance whenever a new click is triggered 2015-09-24 21:24:21 -07:00
Zeno Rocha
3610bfa08c Emits event on base class instead of each element for better performance 2015-09-24 18:19:40 -07:00
Zeno Rocha
ced945f11a Replaces every single event listener in favor of event delegation 2015-09-24 17:02:33 -07:00
Zeno Rocha
540038e2ad Adds documentation for each block 2015-09-24 16:18:50 -07:00
Zeno Rocha
1febe4eecc Adds karma test suite 2015-09-24 15:11:56 -07:00
Zeno Rocha
56dd1aac22 Handles attributes with getters/setters and breaks code into two classes 2015-09-24 15:11:11 -07:00
Zeno Rocha
e72ce02c87 Clears selection only if operation succeeded 2015-09-23 10:54:18 -07:00
Zeno Rocha
dedfbffe05 Fixes selection on non-editable elements 2015-09-22 23:32:04 -07:00
Jaydson Gomes
ba417cf53d Update Internet Explorer logo
Firefox logo was duplicated, just changed to the right IE logo.
2015-09-23 01:12:19 -03:00
Zeno Rocha
fbb2a316bf Removes fake element from screen instead making it transparent 2015-09-22 09:59:15 -07:00
Zeno Rocha
b4a748f89f Uses template string instead of concat strings 2015-09-22 08:33:34 -07:00
Zeno Rocha
e2b9ba69a6 Updates browser logos 2015-09-22 08:32:48 -07:00
Zeno Rocha
706d55504b Adds the why 2015-09-21 12:44:16 -07:00
Zeno Rocha
90878d90c4 Updates snippets to be the same as actual demos 2015-09-21 11:04:48 -07:00
Zeno Rocha
c92c4e545a Removes "no-support" event in favor of "error" and "copy/cut" in favor of "success" 2015-09-21 10:32:11 -07:00
Eduardo Lundgren
23b20d6006 Uses optimistic execCommand and removes redundant logic - Fixes #1 2015-09-21 09:39:41 -07:00
Zeno Rocha
f3c042a364 Only fire detailed events if copy was successful 2015-09-21 01:15:01 -07:00
Zeno Rocha
aebcbdf292 Updates headline and add default export 2015-09-21 00:37:02 -07:00
Zeno Rocha
8050bda877 Updates README according to docs 2015-09-20 16:26:55 -07:00
Zeno Rocha
cd7c8bfc27 Adds tests for Clipboard.validate 2015-09-20 15:40:59 -07:00
Zeno Rocha
52b444609e Improves error handling 2015-09-20 14:35:19 -07:00
Zeno Rocha
ec20389775 Adds tests for constructor 2015-09-20 00:57:19 -07:00
Zeno Rocha
b1d0ac9520 Makes compilation faster but less spec-compliant 2015-09-20 00:47:18 -07:00
Zeno Rocha
bea448d6c5 Includes base test structure 2015-09-20 00:23:33 -07:00
Zeno Rocha
66c18fbcb4 Breaks code into two classes 2015-09-19 18:03:31 -07:00
Zeno Rocha
24f4bc77ed Handles browsers that do not support this API 2015-09-19 16:44:30 -07:00
Zeno Rocha
960d1a9dd9 Adds browserify and babelify for import transformation 2015-09-19 15:42:36 -07:00
Zeno Rocha
7a92a67487 Adds CustomEvent polyfill 2015-09-19 14:02:56 -07:00
Zeno Rocha
76ab07a186 Triggers a custom event after copying/cutting commands 2015-09-19 11:38:19 -07:00
Zeno Rocha
718c1b33f0 Removes files that should belong only to gh-pages 2015-09-19 03:23:09 -07:00
46 changed files with 11643 additions and 568 deletions

11
.babelrc Normal file
View File

@@ -0,0 +1,11 @@
{
"presets": [
[
"@babel/env",
{
"forceAllTransforms": true,
"modules": false
}
]
]
}

View File

@@ -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
@@ -17,3 +17,6 @@ insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[{package.json,bower.json}]
indent_size = 2

12
.eslintignore Normal file
View 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
View 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
View 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?
Its fine if youre 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
View 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
View 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
View 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:**

21
.github/stale.yml vendored Normal file
View 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
View 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
View 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

4
.gitignore vendored
View File

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

1
.husky/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
_

4
.husky/pre-commit Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no-install lint-staged

1
.nvmrc Normal file
View File

@@ -0,0 +1 @@
14

9
.prettierignore Normal file
View 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
View File

@@ -0,0 +1,9 @@
{
"printWidth": 80,
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"trailingComma": "es5",
"bracketSpacing": true,
"arrowParens": "always"
}

21
LICENSE Normal file
View 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.

View File

@@ -1,55 +0,0 @@
# clipboard.js
> A modern approach to copy &amp; cut to the clipboard. No Flash. No dependencies. Just 1kb.
## Install
You can get it using bower:
```
bower install clipboard --save
```
Or [download as ZIP](https://github.com/zenorocha/clipboard.js/archive/master.zip).
## Usage
First, you need to instantiate it using a selector. This selector corresponds to the trigger element, usually a `<button>`.
```js
new Clipboard('.btn');
```
The easiest way to copy some content to the clipboard, is to include a `data-text` attribute in your trigger element.
```html
<button class="btn" data-text="Lorem ipsum">Copy</button>
```
Another way of doing it, is to copy the content from an another element. You can do that by adding a `data-target` attribute in your trigger element. The value you include on this attribute needs to match another's element `id` attribute.
```html
<p id="foo">Lorem ipsum</p>
<button class="btn" data-target="foo">Copy</button>
```
Additionally, you can define a `data-action` attribute to specify if you want to either `copy` or `cut` content. If you omit this attribute, `copy` will be used.
```html
<input id="foo" value="Lorem ipsum"></inpu>
<button class="btn" data-action="cut" data-target="foo">Copy</button>
```
As you may expect, the `cut` action only works on `<input>` or `<textarea>` elements.
## Browser Support
This project relies on both [Select API](https://developer.mozilla.org/en-US/docs/Web/API/Selection) and [execCommand API](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand). When combined, they're supported in the following browsers.
| <img src="https://raw.githubusercontent.com/alrra/browser-logos/master/chrome/chrome_64x64.png" width="48px" height="48px" alt="Chrome logo"> | <img src="https://raw.githubusercontent.com/alrra/browser-logos/master/firefox/firefox_64x64.png" width="48px" height="48px" alt="Firefox logo"> | <img src="https://raw.githubusercontent.com/alrra/browser-logos/master/internet-explorer/internet-explorer_64x64.png" width="48px" height="48px" alt="Internet Explorer logo"> | <img src="https://raw.githubusercontent.com/alrra/browser-logos/master/opera/opera_64x64.png" width="48px" height="48px" alt="Opera logo"> | <img src="https://raw.githubusercontent.com/alrra/browser-logos/master/safari/safari_64x64.png" width="48px" height="48px" alt="Safari logo"> |
|:---:|:---:|:---:|:---:|:---:|
| 42+ ✔ | 41+ ✔ | 9+ ✔ | 29+ ✔ | Nope ✘ |
## License
[MIT License](http://zenorocha.mit-license.org/) © Zeno Rocha

View File

@@ -1,17 +1,18 @@
{
"name": "clipboard",
"version": "0.0.0",
"description": "A modern approach to copy & cut to the clipboard",
"version": "2.0.6",
"description": "Modern copy to clipboard. No Flash. Just 3kb",
"license": "MIT",
"main": "src/clipboard.js",
"keywords": [
"clipboard",
"copy",
"cut"
"main": "dist/clipboard.js",
"ignore": [
"/.*/",
"/demo/",
"/test/",
"/.*",
"/bower.json",
"/karma.conf.js",
"/src",
"/lib"
],
"devDependencies": {
"highlightjs": "~8.8.0",
"octicons": "~3.1.0",
"primer-css": "~2.3.3"
}
"keywords": ["clipboard", "copy", "cut"]
}

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",
"homepage": "http://zenorocha.com/"
}
],
"require": {
"oomphinc/composer-installers-extender": "*"
},
"extra": {
"component": {
"scripts": [
"dist/clipboard.js"
],
"files": [
"dist/clipboard.min.js"
]
}
}
}

29
contributing.md Normal file
View File

@@ -0,0 +1,29 @@
# Contributing guide
Want to contribute to Clipboard.js? Awesome!
There are many ways you can contribute, see below.
## Opening issues
Open an issue to report bugs or to propose new features.
- Reporting bugs: describe the bug as clearly as you can, including steps to reproduce, what happened and what you were expecting to happen. Also include browser version, OS and other related software's (npm, Node.js, etc) versions when applicable.
- Proposing features: explain the proposed feature, what it should do, why it is useful, how users should use it. Give us as much info as possible so it will be easier to discuss, access and implement the proposed feature. When you're unsure about a certain aspect of the feature, feel free to leave it open for others to discuss and find an appropriate solution.
## Proposing pull requests
Pull requests are very welcome. Note that if you are going to propose drastic changes, be sure to open an issue for discussion first, to make sure that your PR will be accepted before you spend effort coding it.
Fork the Clipboard.js repository, clone it locally and create a branch for your proposed bug fix or new feature. Avoid working directly on the master branch.
Implement your bug fix or feature, write tests to cover it and make sure all tests are passing (run a final `npm test` to make sure everything is correct). Then commit your changes, push your bug fix/feature branch to the origin (your forked repo) and open a pull request to the upstream (the repository you originally forked)'s master branch.
## Documentation
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

232
demo.css
View File

@@ -1,232 +0,0 @@
body {
font-family: 'Lato', sans-serif;
}
.gradient {
background: #4cd964;
background: -moz-linear-gradient(45deg, #4cd964 0%, #5ac8fa 100%);
background: -webkit-gradient(left bottom, right top, color-stop(0%, #4cd964), color-stop(100%, #5ac8fa));
background: -webkit-linear-gradient(45deg, #4cd964 0%, #5ac8fa 100%);
background: -o-linear-gradient(45deg, #4cd964 0%, #5ac8fa 100%);
background: -ms-linear-gradient(45deg, #4cd964 0%, #5ac8fa 100%);
background: linear-gradient(45deg, #4cd964 0%, #5ac8fa 100%);
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4cd964', endColorstr='#5ac8fa', GradientType=1 );
margin: 0 auto;
}
/* ==========================================================================
Header
========================================================================== */
.header {
padding-top: 92px;
}
.title {
color: white;
font-size: 64px;
font-weight: 900;
letter-spacing: -1px;
margin: 0 0 20px;
}
.subtitle {
color: #16a085;
font-size: 27px;
font-weight: 400;
margin: 0 0 20px;
}
.subtitle + .subtitle {
color: white;
}
.gh-btns {
margin: 92px 0 0;
background: rgba(0, 0, 0, .1);
padding: 20px 0 10px;
}
/* ==========================================================================
Main
========================================================================== */
.wrap {
margin: 0 auto 90px;
width: 500px;
}
p {
color: #333;
font-size: 18px;
line-height: 1.7;
}
a {
color: #1BC1A1;
border-bottom: 1px dotted #1BC1A1;
-webkit-transition: opacity .3s ease-in-out;
transition: opacity .3s ease-in-out;
}
a:hover,
a:focus {
text-decoration: none;
opacity: .7;
}
h1 {
margin-top: 80px;
}
h3 {
color: #333;
margin: 40px 0;
font-size: 18px;
font-weight: 300;
text-align: center;
}
/* Code
========================================================================== */
pre code {
font-size: 14px;
line-height: 20px;
}
code {
background-color: rgba(0, 0, 0, 0.04);
border-radius: 3px;
font-size: 85%;
margin: 0;
padding: 0.2em;
}
.hljs-keyword {
color: #008080;
font-weight: normal;
}
/* Example
========================================================================== */
.example {
position: relative;
margin: 15px 0 0;
padding: 39px 19px 14px;
background-color: #fff;
border-radius: 4px 4px 0 0;
border: 1px solid #ddd;
}
.example p {
color: #666;
}
.example:after {
content: "Example";
position: absolute;
top: 0;
left: 0;
padding: 2px 8px;
font-size: 12px;
font-weight: bold;
background-color: #f5f5f5;
color: #9da0a4;
border-radius: 4px 0 4px 0;
}
.example + pre {
background: #f8f8f8;
border-radius: 4px;
border: 1px solid #ddd;
clear: both;
margin-top: -20px;
padding: 20px 5px 0;
}
/* Live example
========================================================================== */
.form-actions {
margin-top: 15px;
}
.form-actions .btn {
float: left;
}
textarea {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
width: 100%;
}
/* Support
========================================================================== */
.support {
list-style: none;
}
.support li {
display: inline-block;
text-align: center;
margin: 5px 8px 0;
}
.support p {
margin: 0;
}
/* ==========================================================================
Footer
========================================================================== */
.footer {
padding: 36px 0;
}
.credits {
font-weight: 400;
font-family: 'Lato', sans-serif;
font-size: 20px;
color: #16a085;
}
.credits-link {
color: white;
border-color: white;
}
.credits-link:hover,
.credits-link:focus {
text-decoration: none;
border-color: white;
}
.love {
display: inline-block;
position: relative;
top: .2em;
font-size: 1.4em;
-webkit-transform: scale(.9);
-moz-transform: scale(.9);
transform: scale(.9);
-webkit-animation: love .5s infinite linear alternate-reverse;
-moz-animation: love .5s infinite linear alternate-reverse;
animation: love .5s infinite linear alternate-reverse;
}
@-webkit-keyframes love {
to {-webkit-transform: scale(1.2);}
}
@-moz-keyframes love {
to {-moz-transform: scale(1.2);}
}
@keyframes love {
to {transform: scale(1.2);}
}

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>

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

@@ -0,0 +1,35 @@
<!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>

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

@@ -0,0 +1,35 @@
<!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>

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

@@ -0,0 +1,35 @@
<!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>

944
dist/clipboard.js vendored Normal file
View File

@@ -0,0 +1,944 @@
/*!
* clipboard.js v2.0.6
* https://clipboardjs.com/
*
* Licensed MIT © Zeno Rocha
*/
(function webpackUniversalModuleDefinition(root, factory) {
if(typeof exports === 'object' && typeof module === 'object')
module.exports = factory();
else if(typeof define === 'function' && define.amd)
define([], factory);
else if(typeof exports === 'object')
exports["ClipboardJS"] = factory();
else
root["ClipboardJS"] = factory();
})(this, function() {
return /******/ (() => { // webpackBootstrap
/******/ var __webpack_modules__ = ({
/***/ 134:
/***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
"use strict";
// EXPORTS
__webpack_require__.d(__webpack_exports__, {
"default": () => /* binding */ clipboard
});
// EXTERNAL MODULE: ./node_modules/select/src/select.js
var src_select = __webpack_require__(817);
var select_default = /*#__PURE__*/__webpack_require__.n(src_select);
;// CONCATENATED MODULE: ./src/clipboard-action.js
function _typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
/**
* Inner class which performs selection from either `text` or `target`
* properties and then executes copy or cut operations.
*/
var ClipboardAction = /*#__PURE__*/function () {
/**
* @param {Object} options
*/
function ClipboardAction(options) {
_classCallCheck(this, ClipboardAction);
this.resolveOptions(options);
this.initSelection();
}
/**
* Defines base properties passed from constructor.
* @param {Object} options
*/
_createClass(ClipboardAction, [{
key: "resolveOptions",
value: function resolveOptions() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
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.
*/
}, {
key: "initSelection",
value: function initSelection() {
if (this.text) {
this.selectFake();
} else if (this.target) {
this.selectTarget();
}
}
/**
* Creates a fake textarea element, sets its value from `text` property,
* and makes a selection on it.
*/
}, {
key: "selectFake",
value: function selectFake() {
var _this = this;
var isRTL = document.documentElement.getAttribute('dir') == 'rtl';
this.removeFake();
this.fakeHandlerCallback = function () {
return _this.removeFake();
};
this.fakeHandler = 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
var yPosition = window.pageYOffset || document.documentElement.scrollTop;
this.fakeElem.style.top = "".concat(yPosition, "px");
this.fakeElem.setAttribute('readonly', '');
this.fakeElem.value = this.text;
this.container.appendChild(this.fakeElem);
this.selectedText = select_default()(this.fakeElem);
this.copyText();
}
/**
* Only removes the fake element after another click event, that way
* a user can hit `Ctrl+C` to copy because selection still exists.
*/
}, {
key: "removeFake",
value: function removeFake() {
if (this.fakeHandler) {
this.container.removeEventListener('click', this.fakeHandlerCallback);
this.fakeHandler = null;
this.fakeHandlerCallback = null;
}
if (this.fakeElem) {
this.container.removeChild(this.fakeElem);
this.fakeElem = null;
}
}
/**
* Selects the content from element passed on `target` property.
*/
}, {
key: "selectTarget",
value: function selectTarget() {
this.selectedText = select_default()(this.target);
this.copyText();
}
/**
* Executes the copy operation based on the current selection.
*/
}, {
key: "copyText",
value: function copyText() {
var succeeded;
try {
succeeded = document.execCommand(this.action);
} catch (err) {
succeeded = false;
}
this.handleResult(succeeded);
}
/**
* Fires an event based on the copy operation result.
* @param {Boolean} succeeded
*/
}, {
key: "handleResult",
value: function handleResult(succeeded) {
this.emitter.emit(succeeded ? 'success' : 'error', {
action: this.action,
text: this.selectedText,
trigger: this.trigger,
clearSelection: this.clearSelection.bind(this)
});
}
/**
* Moves focus away from `target` and back to the trigger, removes current selection.
*/
}, {
key: "clearSelection",
value: function clearSelection() {
if (this.trigger) {
this.trigger.focus();
}
document.activeElement.blur();
window.getSelection().removeAllRanges();
}
/**
* Sets the `action` to be performed which can be either 'copy' or 'cut'.
* @param {String} action
*/
}, {
key: "destroy",
/**
* Destroy lifecycle.
*/
value: function destroy() {
this.removeFake();
}
}, {
key: "action",
set: function set() {
var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy';
this._action = action;
if (this._action !== 'copy' && this._action !== 'cut') {
throw new Error('Invalid "action" value, use either "copy" or "cut"');
}
}
/**
* Gets the `action` property.
* @return {String}
*/
,
get: function get() {
return this._action;
}
/**
* Sets the `target` property using an element
* that will be have its content copied.
* @param {Element} target
*/
}, {
key: "target",
set: function set(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: function get() {
return this._target;
}
}]);
return ClipboardAction;
}();
/* harmony default export */ const clipboard_action = (ClipboardAction);
// EXTERNAL MODULE: ./node_modules/tiny-emitter/index.js
var tiny_emitter = __webpack_require__(279);
var tiny_emitter_default = /*#__PURE__*/__webpack_require__.n(tiny_emitter);
// EXTERNAL MODULE: ./node_modules/good-listener/src/listen.js
var listen = __webpack_require__(370);
var listen_default = /*#__PURE__*/__webpack_require__.n(listen);
;// CONCATENATED MODULE: ./src/clipboard.js
function clipboard_typeof(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { clipboard_typeof = function _typeof(obj) { return typeof obj; }; } else { clipboard_typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return clipboard_typeof(obj); }
function clipboard_classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
function clipboard_defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
function clipboard_createClass(Constructor, protoProps, staticProps) { if (protoProps) clipboard_defineProperties(Constructor.prototype, protoProps); if (staticProps) clipboard_defineProperties(Constructor, staticProps); return Constructor; }
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
function _createSuper(Derived) { var hasNativeReflectConstruct = _isNativeReflectConstruct(); return function _createSuperInternal() { var Super = _getPrototypeOf(Derived), result; if (hasNativeReflectConstruct) { var NewTarget = _getPrototypeOf(this).constructor; result = Reflect.construct(Super, arguments, NewTarget); } else { result = Super.apply(this, arguments); } return _possibleConstructorReturn(this, result); }; }
function _possibleConstructorReturn(self, call) { if (call && (clipboard_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
function _isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
/**
* Base class which takes one or more elements, adds event listeners to them,
* and instantiates a new `ClipboardAction` on each click.
*/
var Clipboard = /*#__PURE__*/function (_Emitter) {
_inherits(Clipboard, _Emitter);
var _super = _createSuper(Clipboard);
/**
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
* @param {Object} options
*/
function Clipboard(trigger, options) {
var _this;
clipboard_classCallCheck(this, Clipboard);
_this = _super.call(this);
_this.resolveOptions(options);
_this.listenClick(trigger);
return _this;
}
/**
* Defines if attributes would be resolved using internal setter functions
* or custom functions that were passed in the constructor.
* @param {Object} options
*/
clipboard_createClass(Clipboard, [{
key: "resolveOptions",
value: function resolveOptions() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
this.action = typeof options.action === 'function' ? options.action : this.defaultAction;
this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;
this.text = typeof options.text === 'function' ? options.text : this.defaultText;
this.container = clipboard_typeof(options.container) === 'object' ? options.container : document.body;
}
/**
* Adds a click event listener to the passed trigger.
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
*/
}, {
key: "listenClick",
value: function listenClick(trigger) {
var _this2 = this;
this.listener = listen_default()(trigger, 'click', function (e) {
return _this2.onClick(e);
});
}
/**
* Defines a new `ClipboardAction` on each click event.
* @param {Event} e
*/
}, {
key: "onClick",
value: function onClick(e) {
var trigger = e.delegateTarget || e.currentTarget;
if (this.clipboardAction) {
this.clipboardAction = null;
}
this.clipboardAction = new clipboard_action({
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
*/
}, {
key: "defaultAction",
value: function defaultAction(trigger) {
return getAttributeValue('action', trigger);
}
/**
* Default `target` lookup function.
* @param {Element} trigger
*/
}, {
key: "defaultTarget",
value: function defaultTarget(trigger) {
var 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]
*/
}, {
key: "defaultText",
/**
* Default `text` lookup function.
* @param {Element} trigger
*/
value: function defaultText(trigger) {
return getAttributeValue('text', trigger);
}
/**
* Destroy lifecycle.
*/
}, {
key: "destroy",
value: function destroy() {
this.listener.destroy();
if (this.clipboardAction) {
this.clipboardAction.destroy();
this.clipboardAction = null;
}
}
}], [{
key: "isSupported",
value: function isSupported() {
var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];
var actions = typeof action === 'string' ? [action] : action;
var support = !!document.queryCommandSupported;
actions.forEach(function (action) {
support = support && !!document.queryCommandSupported(action);
});
return support;
}
}]);
return Clipboard;
}((tiny_emitter_default()));
/**
* Helper function to retrieve attribute value.
* @param {String} suffix
* @param {Element} element
*/
function getAttributeValue(suffix, element) {
var attribute = "data-clipboard-".concat(suffix);
if (!element.hasAttribute(attribute)) {
return;
}
return element.getAttribute(attribute);
}
/* harmony default export */ const clipboard = (Clipboard);
/***/ }),
/***/ 828:
/***/ ((module) => {
var DOCUMENT_NODE_TYPE = 9;
/**
* A polyfill for Element.matches()
*/
if (typeof Element !== 'undefined' && !Element.prototype.matches) {
var proto = Element.prototype;
proto.matches = proto.matchesSelector ||
proto.mozMatchesSelector ||
proto.msMatchesSelector ||
proto.oMatchesSelector ||
proto.webkitMatchesSelector;
}
/**
* Finds the closest parent that matches a selector.
*
* @param {Element} element
* @param {String} selector
* @return {Function}
*/
function closest (element, selector) {
while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {
if (typeof element.matches === 'function' &&
element.matches(selector)) {
return element;
}
element = element.parentNode;
}
}
module.exports = closest;
/***/ }),
/***/ 438:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
var closest = __webpack_require__(828);
/**
* Delegates event to a selector.
*
* @param {Element} element
* @param {String} selector
* @param {String} type
* @param {Function} callback
* @param {Boolean} useCapture
* @return {Object}
*/
function _delegate(element, selector, type, callback, useCapture) {
var listenerFn = listener.apply(this, arguments);
element.addEventListener(type, listenerFn, useCapture);
return {
destroy: function() {
element.removeEventListener(type, listenerFn, useCapture);
}
}
}
/**
* Delegates event to a selector.
*
* @param {Element|String|Array} [elements]
* @param {String} selector
* @param {String} type
* @param {Function} callback
* @param {Boolean} useCapture
* @return {Object}
*/
function delegate(elements, selector, type, callback, useCapture) {
// Handle the regular Element usage
if (typeof elements.addEventListener === 'function') {
return _delegate.apply(null, arguments);
}
// Handle Element-less usage, it defaults to global delegation
if (typeof type === 'function') {
// Use `document` as the first parameter, then apply arguments
// This is a short way to .unshift `arguments` without running into deoptimizations
return _delegate.bind(null, document).apply(null, arguments);
}
// Handle Selector-based usage
if (typeof elements === 'string') {
elements = document.querySelectorAll(elements);
}
// Handle Array-like based usage
return Array.prototype.map.call(elements, function (element) {
return _delegate(element, selector, type, callback, useCapture);
});
}
/**
* Finds closest match and invokes callback.
*
* @param {Element} element
* @param {String} selector
* @param {String} type
* @param {Function} callback
* @return {Function}
*/
function listener(element, selector, type, callback) {
return function(e) {
e.delegateTarget = closest(e.target, selector);
if (e.delegateTarget) {
callback.call(element, e);
}
}
}
module.exports = delegate;
/***/ }),
/***/ 879:
/***/ ((__unused_webpack_module, exports) => {
/**
* Check if argument is a HTML element.
*
* @param {Object} value
* @return {Boolean}
*/
exports.node = function(value) {
return value !== undefined
&& value instanceof HTMLElement
&& value.nodeType === 1;
};
/**
* Check if argument is a list of HTML elements.
*
* @param {Object} value
* @return {Boolean}
*/
exports.nodeList = function(value) {
var type = Object.prototype.toString.call(value);
return value !== undefined
&& (type === '[object NodeList]' || type === '[object HTMLCollection]')
&& ('length' in value)
&& (value.length === 0 || exports.node(value[0]));
};
/**
* Check if argument is a string.
*
* @param {Object} value
* @return {Boolean}
*/
exports.string = function(value) {
return typeof value === 'string'
|| value instanceof String;
};
/**
* Check if argument is a function.
*
* @param {Object} value
* @return {Boolean}
*/
exports.fn = function(value) {
var type = Object.prototype.toString.call(value);
return type === '[object Function]';
};
/***/ }),
/***/ 370:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {
var is = __webpack_require__(879);
var delegate = __webpack_require__(438);
/**
* Validates all params and calls the right
* listener function based on its target type.
*
* @param {String|HTMLElement|HTMLCollection|NodeList} target
* @param {String} type
* @param {Function} callback
* @return {Object}
*/
function listen(target, type, callback) {
if (!target && !type && !callback) {
throw new Error('Missing required arguments');
}
if (!is.string(type)) {
throw new TypeError('Second argument must be a String');
}
if (!is.fn(callback)) {
throw new TypeError('Third argument must be a Function');
}
if (is.node(target)) {
return listenNode(target, type, callback);
}
else if (is.nodeList(target)) {
return listenNodeList(target, type, callback);
}
else if (is.string(target)) {
return listenSelector(target, type, callback);
}
else {
throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');
}
}
/**
* Adds an event listener to a HTML element
* and returns a remove listener function.
*
* @param {HTMLElement} node
* @param {String} type
* @param {Function} callback
* @return {Object}
*/
function listenNode(node, type, callback) {
node.addEventListener(type, callback);
return {
destroy: function() {
node.removeEventListener(type, callback);
}
}
}
/**
* Add an event listener to a list of HTML elements
* and returns a remove listener function.
*
* @param {NodeList|HTMLCollection} nodeList
* @param {String} type
* @param {Function} callback
* @return {Object}
*/
function listenNodeList(nodeList, type, callback) {
Array.prototype.forEach.call(nodeList, function(node) {
node.addEventListener(type, callback);
});
return {
destroy: function() {
Array.prototype.forEach.call(nodeList, function(node) {
node.removeEventListener(type, callback);
});
}
}
}
/**
* Add an event listener to a selector
* and returns a remove listener function.
*
* @param {String} selector
* @param {String} type
* @param {Function} callback
* @return {Object}
*/
function listenSelector(selector, type, callback) {
return delegate(document.body, selector, type, callback);
}
module.exports = listen;
/***/ }),
/***/ 817:
/***/ ((module) => {
function select(element) {
var selectedText;
if (element.nodeName === 'SELECT') {
element.focus();
selectedText = element.value;
}
else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
var isReadOnly = element.hasAttribute('readonly');
if (!isReadOnly) {
element.setAttribute('readonly', '');
}
element.select();
element.setSelectionRange(0, element.value.length);
if (!isReadOnly) {
element.removeAttribute('readonly');
}
selectedText = element.value;
}
else {
if (element.hasAttribute('contenteditable')) {
element.focus();
}
var selection = window.getSelection();
var range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
selectedText = selection.toString();
}
return selectedText;
}
module.exports = select;
/***/ }),
/***/ 279:
/***/ ((module) => {
function E () {
// Keep this empty so it's easier to inherit from
// (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
}
E.prototype = {
on: function (name, callback, ctx) {
var e = this.e || (this.e = {});
(e[name] || (e[name] = [])).push({
fn: callback,
ctx: ctx
});
return this;
},
once: function (name, callback, ctx) {
var self = this;
function listener () {
self.off(name, listener);
callback.apply(ctx, arguments);
};
listener._ = callback
return this.on(name, listener, ctx);
},
emit: function (name) {
var data = [].slice.call(arguments, 1);
var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
var i = 0;
var len = evtArr.length;
for (i; i < len; i++) {
evtArr[i].fn.apply(evtArr[i].ctx, data);
}
return this;
},
off: function (name, callback) {
var e = this.e || (this.e = {});
var evts = e[name];
var liveEvents = [];
if (evts && callback) {
for (var i = 0, len = evts.length; i < len; i++) {
if (evts[i].fn !== callback && evts[i].fn._ !== callback)
liveEvents.push(evts[i]);
}
}
// Remove event from queue to prevent memory leak
// Suggested by https://github.com/lazd
// Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910
(liveEvents.length)
? e[name] = liveEvents
: delete e[name];
return this;
}
};
module.exports = E;
module.exports.TinyEmitter = E;
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ if(__webpack_module_cache__[moduleId]) {
/******/ return __webpack_module_cache__[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/************************************************************************/
/******/ /* webpack/runtime/compat get default export */
/******/ (() => {
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = (module) => {
/******/ var getter = module && module.__esModule ?
/******/ () => module['default'] :
/******/ () => module;
/******/ __webpack_require__.d(getter, { a: getter });
/******/ return getter;
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/define property getters */
/******/ (() => {
/******/ // define getter functions for harmony exports
/******/ __webpack_require__.d = (exports, definition) => {
/******/ for(var key in definition) {
/******/ if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ }
/******/ }
/******/ };
/******/ })();
/******/
/******/ /* webpack/runtime/hasOwnProperty shorthand */
/******/ (() => {
/******/ __webpack_require__.o = (obj, prop) => Object.prototype.hasOwnProperty.call(obj, prop)
/******/ })();
/******/
/************************************************************************/
/******/ // module exports must be returned from runtime so entry inlining is disabled
/******/ // startup
/******/ // Load entry module and return exports
/******/ return __webpack_require__(134);
/******/ })()
.default;
});

File diff suppressed because one or more lines are too long

View File

@@ -1,168 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>clipboard.js</title>
<link rel="stylesheet" href="bower_components/primer-css/css/primer.css">
<link rel="stylesheet" href="bower_components/octicons/octicons/octicons.css">
<link rel="stylesheet" href="bower_components/highlightjs/styles/github.css">
<link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Lato:300,400,700,900">
<link rel="stylesheet" href="demo.css">
</head>
<body>
<header class="header gradient text-center">
<h1 class="title">clipboard.js</h1>
<h2 class="subtitle">A modern approach to copy &amp; cut to the clipboard</h2>
<h2 class="subtitle">No Flash. No dependencies. Just 2kb</h2>
<p class="gh-btns">
<iframe src="http://ghbtns.com/github-btn.html?user=zenorocha&amp;repo=clipboard.js&amp;type=watch&amp;count=true&amp;size=large"
allowtransparency="true" frameborder="0" scrolling="0" width="152" height="30"></iframe>
<iframe src="http://ghbtns.com/github-btn.html?user=zenorocha&amp;repo=clipboard.js&amp;type=fork&amp;count=true&amp;size=large"
allowtransparency="true" frameborder="0" scrolling="0" width="156" height="30"></iframe>
</p>
</header>
<main class="wrap">
<h1>Install</h1>
<p>You can get it on npm.</p>
<pre><code class="js">npm install clipboard --save</code></pre>
<p>Or bower, too.</p>
<pre><code class="js">bower install clipboard --save</code></pre>
<p>If you're not into package management, just <a href="https://github.com/zenorocha/clipboard.js/archive/master.zip">download a ZIP</a> file.</p>
<h1>Setup</h1>
<p>First, include the script located on the <code>dist</code> folder</p>
<pre><code class="html">&lt;script src="dist/clipboard.min.js"&gt;&lt;/script&gt;</code></pre>
<p>Now, you need to instantiate it using a DOM selector. This selector corresponds to the trigger element, i.e. <code>&lt;button&gt;</code>.</p>
<pre><code class="html">&lt;script&gt; new Clipboard('.btn'); &lt;/script&gt;</code></pre>
<h1>Usage</h1>
<p>We're living a <em>declarative renaissance</em>, that's why we decided to take advantage of <a href="https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_data_attributes">HTML5 data attributes</a> for better usability.</p>
<h3>Copy text from attribute</h3>
<p>The easiest way to copy some content to the clipboard, is to include a <code>data-text</code> attribute in your trigger element.</p>
<div class="example">
<button class="btn" data-action="copy" data-text="Just because you can doesn't mean you should — clipboard.js">Copy to the clipboard</button>
</div>
<pre><code class="html">&lt;!-- Trigger --&gt;
&lt;button class="btn" data-text="Heya!"&gt;Copy&lt;/button&gt;</code></pre>
<h3>Copy text from another element</h3>
<p>Alternatively, you can copy content from another element by adding a <code>data-target</code> attribute in your trigger element.</p>
<p>The value you include on this attribute needs to match another's element <code>id</code> attribute.</p>
<div class="example">
<div class="input-group">
<input id="foo" type="text" value="https://github.com/zenorocha/clipboard.js.git">
<span class="input-group-button">
<button class="btn" type="button" data-action="copy" data-target="foo">
<span class="octicon octicon-clippy"></span>
</button>
</span>
</div>
</div>
<pre><code class="html">&lt;!-- Target --&gt;
&lt;input id="foo" value="https://git.io/vn3cM"&gt;
&lt;!-- Trigger --&gt;
&lt;button class="btn" data-target="foo"&gt;Copy&lt;/button&gt;</code></pre>
<h3>Cut text from another element</h3>
<p>Additionally, you can define a <code>data-action</code> attribute to specify if you want to either <code>copy</code> or <code>cut</code> content.</p>
<p>If you omit this attribute, <code>copy</code> will be used by default.</p>
<div class="example">
<div class="input-group">
<textarea id="bar" cols="62" rows="4" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false">Mussum ipsum cacilds, vidis litro abertis. Consetis adipiscings elitis. Pra lá , depois divoltis porris, paradis. Paisis, filhis, espiritis santis. Mé faiz elementum girarzis, nisi eros vermeio, in elementis mé pra quem é amistosis quis leo. Manduma pindureta quium dia nois paga.</textarea>
</div>
<div class="form-actions">
<button class="btn" type="button" data-action="cut" data-target="bar">
Cut to the clipboard
</button>
</div>
</div>
<pre><code class="html">&lt;!-- Target --&gt;
&lt;textarea id="bar"&gt;clipboard.js rocks!&lt;/textarea&gt;
&lt;!-- Trigger --&gt;
&lt;button class="btn" data-action="cut" data-target="bar"&gt;
Copy
&lt;/button&gt;</code></pre>
<p>As you may expect, the <code>cut</code> action only works on <code>&lt;input&gt;</code> or <code>&lt;textarea&gt;</code> elements.</p>
<h1>Browser Support</h1>
<p>This library relies on both <a href="https://developer.mozilla.org/en-US/docs/Web/API/Selection">Selection</a> and <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand">execCommand</a> APIs. When combined, they're supported in the following browsers.</p>
<ul class="support">
<li>
<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/chrome/chrome_128x128.png" width="64" height="64" alt="Chrome logo">
<p>Chrome 42+</p>
<li>
<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/firefox/firefox_128x128.png" width="64" height="64" alt="Firefox logo">
<p>Firefox 41+</p>
</li>
<li>
<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/internet-explorer/internet-explorer_128x128.png" width="64" height="64" alt="Internet Explorer logo">
<p>IE 9+</p>
</li>
<li>
<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/opera/opera_128x128.png" width="64" height="64" alt="Opera logo">
<p>Opera 29+</p>
</li>
<li>
<img src="https://raw.githubusercontent.com/alrra/browser-logos/master/safari/safari_128x128.png" width="64" height="64" alt="Safari logo">
<p>Safari ✘</p>
</li>
</ul>
</main>
<footer class="footer gradient text-center">
<p class="credits">
Made with <span class="love"></span> by <a class="credits-link" href="http://zenorocha.com/">Zeno Rocha</a> under <a class="credits-link" href="http://zenorocha.mit-license.org/">MIT license</a>
</p>
</footer>
<!-- Clipboard.js -->
<script src="dist/clipboard.min.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
new Clipboard('.btn');
});
</script>
<!-- Highlight.js -->
<script src="bower_components/highlightjs/highlight.pack.min.js"></script>
<script>hljs.initHighlightingOnLoad();</script>
<!-- Google Analytics -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-4114546-44', 'auto');
ga('send', 'pageview');
</script>
</body>
</html>

36
karma.conf.js Normal file
View File

@@ -0,0 +1,36 @@
var webpackConfig = require('./webpack.config.js');
module.exports = function (karma) {
karma.set({
plugins: [
'karma-webpack',
'karma-chai',
'karma-sinon',
'karma-mocha',
'karma-chrome-launcher',
],
frameworks: ['chai', 'sinon', 'mocha', 'webpack'],
files: [
{ pattern: 'src/**/*.js', watched: false },
{ pattern: 'test/**/*.js', watched: false },
],
preprocessors: {
'src/**/*.js': ['webpack'],
'test/**/*.js': ['webpack'],
},
webpack: {
module: webpackConfig.module,
plugins: webpackConfig.plugins,
},
webpackMiddleware: {
stats: 'errors-only',
},
browsers: ['ChromeHeadless'],
});
};

8862
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.6',
git: 'https://github.com/zenorocha/clipboard.js',
});
Package.onUse(function (api) {
api.addFiles('dist/clipboard.js', 'client');
});

View File

@@ -1,16 +1,63 @@
{
"name": "clipboard",
"version": "0.0.0",
"description": "A modern approach to copy to the clipboard",
"main": "src/clipboard.js",
"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",
"cut"
],
"dependencies": {
"babel": "^5.8.23",
"uglify": "^0.1.5"
"good-listener": "^1.2.2",
"select": "^1.1.2",
"tiny-emitter": "^2.0.0"
},
"devDependencies": {
"@babel/core": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"babel-loader": "^8.2.2",
"chai": "^4.2.0",
"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-chrome-launcher": "^3.1.0",
"karma-mocha": "^2.0.1",
"karma-sinon": "^1.0.4",
"karma-webpack": "^5.0.0-alpha.5",
"lint-staged": "^10.5.3",
"mocha": "^8.2.1",
"prettier": "2.2.1",
"sinon": "^9.2.3",
"tsd": "^0.7.2",
"uglifyjs-webpack-plugin": "^2.2.0",
"webpack": "^5.15.0",
"webpack-cli": "^4.4.0"
},
"scripts": {
"start" : "npm run build && npm run minify",
"build" : "babel src/clipboard.js --out-file dist/clipboard.min.js",
"watch" : "babel src/clipboard.js --out-file dist/clipboard.min.js --watch",
"minify": "uglify -s dist/clipboard.min.js -o dist/clipboard.min.js"
"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",
"lint": "eslint --ext .js src/"
},
"lint-staged": {
"*.{js,css,md}": [
"prettier --write",
"eslint --fix"
]
}
}

192
readme.md Normal file
View File

@@ -0,0 +1,192 @@
# clipboard.js
![Build Status](https://github.com/zenorocha/clipboard.js/workflows/build/badge.svg)
![Killing Flash](https://img.shields.io/badge/killing-flash-brightgreen.svg?style=flat)
> Modern copy to clipboard. No Flash. Just 3kb gzipped.
<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
Copying text to the clipboard shouldn't be hard. It shouldn't require dozens of steps to configure or hundreds of KBs to load. But most of all, it shouldn't depend on Flash or any bloated framework.
That's why clipboard.js exists.
## Install
You can get it on npm.
```
npm install clipboard --save
```
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 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>
```
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 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](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
We're living a _declarative renaissance_, that's why we decided to take advantage of [HTML5 data attributes](https://developer.mozilla.org/en-US/docs/Web/Guide/HTML/Using_data_attributes) for better usability.
### Copy text from another element
A pretty common use case is to copy content from another element. You can do that by adding a `data-clipboard-target` attribute in your trigger element.
The value you include on this attribute needs to match another's element selector.
<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 -->
<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" />
</button>
```
### Cut text from another element
Additionally, you can define a `data-clipboard-action` attribute to specify if you want to either `copy` or `cut` content.
If you omit this attribute, `copy` will be used by default.
<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 -->
<textarea id="bar">Mussum ipsum cacilds...</textarea>
<!-- Trigger -->
<button class="btn" data-clipboard-action="cut" data-clipboard-target="#bar">
Cut to clipboard
</button>
```
As you may expect, the `cut` action only works on `<input>` or `<textarea>` elements.
### Copy text from attribute
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://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 -->
<button
class="btn"
data-clipboard-text="Just because you can doesn't mean you should — clipboard.js"
>
Copy to clipboard
</button>
```
## Events
There are cases where you'd like to show some user feedback or capture what has been selected after a copy/cut operation.
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 ClipboardJS('.btn');
clipboard.on('success', function (e) {
console.info('Action:', e.action);
console.info('Text:', e.text);
console.info('Trigger:', e.trigger);
e.clearSelection();
});
clipboard.on('error', function (e) {
console.error('Action:', e.action);
console.error('Trigger:', e.trigger);
});
```
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://primer.style/css/components/tooltips). You may want to check that out if you're looking for a similar look and feel.
## Advanced Options
If you don't want to modify your HTML, there's a pretty handy imperative API for you to use. All you need to do is declare a function, do your thing, and return a value.
For instance, if you want to dynamically set a `target`, you'll need to return a Node.
```js
new ClipboardJS('.btn', {
target: function (trigger) {
return trigger.nextElementSibling;
},
});
```
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');
},
});
```
For use in Bootstrap Modals or with any other library that changes the focus you'll want to set the focused element as the `container` value.
```js
new 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 first one is [supported by all browsers](https://caniuse.com/#search=selection) while the second one is supported in the following browsers.
| <img src="https://clipboardjs.com/assets/images/chrome.png" width="48px" height="48px" alt="Chrome logo"> | <img src="https://clipboardjs.com/assets/images/edge.png" width="48px" height="48px" alt="Edge logo"> | <img src="https://clipboardjs.com/assets/images/firefox.png" width="48px" height="48px" alt="Firefox logo"> | <img src="https://clipboardjs.com/assets/images/ie.png" width="48px" height="48px" alt="Internet Explorer logo"> | <img src="https://clipboardjs.com/assets/images/opera.png" width="48px" height="48px" alt="Opera logo"> | <img src="https://clipboardjs.com/assets/images/safari.png" width="48px" height="48px" alt="Safari logo"> |
| :-------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------: |
| 42+ ✔ | 12+ ✔ | 41+ ✔ | 9+ ✔ | 29+ ✔ | 10+ ✔ |
The good news is that clipboard.js gracefully degrades if you need to support older browsers. All you have to do is show a tooltip saying `Copied!` when `success` event is called and `Press Ctrl+C to copy` when `error` event is called because the text is already selected.
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.
## 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
[MIT License](https://zenorocha.mit-license.org/) © Zeno Rocha

221
src/clipboard-action.js Normal file
View File

@@ -0,0 +1,221 @@
import select from 'select';
/**
* Inner class which performs selection from either `text` or `target`
* properties and then executes copy or cut operations.
*/
class ClipboardAction {
/**
* @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;
}
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;
}
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();
}
/**
* 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();
}
}
export default ClipboardAction;

84
src/clipboard.d.ts vendored Normal file
View 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;

View File

@@ -1,94 +1,142 @@
class Clipboard {
import Emitter from 'tiny-emitter';
import listen from 'good-listener';
import ClipboardAction from './clipboard-action';
// Constructor
/**
* Helper function to retrieve attribute value.
* @param {String} suffix
* @param {Element} element
*/
function getAttributeValue(suffix, element) {
const attribute = `data-clipboard-${suffix}`;
constructor(triggers) {
this._triggers = triggers;
this.init();
}
if (!element.hasAttribute(attribute)) {
return;
}
// Getters & Setters
get triggers() {
return document.querySelectorAll(this._triggers);
}
set triggers(val) {
return this._triggers = val;
}
// Methods
init() {
if (this.triggers.length > 0) {
[].forEach.call(this.triggers, (trigger) => this.bind(trigger));
}
else {
throw new Error('The provided selector is empty');
}
}
bind(trigger) {
trigger.addEventListener('click', (e) => this.select(e));
}
select(e) {
let actionAttr = e.currentTarget.getAttribute('data-action') || 'copy';
let targetAttr = e.currentTarget.getAttribute('data-target');
let textAttr = e.currentTarget.getAttribute('data-text');
if (textAttr) {
this.selectValue(textAttr, actionAttr);
}
else if (targetAttr) {
this.selectTarget(targetAttr, actionAttr);
}
else {
throw new Error('Missing "data-target" or "data-text" attribute');
}
e.preventDefault();
}
selectValue(textAttr, actionAttr) {
let fake = document.createElement('input');
fake.value = textAttr;
fake.style.opacity = 0;
fake.style.zIndex = -1;
document.body.appendChild(fake);
fake.select();
this.copy(actionAttr);
document.body.removeChild(fake);
}
selectTarget(targetAttr, actionAttr) {
let target = document.getElementById(targetAttr);
if (target.nodeName === 'INPUT' || target.nodeName === 'TEXTAREA') {
target.select();
}
else {
let range = document.createRange();
range.selectNode(target);
window.getSelection().addRange(range);
}
this.copy(actionAttr);
}
copy(actionAttr) {
try {
let successful = document.execCommand(actionAttr);
if (!successful) throw 'Invalid "data-action" attribute';
window.getSelection().removeAllRanges();
}
catch (err) {
throw new Error(err);
}
}
return element.getAttribute(attribute);
}
/**
* 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
View File

@@ -0,0 +1,4 @@
import { expectType } from 'tsd';
import Clipboard from './clipboard';
expectType<Clipboard>(new Clipboard('.btn'));

248
test/clipboard-action.js Normal file
View File

@@ -0,0 +1,248 @@
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);
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(() => {
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);
});
});
});

133
test/clipboard.js Normal file
View File

@@ -0,0 +1,133 @@
import Clipboard from '../src/clipboard';
import ClipboardAction from '../src/clipboard-action';
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.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);
});
});
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);
});
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();
}
});
});
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);
});
});
});

46
webpack.config.js Normal file
View File

@@ -0,0 +1,46 @@
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://clipboardjs.com/
Licensed MIT © Zeno Rocha`;
module.exports = {
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 })],
};