mirror of
https://github.com/zenorocha/clipboard.js.git
synced 2023-08-10 21:12:48 +03:00
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
This commit is contained in:
commit
ddb5adc6f8
@ -7,7 +7,7 @@ root = true
|
|||||||
[*]
|
[*]
|
||||||
# Change these settings to your own preference
|
# Change these settings to your own preference
|
||||||
indent_style = space
|
indent_style = space
|
||||||
indent_size = 4
|
indent_size = 2
|
||||||
|
|
||||||
# We recommend you to keep these unchanged
|
# We recommend you to keep these unchanged
|
||||||
end_of_line = lf
|
end_of_line = lf
|
||||||
|
9
.prettierignore
Normal file
9
.prettierignore
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
# Ignore artifacts:
|
||||||
|
dist
|
||||||
|
|
||||||
|
lib
|
||||||
|
npm-debug.log
|
||||||
|
bower_components
|
||||||
|
node_modules
|
||||||
|
yarn-error.log
|
||||||
|
yarn.lock
|
9
.prettierrc.json
Normal file
9
.prettierrc.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 80,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "es5",
|
||||||
|
"bracketSpacing": true,
|
||||||
|
"arrowParens": "always"
|
||||||
|
}
|
@ -14,9 +14,5 @@
|
|||||||
"/src",
|
"/src",
|
||||||
"/lib"
|
"/lib"
|
||||||
],
|
],
|
||||||
"keywords": [
|
"keywords": ["clipboard", "copy", "cut"]
|
||||||
"clipboard",
|
|
||||||
"copy",
|
|
||||||
"cut"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
@ -24,5 +24,6 @@ Implement your bug fix or feature, write tests to cover it and make sure all tes
|
|||||||
Documentation is extremely important and takes a fair deal of time and effort to write and keep updated. Please submit any and all improvements you can make to the repository's docs.
|
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
|
## Known issues
|
||||||
|
|
||||||
If you're using npm@3 you'll probably face some issues related to peerDependencies.
|
If you're using npm@3 you'll probably face some issues related to peerDependencies.
|
||||||
https://github.com/npm/npm/issues/9204
|
https://github.com/npm/npm/issues/9204
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<title>constructor-node</title>
|
<title>constructor-node</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- 1. Define some markup -->
|
<!-- 1. Define some markup -->
|
||||||
<div id="btn" data-clipboard-text="1">
|
<div id="btn" data-clipboard-text="1">
|
||||||
<span>Copy</span>
|
<span>Copy</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 2. Include library -->
|
<!-- 2. Include library -->
|
||||||
@ -16,16 +16,16 @@
|
|||||||
|
|
||||||
<!-- 3. Instantiate clipboard by passing a HTML element -->
|
<!-- 3. Instantiate clipboard by passing a HTML element -->
|
||||||
<script>
|
<script>
|
||||||
var btn = document.getElementById('btn');
|
var btn = document.getElementById('btn');
|
||||||
var clipboard = new ClipboardJS(btn);
|
var clipboard = new ClipboardJS(btn);
|
||||||
|
|
||||||
clipboard.on('success', function(e) {
|
clipboard.on('success', function (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
clipboard.on('error', function(e) {
|
clipboard.on('error', function (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<title>constructor-nodelist</title>
|
<title>constructor-nodelist</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- 1. Define some markup -->
|
<!-- 1. Define some markup -->
|
||||||
<button data-clipboard-text="1">Copy</button>
|
<button data-clipboard-text="1">Copy</button>
|
||||||
<button data-clipboard-text="2">Copy</button>
|
<button data-clipboard-text="2">Copy</button>
|
||||||
@ -16,16 +16,16 @@
|
|||||||
|
|
||||||
<!-- 3. Instantiate clipboard by passing a list of HTML elements -->
|
<!-- 3. Instantiate clipboard by passing a list of HTML elements -->
|
||||||
<script>
|
<script>
|
||||||
var btns = document.querySelectorAll('button');
|
var btns = document.querySelectorAll('button');
|
||||||
var clipboard = new ClipboardJS(btns);
|
var clipboard = new ClipboardJS(btns);
|
||||||
|
|
||||||
clipboard.on('success', function(e) {
|
clipboard.on('success', function (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
clipboard.on('error', function(e) {
|
clipboard.on('error', function (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<title>constructor-selector</title>
|
<title>constructor-selector</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- 1. Define some markup -->
|
<!-- 1. Define some markup -->
|
||||||
<button class="btn" data-clipboard-text="1">Copy</button>
|
<button class="btn" data-clipboard-text="1">Copy</button>
|
||||||
<button class="btn" data-clipboard-text="2">Copy</button>
|
<button class="btn" data-clipboard-text="2">Copy</button>
|
||||||
@ -16,15 +16,15 @@
|
|||||||
|
|
||||||
<!-- 3. Instantiate clipboard by passing a string selector -->
|
<!-- 3. Instantiate clipboard by passing a string selector -->
|
||||||
<script>
|
<script>
|
||||||
var clipboard = new ClipboardJS('.btn');
|
var clipboard = new ClipboardJS('.btn');
|
||||||
|
|
||||||
clipboard.on('success', function(e) {
|
clipboard.on('success', function (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
clipboard.on('error', function(e) {
|
clipboard.on('error', function (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<title>function-target</title>
|
<title>function-target</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- 1. Define some markup -->
|
<!-- 1. Define some markup -->
|
||||||
<button class="btn">Copy</button>
|
<button class="btn">Copy</button>
|
||||||
<div>hello</div>
|
<div>hello</div>
|
||||||
@ -15,19 +15,19 @@
|
|||||||
|
|
||||||
<!-- 3. Instantiate clipboard -->
|
<!-- 3. Instantiate clipboard -->
|
||||||
<script>
|
<script>
|
||||||
var clipboard = new ClipboardJS('.btn', {
|
var clipboard = new ClipboardJS('.btn', {
|
||||||
target: function() {
|
target: function () {
|
||||||
return document.querySelector('div');
|
return document.querySelector('div');
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
clipboard.on('success', function(e) {
|
clipboard.on('success', function (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
clipboard.on('error', function(e) {
|
clipboard.on('error', function (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<title>function-text</title>
|
<title>function-text</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- 1. Define some markup -->
|
<!-- 1. Define some markup -->
|
||||||
<button class="btn">Copy</button>
|
<button class="btn">Copy</button>
|
||||||
|
|
||||||
@ -14,19 +14,19 @@
|
|||||||
|
|
||||||
<!-- 3. Instantiate clipboard -->
|
<!-- 3. Instantiate clipboard -->
|
||||||
<script>
|
<script>
|
||||||
var clipboard = new ClipboardJS('.btn', {
|
var clipboard = new ClipboardJS('.btn', {
|
||||||
text: function() {
|
text: function () {
|
||||||
return 'to be or not to be';
|
return 'to be or not to be';
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
clipboard.on('success', function(e) {
|
clipboard.on('success', function (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
clipboard.on('error', function(e) {
|
clipboard.on('error', function (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,29 +1,35 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<title>target-div</title>
|
<title>target-div</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- 1. Define some markup -->
|
<!-- 1. Define some markup -->
|
||||||
<div>hello</div>
|
<div>hello</div>
|
||||||
<button class="btn" data-clipboard-action="copy" data-clipboard-target="div">Copy</button>
|
<button
|
||||||
|
class="btn"
|
||||||
|
data-clipboard-action="copy"
|
||||||
|
data-clipboard-target="div"
|
||||||
|
>
|
||||||
|
Copy
|
||||||
|
</button>
|
||||||
|
|
||||||
<!-- 2. Include library -->
|
<!-- 2. Include library -->
|
||||||
<script src="../dist/clipboard.min.js"></script>
|
<script src="../dist/clipboard.min.js"></script>
|
||||||
|
|
||||||
<!-- 3. Instantiate clipboard -->
|
<!-- 3. Instantiate clipboard -->
|
||||||
<script>
|
<script>
|
||||||
var clipboard = new ClipboardJS('.btn');
|
var clipboard = new ClipboardJS('.btn');
|
||||||
|
|
||||||
clipboard.on('success', function(e) {
|
clipboard.on('success', function (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
clipboard.on('error', function(e) {
|
clipboard.on('error', function (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,29 +1,35 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<title>target-input</title>
|
<title>target-input</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- 1. Define some markup -->
|
<!-- 1. Define some markup -->
|
||||||
<input id="foo" type="text" value="hello">
|
<input id="foo" type="text" value="hello" />
|
||||||
<button class="btn" data-clipboard-action="copy" data-clipboard-target="#foo">Copy</button>
|
<button
|
||||||
|
class="btn"
|
||||||
|
data-clipboard-action="copy"
|
||||||
|
data-clipboard-target="#foo"
|
||||||
|
>
|
||||||
|
Copy
|
||||||
|
</button>
|
||||||
|
|
||||||
<!-- 2. Include library -->
|
<!-- 2. Include library -->
|
||||||
<script src="../dist/clipboard.min.js"></script>
|
<script src="../dist/clipboard.min.js"></script>
|
||||||
|
|
||||||
<!-- 3. Instantiate clipboard -->
|
<!-- 3. Instantiate clipboard -->
|
||||||
<script>
|
<script>
|
||||||
var clipboard = new ClipboardJS('.btn');
|
var clipboard = new ClipboardJS('.btn');
|
||||||
|
|
||||||
clipboard.on('success', function(e) {
|
clipboard.on('success', function (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
clipboard.on('error', function(e) {
|
clipboard.on('error', function (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,29 +1,35 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8">
|
<meta charset="UTF-8" />
|
||||||
<title>target-textarea</title>
|
<title>target-textarea</title>
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<!-- 1. Define some markup -->
|
<!-- 1. Define some markup -->
|
||||||
<textarea id="bar">hello</textarea>
|
<textarea id="bar">hello</textarea>
|
||||||
<button class="btn" data-clipboard-action="cut" data-clipboard-target="#bar">Cut</button>
|
<button
|
||||||
|
class="btn"
|
||||||
|
data-clipboard-action="cut"
|
||||||
|
data-clipboard-target="#bar"
|
||||||
|
>
|
||||||
|
Cut
|
||||||
|
</button>
|
||||||
|
|
||||||
<!-- 2. Include library -->
|
<!-- 2. Include library -->
|
||||||
<script src="../dist/clipboard.min.js"></script>
|
<script src="../dist/clipboard.min.js"></script>
|
||||||
|
|
||||||
<!-- 3. Instantiate clipboard -->
|
<!-- 3. Instantiate clipboard -->
|
||||||
<script>
|
<script>
|
||||||
var clipboard = new ClipboardJS('.btn');
|
var clipboard = new ClipboardJS('.btn');
|
||||||
|
|
||||||
clipboard.on('success', function(e) {
|
clipboard.on('success', function (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
|
|
||||||
clipboard.on('error', function(e) {
|
clipboard.on('error', function (e) {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
@ -1,36 +1,36 @@
|
|||||||
var webpackConfig = require("./webpack.config.js");
|
var webpackConfig = require('./webpack.config.js');
|
||||||
|
|
||||||
module.exports = function (karma) {
|
module.exports = function (karma) {
|
||||||
karma.set({
|
karma.set({
|
||||||
plugins: [
|
plugins: [
|
||||||
"karma-webpack",
|
'karma-webpack',
|
||||||
"karma-chai",
|
'karma-chai',
|
||||||
"karma-sinon",
|
'karma-sinon',
|
||||||
"karma-mocha",
|
'karma-mocha',
|
||||||
"karma-chrome-launcher",
|
'karma-chrome-launcher',
|
||||||
],
|
],
|
||||||
|
|
||||||
frameworks: ["chai", "sinon", "mocha", "webpack"],
|
frameworks: ['chai', 'sinon', 'mocha', 'webpack'],
|
||||||
|
|
||||||
files: [
|
files: [
|
||||||
{ pattern: "src/**/*.js", watched: false },
|
{ pattern: 'src/**/*.js', watched: false },
|
||||||
{ pattern: "test/**/*.js", watched: false },
|
{ pattern: 'test/**/*.js', watched: false },
|
||||||
],
|
],
|
||||||
|
|
||||||
preprocessors: {
|
preprocessors: {
|
||||||
"src/**/*.js": ["webpack"],
|
'src/**/*.js': ['webpack'],
|
||||||
"test/**/*.js": ["webpack"],
|
'test/**/*.js': ['webpack'],
|
||||||
},
|
},
|
||||||
|
|
||||||
webpack: {
|
webpack: {
|
||||||
module: webpackConfig.module,
|
module: webpackConfig.module,
|
||||||
plugins: webpackConfig.plugins,
|
plugins: webpackConfig.plugins,
|
||||||
},
|
},
|
||||||
|
|
||||||
webpackMiddleware: {
|
webpackMiddleware: {
|
||||||
stats: "errors-only",
|
stats: 'errors-only',
|
||||||
},
|
},
|
||||||
|
|
||||||
browsers: ["ChromeHeadless"],
|
browsers: ['ChromeHeadless'],
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
7772
package-lock.json
generated
7772
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
12
package.js
12
package.js
@ -1,12 +1,12 @@
|
|||||||
// Package metadata for Meteor.js.
|
// Package metadata for Meteor.js.
|
||||||
|
|
||||||
Package.describe({
|
Package.describe({
|
||||||
name: "zenorocha:clipboard",
|
name: 'zenorocha:clipboard',
|
||||||
summary: "Modern copy to clipboard. No Flash. Just 3kb.",
|
summary: 'Modern copy to clipboard. No Flash. Just 3kb.',
|
||||||
version: "2.0.6",
|
version: '2.0.6',
|
||||||
git: "https://github.com/zenorocha/clipboard.js"
|
git: 'https://github.com/zenorocha/clipboard.js',
|
||||||
});
|
});
|
||||||
|
|
||||||
Package.onUse(function(api) {
|
Package.onUse(function (api) {
|
||||||
api.addFiles("dist/clipboard.js", "client");
|
api.addFiles('dist/clipboard.js', 'client');
|
||||||
});
|
});
|
||||||
|
11
package.json
11
package.json
@ -22,13 +22,16 @@
|
|||||||
"babel-loader": "^8.2.2",
|
"babel-loader": "^8.2.2",
|
||||||
"chai": "^4.2.0",
|
"chai": "^4.2.0",
|
||||||
"cross-env": "^7.0.3",
|
"cross-env": "^7.0.3",
|
||||||
|
"husky": "^4.3.8",
|
||||||
"karma": "^6.0.0",
|
"karma": "^6.0.0",
|
||||||
"karma-chai": "^0.1.0",
|
"karma-chai": "^0.1.0",
|
||||||
"karma-chrome-launcher": "^3.1.0",
|
"karma-chrome-launcher": "^3.1.0",
|
||||||
"karma-mocha": "^2.0.1",
|
"karma-mocha": "^2.0.1",
|
||||||
"karma-sinon": "^1.0.4",
|
"karma-sinon": "^1.0.4",
|
||||||
"karma-webpack": "^5.0.0-alpha.5",
|
"karma-webpack": "^5.0.0-alpha.5",
|
||||||
|
"lint-staged": "^10.5.3",
|
||||||
"mocha": "^8.2.1",
|
"mocha": "^8.2.1",
|
||||||
|
"prettier": "2.2.1",
|
||||||
"sinon": "^9.2.3",
|
"sinon": "^9.2.3",
|
||||||
"uglifyjs-webpack-plugin": "^2.2.0",
|
"uglifyjs-webpack-plugin": "^2.2.0",
|
||||||
"webpack": "^5.15.0",
|
"webpack": "^5.15.0",
|
||||||
@ -41,5 +44,13 @@
|
|||||||
"build-watch": "webpack --watch",
|
"build-watch": "webpack --watch",
|
||||||
"test": "karma start --single-run",
|
"test": "karma start --single-run",
|
||||||
"prepublish": "npm run build"
|
"prepublish": "npm run build"
|
||||||
|
},
|
||||||
|
"husky": {
|
||||||
|
"hooks": {
|
||||||
|
"pre-commit": "lint-staged"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lint-staged": {
|
||||||
|
"*.{js,css,md}": "prettier --write"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
49
readme.md
49
readme.md
@ -55,11 +55,11 @@ The value you include on this attribute needs to match another's element selecto
|
|||||||
|
|
||||||
```html
|
```html
|
||||||
<!-- Target -->
|
<!-- Target -->
|
||||||
<input id="foo" value="https://github.com/zenorocha/clipboard.js.git">
|
<input id="foo" value="https://github.com/zenorocha/clipboard.js.git" />
|
||||||
|
|
||||||
<!-- Trigger -->
|
<!-- Trigger -->
|
||||||
<button class="btn" data-clipboard-target="#foo">
|
<button class="btn" data-clipboard-target="#foo">
|
||||||
<img src="assets/clippy.svg" alt="Copy to clipboard">
|
<img src="assets/clippy.svg" alt="Copy to clipboard" />
|
||||||
</button>
|
</button>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ If you omit this attribute, `copy` will be used by default.
|
|||||||
|
|
||||||
<!-- Trigger -->
|
<!-- Trigger -->
|
||||||
<button class="btn" data-clipboard-action="cut" data-clipboard-target="#bar">
|
<button class="btn" data-clipboard-action="cut" data-clipboard-target="#bar">
|
||||||
Cut to clipboard
|
Cut to clipboard
|
||||||
</button>
|
</button>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -91,8 +91,11 @@ Truth is, you don't even need another element to copy its content from. You can
|
|||||||
|
|
||||||
```html
|
```html
|
||||||
<!-- Trigger -->
|
<!-- Trigger -->
|
||||||
<button class="btn" data-clipboard-text="Just because you can doesn't mean you should — clipboard.js">
|
<button
|
||||||
Copy to clipboard
|
class="btn"
|
||||||
|
data-clipboard-text="Just because you can doesn't mean you should — clipboard.js"
|
||||||
|
>
|
||||||
|
Copy to clipboard
|
||||||
</button>
|
</button>
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -105,17 +108,17 @@ That's why we fire custom events such as `success` and `error` for you to listen
|
|||||||
```js
|
```js
|
||||||
var clipboard = new ClipboardJS('.btn');
|
var clipboard = new ClipboardJS('.btn');
|
||||||
|
|
||||||
clipboard.on('success', function(e) {
|
clipboard.on('success', function (e) {
|
||||||
console.info('Action:', e.action);
|
console.info('Action:', e.action);
|
||||||
console.info('Text:', e.text);
|
console.info('Text:', e.text);
|
||||||
console.info('Trigger:', e.trigger);
|
console.info('Trigger:', e.trigger);
|
||||||
|
|
||||||
e.clearSelection();
|
e.clearSelection();
|
||||||
});
|
});
|
||||||
|
|
||||||
clipboard.on('error', function(e) {
|
clipboard.on('error', function (e) {
|
||||||
console.error('Action:', e.action);
|
console.error('Action:', e.action);
|
||||||
console.error('Trigger:', e.trigger);
|
console.error('Trigger:', e.trigger);
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -135,9 +138,9 @@ For instance, if you want to dynamically set a `target`, you'll need to return a
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
new ClipboardJS('.btn', {
|
new ClipboardJS('.btn', {
|
||||||
target: function(trigger) {
|
target: function (trigger) {
|
||||||
return trigger.nextElementSibling;
|
return trigger.nextElementSibling;
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -145,9 +148,9 @@ If you want to dynamically set a `text`, you'll return a String.
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
new ClipboardJS('.btn', {
|
new ClipboardJS('.btn', {
|
||||||
text: function(trigger) {
|
text: function (trigger) {
|
||||||
return trigger.getAttribute('aria-label');
|
return trigger.getAttribute('aria-label');
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -155,7 +158,7 @@ For use in Bootstrap Modals or with any other library that changes the focus you
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
new ClipboardJS('.btn', {
|
new ClipboardJS('.btn', {
|
||||||
container: document.getElementById('modal')
|
container: document.getElementById('modal'),
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -171,8 +174,8 @@ clipboard.destroy();
|
|||||||
This library relies on both [Selection](https://developer.mozilla.org/en-US/docs/Web/API/Selection) and [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand) APIs. The first one is [supported by all browsers](https://caniuse.com/#search=selection) while the second one is supported in the following browsers.
|
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"> |
|
| <img src="https://clipboardjs.com/assets/images/chrome.png" width="48px" height="48px" alt="Chrome logo"> | <img src="https://clipboardjs.com/assets/images/edge.png" width="48px" height="48px" alt="Edge logo"> | <img src="https://clipboardjs.com/assets/images/firefox.png" width="48px" height="48px" alt="Firefox logo"> | <img src="https://clipboardjs.com/assets/images/ie.png" width="48px" height="48px" alt="Internet Explorer logo"> | <img src="https://clipboardjs.com/assets/images/opera.png" width="48px" height="48px" alt="Opera logo"> | <img src="https://clipboardjs.com/assets/images/safari.png" width="48px" height="48px" alt="Safari logo"> |
|
||||||
|:---:|:---:|:---:|:---:|:---:|:---:|
|
| :-------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------: |
|
||||||
| 42+ ✔ | 12+ ✔ | 41+ ✔ | 9+ ✔ | 29+ ✔ | 10+ ✔ |
|
| 42+ ✔ | 12+ ✔ | 41+ ✔ | 9+ ✔ | 29+ ✔ | 10+ ✔ |
|
||||||
|
|
||||||
The good news is that clipboard.js gracefully degrades if you need to support older browsers. All you have to do is show a tooltip saying `Copied!` when `success` event is called and `Press Ctrl+C to copy` when `error` event is called because the text is already selected.
|
The good news is that clipboard.js gracefully degrades if you need to support older browsers. All you have to do is show a tooltip saying `Copied!` when `success` event is called and `Press Ctrl+C to copy` when `error` event is called because the text is already selected.
|
||||||
|
|
||||||
@ -180,7 +183,7 @@ You can also check if clipboard.js is supported or not by running `ClipboardJS.i
|
|||||||
|
|
||||||
## Bonus
|
## Bonus
|
||||||
|
|
||||||
A browser extension that adds a "copy to clipboard" button to every code block on *GitHub, MDN, Gist, StackOverflow, StackExchange, npm, and even Medium.*
|
A browser extension that adds a "copy to clipboard" button to every code block on _GitHub, MDN, Gist, StackOverflow, StackExchange, npm, and even Medium._
|
||||||
|
|
||||||
Install for [Chrome](https://chrome.google.com/webstore/detail/codecopy/fkbfebkcoelajmhanocgppanfoojcdmg) and [Firefox](https://addons.mozilla.org/en-US/firefox/addon/codecopy/).
|
Install for [Chrome](https://chrome.google.com/webstore/detail/codecopy/fkbfebkcoelajmhanocgppanfoojcdmg) and [Firefox](https://addons.mozilla.org/en-US/firefox/addon/codecopy/).
|
||||||
|
|
||||||
|
@ -5,200 +5,206 @@ import select from 'select';
|
|||||||
* properties and then executes copy or cut operations.
|
* properties and then executes copy or cut operations.
|
||||||
*/
|
*/
|
||||||
class ClipboardAction {
|
class ClipboardAction {
|
||||||
/**
|
/**
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
*/
|
*/
|
||||||
constructor(options) {
|
constructor(options) {
|
||||||
this.resolveOptions(options);
|
this.resolveOptions(options);
|
||||||
this.initSelection();
|
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,
|
||||||
|
* and makes a selection on it.
|
||||||
|
*/
|
||||||
|
selectFake() {
|
||||||
|
const isRTL = document.documentElement.getAttribute('dir') == 'rtl';
|
||||||
|
|
||||||
|
this.removeFake();
|
||||||
|
|
||||||
|
this.fakeHandlerCallback = () => this.removeFake();
|
||||||
|
this.fakeHandler =
|
||||||
|
this.container.addEventListener('click', this.fakeHandlerCallback) ||
|
||||||
|
true;
|
||||||
|
|
||||||
|
this.fakeElem = document.createElement('textarea');
|
||||||
|
// Prevent zooming on iOS
|
||||||
|
this.fakeElem.style.fontSize = '12pt';
|
||||||
|
// Reset box model
|
||||||
|
this.fakeElem.style.border = '0';
|
||||||
|
this.fakeElem.style.padding = '0';
|
||||||
|
this.fakeElem.style.margin = '0';
|
||||||
|
// Move element out of screen horizontally
|
||||||
|
this.fakeElem.style.position = 'absolute';
|
||||||
|
this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px';
|
||||||
|
// Move element to the same position vertically
|
||||||
|
let yPosition = window.pageYOffset || document.documentElement.scrollTop;
|
||||||
|
this.fakeElem.style.top = `${yPosition}px`;
|
||||||
|
|
||||||
|
this.fakeElem.setAttribute('readonly', '');
|
||||||
|
this.fakeElem.value = this.text;
|
||||||
|
|
||||||
|
this.container.appendChild(this.fakeElem);
|
||||||
|
|
||||||
|
this.selectedText = select(this.fakeElem);
|
||||||
|
this.copyText();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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) {
|
||||||
* Defines base properties passed from constructor.
|
this.container.removeChild(this.fakeElem);
|
||||||
* @param {Object} options
|
this.fakeElem = null;
|
||||||
*/
|
}
|
||||||
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 = '';
|
/**
|
||||||
|
* 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);
|
||||||
* Decides which selection strategy is going to be applied based
|
}
|
||||||
* on the existence of `text` and `target` properties.
|
|
||||||
*/
|
/**
|
||||||
initSelection() {
|
* Fires an event based on the copy operation result.
|
||||||
if (this.text) {
|
* @param {Boolean} succeeded
|
||||||
this.selectFake();
|
*/
|
||||||
}
|
handleResult(succeeded) {
|
||||||
else if (this.target) {
|
this.emitter.emit(succeeded ? 'success' : 'error', {
|
||||||
this.selectTarget();
|
action: this.action,
|
||||||
}
|
text: this.selectedText,
|
||||||
|
trigger: this.trigger,
|
||||||
|
clearSelection: this.clearSelection.bind(this),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Moves focus away from `target` and back to the trigger, removes current selection.
|
||||||
|
*/
|
||||||
|
clearSelection() {
|
||||||
|
if (this.trigger) {
|
||||||
|
this.trigger.focus();
|
||||||
}
|
}
|
||||||
|
document.activeElement.blur();
|
||||||
|
window.getSelection().removeAllRanges();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a fake textarea element, sets its value from `text` property,
|
* Sets the `action` to be performed which can be either 'copy' or 'cut'.
|
||||||
* and makes a selection on it.
|
* @param {String} action
|
||||||
*/
|
*/
|
||||||
selectFake() {
|
set action(action = 'copy') {
|
||||||
const isRTL = document.documentElement.getAttribute('dir') == 'rtl';
|
this._action = action;
|
||||||
|
|
||||||
this.removeFake();
|
if (this._action !== 'copy' && this._action !== 'cut') {
|
||||||
|
throw new Error('Invalid "action" value, use either "copy" or "cut"');
|
||||||
this.fakeHandlerCallback = () => this.removeFake();
|
|
||||||
this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true;
|
|
||||||
|
|
||||||
this.fakeElem = document.createElement('textarea');
|
|
||||||
// Prevent zooming on iOS
|
|
||||||
this.fakeElem.style.fontSize = '12pt';
|
|
||||||
// Reset box model
|
|
||||||
this.fakeElem.style.border = '0';
|
|
||||||
this.fakeElem.style.padding = '0';
|
|
||||||
this.fakeElem.style.margin = '0';
|
|
||||||
// Move element out of screen horizontally
|
|
||||||
this.fakeElem.style.position = 'absolute';
|
|
||||||
this.fakeElem.style[ isRTL ? 'right' : 'left' ] = '-9999px';
|
|
||||||
// Move element to the same position vertically
|
|
||||||
let yPosition = window.pageYOffset || document.documentElement.scrollTop;
|
|
||||||
this.fakeElem.style.top = `${yPosition}px`;
|
|
||||||
|
|
||||||
this.fakeElem.setAttribute('readonly', '');
|
|
||||||
this.fakeElem.value = this.text;
|
|
||||||
|
|
||||||
this.container.appendChild(this.fakeElem);
|
|
||||||
|
|
||||||
this.selectedText = select(this.fakeElem);
|
|
||||||
this.copyText();
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Only removes the fake element after another click event, that way
|
* Gets the `action` property.
|
||||||
* a user can hit `Ctrl+C` to copy because selection still exists.
|
* @return {String}
|
||||||
*/
|
*/
|
||||||
removeFake() {
|
get action() {
|
||||||
if (this.fakeHandler) {
|
return this._action;
|
||||||
this.container.removeEventListener('click', this.fakeHandlerCallback);
|
}
|
||||||
this.fakeHandler = null;
|
|
||||||
this.fakeHandlerCallback = null;
|
/**
|
||||||
|
* Sets the `target` property using an element
|
||||||
|
* that will be have its content copied.
|
||||||
|
* @param {Element} target
|
||||||
|
*/
|
||||||
|
set target(target) {
|
||||||
|
if (target !== undefined) {
|
||||||
|
if (target && typeof target === 'object' && target.nodeType === 1) {
|
||||||
|
if (this.action === 'copy' && target.hasAttribute('disabled')) {
|
||||||
|
throw new Error(
|
||||||
|
'Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.fakeElem) {
|
if (
|
||||||
this.container.removeChild(this.fakeElem);
|
this.action === 'cut' &&
|
||||||
this.fakeElem = null;
|
(target.hasAttribute('readonly') || target.hasAttribute('disabled'))
|
||||||
}
|
) {
|
||||||
}
|
throw new Error(
|
||||||
|
'Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes'
|
||||||
/**
|
);
|
||||||
* 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);
|
this._target = target;
|
||||||
|
} else {
|
||||||
|
throw new Error('Invalid "target" value, use a valid Element');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Fires an event based on the copy operation result.
|
* Gets the `target` property.
|
||||||
* @param {Boolean} succeeded
|
* @return {String|HTMLElement}
|
||||||
*/
|
*/
|
||||||
handleResult(succeeded) {
|
get target() {
|
||||||
this.emitter.emit(succeeded ? 'success' : 'error', {
|
return this._target;
|
||||||
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.
|
* Destroy lifecycle.
|
||||||
*/
|
*/
|
||||||
clearSelection() {
|
destroy() {
|
||||||
if (this.trigger) {
|
this.removeFake();
|
||||||
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;
|
export default ClipboardAction;
|
||||||
|
221
src/clipboard.js
221
src/clipboard.js
@ -7,129 +7,136 @@ import listen from 'good-listener';
|
|||||||
* and instantiates a new `ClipboardAction` on each click.
|
* and instantiates a new `ClipboardAction` on each click.
|
||||||
*/
|
*/
|
||||||
class Clipboard extends Emitter {
|
class Clipboard extends Emitter {
|
||||||
/**
|
/**
|
||||||
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
|
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
*/
|
*/
|
||||||
constructor(trigger, options) {
|
constructor(trigger, options) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.resolveOptions(options);
|
this.resolveOptions(options);
|
||||||
this.listenClick(trigger);
|
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({
|
||||||
* Defines if attributes would be resolved using internal setter functions
|
action: this.action(trigger),
|
||||||
* or custom functions that were passed in the constructor.
|
target: this.target(trigger),
|
||||||
* @param {Object} options
|
text: this.text(trigger),
|
||||||
*/
|
container: this.container,
|
||||||
resolveOptions(options = {}) {
|
trigger: trigger,
|
||||||
this.action = (typeof options.action === 'function') ? options.action : this.defaultAction;
|
emitter: this,
|
||||||
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;
|
|
||||||
|
/**
|
||||||
|
* 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);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Adds a click event listener to the passed trigger.
|
* Returns the support of the given action, or all actions if no action is
|
||||||
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
|
* given.
|
||||||
*/
|
* @param {String} [action]
|
||||||
listenClick(trigger) {
|
*/
|
||||||
this.listener = listen(trigger, 'click', (e) => this.onClick(e));
|
static isSupported(action = ['copy', 'cut']) {
|
||||||
}
|
const actions = typeof action === 'string' ? [action] : action;
|
||||||
|
let support = !!document.queryCommandSupported;
|
||||||
/**
|
|
||||||
* Defines a new `ClipboardAction` on each click event.
|
actions.forEach((action) => {
|
||||||
* @param {Event} e
|
support = support && !!document.queryCommandSupported(action);
|
||||||
*/
|
});
|
||||||
onClick(e) {
|
|
||||||
const trigger = e.delegateTarget || e.currentTarget;
|
return support;
|
||||||
|
}
|
||||||
if (this.clipboardAction) {
|
|
||||||
this.clipboardAction = null;
|
/**
|
||||||
}
|
* Default `text` lookup function.
|
||||||
|
* @param {Element} trigger
|
||||||
this.clipboardAction = new ClipboardAction({
|
*/
|
||||||
action : this.action(trigger),
|
defaultText(trigger) {
|
||||||
target : this.target(trigger),
|
return getAttributeValue('text', trigger);
|
||||||
text : this.text(trigger),
|
}
|
||||||
container : this.container,
|
|
||||||
trigger : trigger,
|
/**
|
||||||
emitter : this
|
* Destroy lifecycle.
|
||||||
});
|
*/
|
||||||
}
|
destroy() {
|
||||||
|
this.listener.destroy();
|
||||||
/**
|
|
||||||
* Default `action` lookup function.
|
if (this.clipboardAction) {
|
||||||
* @param {Element} trigger
|
this.clipboardAction.destroy();
|
||||||
*/
|
this.clipboardAction = null;
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper function to retrieve attribute value.
|
* Helper function to retrieve attribute value.
|
||||||
* @param {String} suffix
|
* @param {String} suffix
|
||||||
* @param {Element} element
|
* @param {Element} element
|
||||||
*/
|
*/
|
||||||
function getAttributeValue(suffix, element) {
|
function getAttributeValue(suffix, element) {
|
||||||
const attribute = `data-clipboard-${suffix}`;
|
const attribute = `data-clipboard-${suffix}`;
|
||||||
|
|
||||||
if (!element.hasAttribute(attribute)) {
|
if (!element.hasAttribute(attribute)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
return element.getAttribute(attribute);
|
return element.getAttribute(attribute);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Clipboard;
|
export default Clipboard;
|
||||||
|
@ -2,242 +2,243 @@ import ClipboardAction from '../src/clipboard-action';
|
|||||||
import Emitter from 'tiny-emitter';
|
import Emitter from 'tiny-emitter';
|
||||||
|
|
||||||
describe('ClipboardAction', () => {
|
describe('ClipboardAction', () => {
|
||||||
before(() => {
|
before(() => {
|
||||||
global.input = document.createElement('input');
|
global.input = document.createElement('input');
|
||||||
global.input.setAttribute('id', 'input');
|
global.input.setAttribute('id', 'input');
|
||||||
global.input.setAttribute('value', 'abc');
|
global.input.setAttribute('value', 'abc');
|
||||||
document.body.appendChild(global.input);
|
document.body.appendChild(global.input);
|
||||||
|
|
||||||
global.paragraph = document.createElement('p');
|
global.paragraph = document.createElement('p');
|
||||||
global.paragraph.setAttribute('id', 'paragraph');
|
global.paragraph.setAttribute('id', 'paragraph');
|
||||||
global.paragraph.textContent = 'abc';
|
global.paragraph.textContent = 'abc';
|
||||||
document.body.appendChild(global.paragraph);
|
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',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(clip.fakeElem.style.right, '-9999px');
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#set action', () => {
|
||||||
|
it('should throw an error since "action" is invalid', (done) => {
|
||||||
|
try {
|
||||||
|
new ClipboardAction({
|
||||||
|
text: 'foo',
|
||||||
|
action: 'paste',
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
assert.equal(
|
||||||
|
e.message,
|
||||||
|
'Invalid "action" value, use either "copy" or "cut"'
|
||||||
|
);
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#set target', () => {
|
||||||
|
it('should throw an error since "target" do not match any element', (done) => {
|
||||||
|
try {
|
||||||
|
new ClipboardAction({
|
||||||
|
target: document.querySelector('#foo'),
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
assert.equal(e.message, 'Invalid "target" value, use a valid Element');
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#selectText', () => {
|
||||||
|
it('should create a fake element and select its value', () => {
|
||||||
|
let clip = new ClipboardAction({
|
||||||
|
emitter: new Emitter(),
|
||||||
|
container: document.body,
|
||||||
|
text: 'blah',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(clip.selectedText, clip.fakeElem.value);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#removeFake', () => {
|
||||||
|
it('should remove a temporary fake element', () => {
|
||||||
|
let clip = new ClipboardAction({
|
||||||
|
emitter: new Emitter(),
|
||||||
|
container: document.body,
|
||||||
|
text: 'blah',
|
||||||
|
});
|
||||||
|
|
||||||
|
clip.removeFake();
|
||||||
|
|
||||||
|
assert.equal(clip.fakeElem, null);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#selectTarget', () => {
|
||||||
|
it('should select text from editable element', () => {
|
||||||
|
let clip = new ClipboardAction({
|
||||||
|
emitter: new Emitter(),
|
||||||
|
container: document.body,
|
||||||
|
target: document.querySelector('#input'),
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(clip.selectedText, clip.target.value);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should select text from non-editable element', () => {
|
||||||
|
let clip = new ClipboardAction({
|
||||||
|
emitter: new Emitter(),
|
||||||
|
container: document.body,
|
||||||
|
target: document.querySelector('#paragraph'),
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(clip.selectedText, clip.target.textContent);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#copyText', () => {
|
||||||
|
before(() => {
|
||||||
|
global.stub = sinon.stub(document, 'execCommand');
|
||||||
});
|
});
|
||||||
|
|
||||||
after(() => {
|
after(() => {
|
||||||
document.body.innerHTML = '';
|
global.stub.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#resolveOptions', () => {
|
it('should fire a success event on browsers that support copy command', (done) => {
|
||||||
it('should set base properties', () => {
|
global.stub.returns(true);
|
||||||
let clip = new ClipboardAction({
|
|
||||||
emitter: new Emitter(),
|
|
||||||
container: document.body,
|
|
||||||
text: 'foo'
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.property(clip, 'action');
|
let emitter = new Emitter();
|
||||||
assert.property(clip, 'container');
|
|
||||||
assert.property(clip, 'emitter');
|
emitter.on('success', () => {
|
||||||
assert.property(clip, 'target');
|
done();
|
||||||
assert.property(clip, 'text');
|
});
|
||||||
assert.property(clip, 'trigger');
|
|
||||||
assert.property(clip, 'selectedText');
|
let clip = new ClipboardAction({
|
||||||
});
|
emitter,
|
||||||
|
target: document.querySelector('#input'),
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#initSelection', () => {
|
it('should fire an error event on browsers that support copy command', (done) => {
|
||||||
it('should set the position right style property', done => {
|
global.stub.returns(false);
|
||||||
// Set document direction
|
|
||||||
document.documentElement.setAttribute('dir', 'rtl');
|
|
||||||
|
|
||||||
let clip = new ClipboardAction({
|
let emitter = new Emitter();
|
||||||
emitter: new Emitter(),
|
|
||||||
container: document.body,
|
|
||||||
text: 'foo'
|
|
||||||
});
|
|
||||||
|
|
||||||
assert.equal(clip.fakeElem.style.right, '-9999px');
|
emitter.on('error', () => {
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let clip = new ClipboardAction({
|
||||||
|
emitter,
|
||||||
|
target: document.querySelector('#input'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#handleResult', () => {
|
||||||
|
it('should fire a success event with certain properties', (done) => {
|
||||||
|
let clip = new ClipboardAction({
|
||||||
|
emitter: new Emitter(),
|
||||||
|
container: document.body,
|
||||||
|
target: document.querySelector('#input'),
|
||||||
|
});
|
||||||
|
|
||||||
|
clip.emitter.on('success', (e) => {
|
||||||
|
assert.property(e, 'action');
|
||||||
|
assert.property(e, 'text');
|
||||||
|
assert.property(e, 'trigger');
|
||||||
|
assert.property(e, 'clearSelection');
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
clip.handleResult(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#set action', () => {
|
it('should fire a error event with certain properties', (done) => {
|
||||||
it('should throw an error since "action" is invalid', done => {
|
let clip = new ClipboardAction({
|
||||||
try {
|
emitter: new Emitter(),
|
||||||
new ClipboardAction({
|
container: document.body,
|
||||||
text: 'foo',
|
target: document.querySelector('#input'),
|
||||||
action: 'paste'
|
});
|
||||||
});
|
|
||||||
}
|
clip.emitter.on('error', (e) => {
|
||||||
catch(e) {
|
assert.property(e, 'action');
|
||||||
assert.equal(e.message, 'Invalid "action" value, use either "copy" or "cut"');
|
assert.property(e, 'trigger');
|
||||||
done();
|
assert.property(e, 'clearSelection');
|
||||||
}
|
|
||||||
});
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
clip.handleResult(false);
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('#set target', () => {
|
describe('#clearSelection', () => {
|
||||||
it('should throw an error since "target" do not match any element', done => {
|
it('should remove focus from target and text selection', () => {
|
||||||
try {
|
let clip = new ClipboardAction({
|
||||||
new ClipboardAction({
|
emitter: new Emitter(),
|
||||||
target: document.querySelector('#foo')
|
container: document.body,
|
||||||
});
|
target: document.querySelector('#input'),
|
||||||
}
|
});
|
||||||
catch(e) {
|
|
||||||
assert.equal(e.message, 'Invalid "target" value, use a valid Element');
|
clip.clearSelection();
|
||||||
done();
|
|
||||||
}
|
let selectedElem = document.activeElement;
|
||||||
});
|
let selectedText = window.getSelection().toString();
|
||||||
|
|
||||||
|
assert.equal(selectedElem, document.body);
|
||||||
|
assert.equal(selectedText, '');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('#selectText', () => {
|
describe('#destroy', () => {
|
||||||
it('should create a fake element and select its value', () => {
|
it('should destroy an existing fake element', () => {
|
||||||
let clip = new ClipboardAction({
|
let clip = new ClipboardAction({
|
||||||
emitter: new Emitter(),
|
emitter: new Emitter(),
|
||||||
container: document.body,
|
container: document.body,
|
||||||
text: 'blah'
|
text: 'blah',
|
||||||
});
|
});
|
||||||
|
|
||||||
assert.equal(clip.selectedText, clip.fakeElem.value);
|
clip.selectFake();
|
||||||
});
|
clip.destroy();
|
||||||
});
|
|
||||||
|
assert.equal(clip.fakeElem, null);
|
||||||
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);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -1,136 +1,133 @@
|
|||||||
import Clipboard from "../src/clipboard";
|
import Clipboard from '../src/clipboard';
|
||||||
import ClipboardAction from "../src/clipboard-action";
|
import ClipboardAction from '../src/clipboard-action';
|
||||||
|
|
||||||
describe("Clipboard", () => {
|
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(() => {
|
before(() => {
|
||||||
global.button = document.createElement("button");
|
global.fn = () => {};
|
||||||
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(() => {
|
it('should set action as a function', () => {
|
||||||
document.body.innerHTML = "";
|
let clipboard = new Clipboard('.btn', {
|
||||||
|
action: global.fn,
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(global.fn, clipboard.action);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#resolveOptions", () => {
|
it('should set target as a function', () => {
|
||||||
before(() => {
|
let clipboard = new Clipboard('.btn', {
|
||||||
global.fn = () => {};
|
target: global.fn,
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should set action as a function", () => {
|
assert.equal(global.fn, clipboard.target);
|
||||||
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 set text as a function', () => {
|
||||||
it("should add a click event listener to the passed selector", () => {
|
let clipboard = new Clipboard('.btn', {
|
||||||
let clipboard = new Clipboard(".btn");
|
text: global.fn,
|
||||||
assert.isObject(clipboard.listener);
|
});
|
||||||
});
|
|
||||||
|
assert.equal(global.fn, clipboard.text);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#onClick", () => {
|
it('should set container as an object', () => {
|
||||||
it("should create a new instance of ClipboardAction", () => {
|
let clipboard = new Clipboard('.btn', {
|
||||||
let clipboard = new Clipboard(".btn");
|
container: document.body,
|
||||||
|
});
|
||||||
|
|
||||||
clipboard.onClick(global.event);
|
assert.equal(document.body, clipboard.container);
|
||||||
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 set container as body by default', () => {
|
||||||
it("should return the support of the given action", () => {
|
let clipboard = new Clipboard('.btn');
|
||||||
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(document.body, clipboard.container);
|
||||||
assert.equal(Clipboard.isSupported(), true);
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('#listenClick', () => {
|
||||||
|
it('should add a click event listener to the passed selector', () => {
|
||||||
|
let clipboard = new Clipboard('.btn');
|
||||||
|
assert.isObject(clipboard.listener);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('#onClick', () => {
|
||||||
|
it('should create a new instance of ClipboardAction', () => {
|
||||||
|
let clipboard = new Clipboard('.btn');
|
||||||
|
|
||||||
|
clipboard.onClick(global.event);
|
||||||
|
assert.instanceOf(clipboard.clipboardAction, ClipboardAction);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("#destroy", () => {
|
it("should use an event's currentTarget when not equal to target", () => {
|
||||||
it("should destroy an existing instance of ClipboardAction", () => {
|
let clipboard = new Clipboard('.btn');
|
||||||
let clipboard = new Clipboard(".btn");
|
let bubbledEvent = {
|
||||||
|
target: global.span,
|
||||||
|
currentTarget: global.button,
|
||||||
|
};
|
||||||
|
|
||||||
clipboard.onClick(global.event);
|
clipboard.onClick(bubbledEvent);
|
||||||
clipboard.destroy();
|
assert.instanceOf(clipboard.clipboardAction, ClipboardAction);
|
||||||
|
|
||||||
assert.equal(clipboard.clipboardAction, null);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -11,36 +11,35 @@ https://clipboardjs.com/
|
|||||||
Licensed MIT © Zeno Rocha`;
|
Licensed MIT © Zeno Rocha`;
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
entry: './src/clipboard.js',
|
entry: './src/clipboard.js',
|
||||||
mode: 'production',
|
mode: 'production',
|
||||||
output: {
|
output: {
|
||||||
filename: production ? 'clipboard.min.js' : 'clipboard.js',
|
filename: production ? 'clipboard.min.js' : 'clipboard.js',
|
||||||
path: path.resolve(__dirname, 'dist'),
|
path: path.resolve(__dirname, 'dist'),
|
||||||
library: 'ClipboardJS',
|
library: 'ClipboardJS',
|
||||||
globalObject: 'this',
|
globalObject: 'this',
|
||||||
libraryExport: 'default',
|
libraryExport: 'default',
|
||||||
libraryTarget: 'umd'
|
libraryTarget: 'umd',
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
rules: [
|
rules: [{ test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader' }],
|
||||||
{test: /\.js$/, exclude: /node_modules/, loader: 'babel-loader'}
|
},
|
||||||
]
|
optimization: {
|
||||||
},
|
minimize: production,
|
||||||
optimization: {
|
minimizer: [
|
||||||
minimize: production,
|
new UglifyJSPlugin({
|
||||||
minimizer: [
|
parallel: require('os').cpus().length,
|
||||||
new UglifyJSPlugin({
|
uglifyOptions: {
|
||||||
parallel: require('os').cpus().length,
|
ie8: false,
|
||||||
uglifyOptions: {
|
keep_fnames: false,
|
||||||
ie8: false,
|
output: {
|
||||||
keep_fnames: false,
|
beautify: false,
|
||||||
output: {
|
comments: (node, { value, type }) =>
|
||||||
beautify: false,
|
type == 'comment2' && value.startsWith('!'),
|
||||||
comments: (node, {value, type}) => type == 'comment2' && value.startsWith('!')
|
},
|
||||||
}
|
},
|
||||||
}
|
}),
|
||||||
})
|
],
|
||||||
]
|
},
|
||||||
},
|
plugins: [new webpack.BannerPlugin({ banner })],
|
||||||
plugins: [new webpack.BannerPlugin({ banner })]
|
|
||||||
};
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user