Compare commits

..

1 Commits

Author SHA1 Message Date
Zeno Rocha
0a0de9fc01 Release v1.5.3 2015-10-28 13:06:53 -07:00
20 changed files with 499 additions and 603 deletions

View File

@@ -1,4 +0,0 @@
{
"presets": ["es2015-loose"],
"plugins": ["transform-es2015-modules-umd"]
}

View File

@@ -1,15 +0,0 @@
### Minimal example
> Fork this [JSFiddle](https://jsfiddle.net/zenorocha/5kk0eysw/) and reproduce your issue.
### Expected behaviour
I thought that by going to the page '...' and pressing the button '...' then '...' would happen.
### Actual behaviour
Instead of '...', what I saw was that '...' happened instead.
### Browsers affected
I tested on all major browsers and only IE 11 does not work.

View File

@@ -1,6 +1,6 @@
{
"name": "clipboard",
"version": "1.5.13",
"version": "1.5.3",
"description": "Modern copy to clipboard. No Flash. Just 2kb",
"license": "MIT",
"main": "dist/clipboard.js",

View File

@@ -3,13 +3,10 @@
<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>
<button id="btn" data-clipboard-text="1">Copy</button>
<!-- 2. Include library -->
<script src="../dist/clipboard.min.js"></script>

View File

@@ -3,7 +3,6 @@
<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 -->

View File

@@ -3,7 +3,6 @@
<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 -->

View File

@@ -3,7 +3,6 @@
<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 -->

View File

@@ -3,7 +3,6 @@
<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 -->

View File

@@ -3,7 +3,6 @@
<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 -->

View File

@@ -3,7 +3,6 @@
<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 -->

View File

@@ -3,7 +3,6 @@
<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 -->

876
dist/clipboard.js vendored

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@@ -3,10 +3,10 @@
Package.describe({
name: "zenorocha:clipboard",
summary: "Modern copy to clipboard. No Flash. Just 2kb.",
version: "1.5.13",
version: "1.5.1",
git: "https://github.com/zenorocha/clipboard.js"
});
Package.onUse(function(api) {
api.addFiles("dist/clipboard.js", "client");
api.addFiles("dist/clipboard.min.js", "client");
});

View File

@@ -1,6 +1,6 @@
{
"name": "clipboard",
"version": "1.5.13",
"version": "1.5.3",
"description": "Modern copy to clipboard. No Flash. Just 2kb",
"repository": "zenorocha/clipboard.js",
"license": "MIT",
@@ -11,38 +11,31 @@
"cut"
],
"dependencies": {
"good-listener": "^1.1.6",
"select": "^1.0.6",
"good-listener": "^1.1.2",
"select": "^1.0.4",
"tiny-emitter": "^1.0.0"
},
"devDependencies": {
"babel-cli": "^6.5.1",
"babel-core": "^6.5.2",
"babel-plugin-transform-es2015-modules-umd": "^6.5.0",
"babel-preset-es2015": "^6.5.0",
"babel-preset-es2015-loose": "^7.0.0",
"babelify": "^7.2.0",
"bannerify": "Vekat/bannerify#feature-option",
"browserify": "^13.0.0",
"chai": "^3.4.1",
"install": "^0.4.4",
"babel": "^5.8.29",
"babelify": "^6.3.0",
"browserify": "^11.2.0",
"karma": "^0.13.10",
"karma-browserify": "^5.0.1",
"karma-browserify": "^4.4.0",
"karma-chai": "^0.1.0",
"karma-mocha": "^0.2.0",
"karma-phantomjs-launcher": "^1.0.0",
"karma-phantomjs-launcher": "^0.2.1",
"karma-sinon": "^1.0.4",
"mocha": "^2.3.3",
"phantomjs-prebuilt": "^2.1.4",
"sinon": "^1.17.2",
"phantomjs-polyfill": "0.0.1",
"uglify-js": "^2.4.24",
"watchify": "^3.4.0"
"watchify": "^3.4.0",
"bannerify": "Vekat/bannerify#feature-option"
},
"scripts": {
"build": "npm run build-debug && npm run build-min",
"build-debug": "browserify src/clipboard.js -s Clipboard -t [babelify] -p [bannerify --file .banner ] -o dist/clipboard.js",
"build-debug": "browserify src/clipboard.js -s Clipboard -t [babelify --loose all] -p [bannerify --file .banner ] -o dist/clipboard.js",
"build-min": "uglifyjs dist/clipboard.js --comments '/!/' -m screw_ie8=true -c screw_ie8=true,unused=false -o dist/clipboard.min.js",
"build-watch": "watchify src/clipboard.js -s Clipboard -t [babelify] -o dist/clipboard.js -v",
"build-watch": "watchify src/clipboard.js -s Clipboard -t [babelify --loose all] -o dist/clipboard.js -v",
"test": "karma start --single-run",
"prepublish": "babel src --out-dir lib --loose all"
}

View File

@@ -3,9 +3,9 @@
[![Build Status](http://img.shields.io/travis/zenorocha/clipboard.js/master.svg?style=flat)](https://travis-ci.org/zenorocha/clipboard.js)
![Killing Flash](https://img.shields.io/badge/killing-flash-brightgreen.svg?style=flat)
> Modern copy to clipboard. No Flash. Just 3kb gzipped.
> Modern copy to clipboard. No Flash. Just 2kb
<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>
<a href="http://clipboardjs.com/"><img width="728" src="https://cloud.githubusercontent.com/assets/398893/9983535/5ab0a950-5fb4-11e5-9602-e73c0b661883.jpg" alt="Demo"></a>
## Why
@@ -57,7 +57,7 @@ A pretty common use case is to copy content from another element. You can do tha
The value you include on this attribute needs to match another's element selector.
<a href="https://clipboardjs.com/#example-target"><img width="473" alt="example-2" src="https://cloud.githubusercontent.com/assets/398893/9983467/a4946aaa-5fb1-11e5-9780-f09fcd7ca6c8.png"></a>
<a href="http://clipboardjs.com/#example-target"><img width="473" alt="example-2" src="https://cloud.githubusercontent.com/assets/398893/9983467/a4946aaa-5fb1-11e5-9780-f09fcd7ca6c8.png"></a>
```html
<!-- Target -->
@@ -75,7 +75,7 @@ Additionally, you can define a `data-clipboard-action` attribute to specify if y
If you omit this attribute, `copy` will be used by default.
<a href="https://clipboardjs.com/#example-action"><img width="473" alt="example-3" src="https://cloud.githubusercontent.com/assets/398893/10000358/7df57b9c-6050-11e5-9cd1-fbc51d2fd0a7.png"></a>
<a href="http://clipboardjs.com/#example-action"><img width="473" alt="example-3" src="https://cloud.githubusercontent.com/assets/398893/10000358/7df57b9c-6050-11e5-9cd1-fbc51d2fd0a7.png"></a>
```html
<!-- Target -->
@@ -93,7 +93,7 @@ As you may expect, the `cut` action only works on `<input>` or `<textarea>` elem
Truth is, you don't even need another element to copy its content from. You can just include a `data-clipboard-text` attribute in your trigger element.
<a href="https://clipboardjs.com/#example-text"><img width="147" alt="example-1" src="https://cloud.githubusercontent.com/assets/398893/10000347/6e16cf8c-6050-11e5-9883-1c5681f9ec45.png"></a>
<a href="http://clipboardjs.com/#example-text"><img width="147" alt="example-1" src="https://cloud.githubusercontent.com/assets/398893/10000347/6e16cf8c-6050-11e5-9883-1c5681f9ec45.png"></a>
```html
<!-- Trigger -->
@@ -125,7 +125,7 @@ clipboard.on('error', function(e) {
});
```
For a live demonstration, open this [site](https://clipboardjs.com/) and just your console :)
For a live demonstration, open this [site](http://clipboardjs.com/) and just your console :)
## Advanced Options
@@ -151,7 +151,7 @@ new Clipboard('.btn', {
});
```
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.
Also, with are working with single page apps, you may want to manage the lifecycle of the DOM more precisely. Here's how you clean up the events and objects that we create.
```js
var clipboard = new Clipboard('.btn');
@@ -160,13 +160,17 @@ clipboard.destroy();
## Browser Support
This library relies on both [Selection](https://developer.mozilla.org/en-US/docs/Web/API/Selection) and [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand) APIs. The first one is [supported by all browsers](http://caniuse.com/#search=selection) while the second one is supported in the following browsers.
This library relies on both [Selection](https://developer.mozilla.org/en-US/docs/Web/API/Selection) and [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand) APIs. The 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+ ✔ |
| <img src="http://clipboardjs.com/assets/images/chrome.png" width="48px" height="48px" alt="Chrome logo"> | <img src="http://clipboardjs.com/assets/images/firefox.png" width="48px" height="48px" alt="Firefox logo"> | <img src="http://clipboardjs.com/assets/images/ie.png" width="48px" height="48px" alt="Internet Explorer logo"> | <img src="http://clipboardjs.com/assets/images/opera.png" width="48px" height="48px" alt="Opera logo"> | <img src="http://clipboardjs.com/assets/images/safari.png" width="48px" height="48px" alt="Safari logo"> |
|:---:|:---:|:---:|:---:|:---:|
| 42+ ✔ | 41+ ✔ | 9+ ✔ | 29+ ✔ | Nope ✘ |
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.
Although copy/cut operations with [execCommand](https://developer.mozilla.org/en-US/docs/Web/API/Document/execCommand) aren't supported on Safari yet (including mobile), it gracefully degrades because [Selection](https://developer.mozilla.org/en-US/docs/Web/API/Selection) is supported.
That means you can show a tooltip saying `Copied!` when `success` event is called and `Press Ctrl+C to copy` when `error` event is called because the text is already selected.
For a live demonstration, open this [site](http://clipboardjs.com) on Safari.
## License

View File

@@ -32,12 +32,18 @@ class ClipboardAction {
* on the existence of `text` and `target` properties.
*/
initSelection() {
if (this.text) {
if (this.text && this.target) {
throw new Error('Multiple attributes declared, use either "target" or "text"');
}
else if (this.text) {
this.selectFake();
}
else if (this.target) {
this.selectTarget();
}
else {
throw new Error('Missing required attributes, use either "target" or "text"');
}
}
/**
@@ -45,28 +51,14 @@ class ClipboardAction {
* and makes a selection on it.
*/
selectFake() {
const isRTL = document.documentElement.getAttribute('dir') == 'rtl';
this.removeFake();
this.fakeHandlerCallback = () => this.removeFake();
this.fakeHandler = document.body.addEventListener('click', this.fakeHandlerCallback) || true;
this.fakeHandler = document.body.addEventListener('click', () => this.removeFake());
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.addEventListener('focus', window.scrollTo(0, yPosition));
this.fakeElem.style.top = yPosition + 'px';
this.fakeElem.style.left = '-9999px';
this.fakeElem.style.top = (window.pageYOffset || document.documentElement.scrollTop) + 'px';
this.fakeElem.setAttribute('readonly', '');
this.fakeElem.value = this.text;
@@ -82,9 +74,8 @@ class ClipboardAction {
*/
removeFake() {
if (this.fakeHandler) {
document.body.removeEventListener('click', this.fakeHandlerCallback);
document.body.removeEventListener('click');
this.fakeHandler = null;
this.fakeHandlerCallback = null;
}
if (this.fakeElem) {
@@ -122,12 +113,21 @@ class ClipboardAction {
* @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)
});
if (succeeded) {
this.emitter.emit('success', {
action: this.action,
text: this.selectedText,
trigger: this.trigger,
clearSelection: this.clearSelection.bind(this)
});
}
else {
this.emitter.emit('error', {
action: this.action,
trigger: this.trigger,
clearSelection: this.clearSelection.bind(this)
});
}
}
/**
@@ -169,14 +169,6 @@ class ClipboardAction {
set target(target) {
if (target !== undefined) {
if (target && typeof target === 'object' && target.nodeType === 1) {
if (this.action === 'copy' && target.hasAttribute('disabled')) {
throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');
}
if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {
throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');
}
this._target = target;
}
else {
@@ -201,4 +193,4 @@ class ClipboardAction {
}
}
module.exports = ClipboardAction;
export default ClipboardAction;

View File

@@ -42,17 +42,15 @@ class Clipboard extends Emitter {
* @param {Event} e
*/
onClick(e) {
const trigger = e.delegateTarget || e.currentTarget;
if (this.clipboardAction) {
this.clipboardAction = null;
}
this.clipboardAction = new ClipboardAction({
action : this.action(trigger),
target : this.target(trigger),
text : this.text(trigger),
trigger : trigger,
action : this.action(e.target),
target : this.target(e.target),
text : this.text(e.target),
trigger : e.target,
emitter : this
});
}
@@ -70,7 +68,7 @@ class Clipboard extends Emitter {
* @param {Element} trigger
*/
defaultTarget(trigger) {
const selector = getAttributeValue('target', trigger);
let selector = getAttributeValue('target', trigger);
if (selector) {
return document.querySelector(selector);
@@ -105,7 +103,7 @@ class Clipboard extends Emitter {
* @param {Element} element
*/
function getAttributeValue(suffix, element) {
const attribute = `data-clipboard-${suffix}`;
let attribute = `data-clipboard-${suffix}`;
if (!element.hasAttribute(attribute)) {
return;
@@ -114,4 +112,4 @@ function getAttributeValue(suffix, element) {
return element.getAttribute(attribute);
}
module.exports = Clipboard;
export default Clipboard;

View File

@@ -35,17 +35,27 @@ describe('ClipboardAction', () => {
});
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(),
text: 'foo'
it('should throw an error since both "text" and "target" were passed', done => {
try {
new ClipboardAction({
text: 'foo',
target: document.querySelector('#input')
});
}
catch(e) {
assert.equal(e.message, 'Multiple attributes declared, use either "target" or "text"');
done();
}
});
assert.equal(clip.fakeElem.style.right, '-9999px');
done();
it('should throw an error since neither "text" nor "target" were passed', done => {
try {
new ClipboardAction();
}
catch(e) {
assert.equal(e.message, 'Missing required attributes, use either "target" or "text"');
done();
}
});
});

View File

@@ -9,14 +9,8 @@ describe('Clipboard', () => {
global.button.setAttribute('data-clipboard-text', 'foo');
document.body.appendChild(global.button);
global.span = document.createElement('span');
global.span.innerHTML = 'bar';
global.button.appendChild(span);
global.event = {
target: global.button,
currentTarget: global.button
target: global.button
};
});
@@ -69,15 +63,7 @@ describe('Clipboard', () => {
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 => {
it('should throws exception target', done => {
try {
var clipboard = new Clipboard('.btn', {
target: function() {