Merge pull request #1 from zenorocha/master

Pull from zenorocha/clipboard.js
This commit is contained in:
Olaolu Olawuyi 2016-02-21 12:08:32 +00:00
commit 4ed19cc571
12 changed files with 163 additions and 91 deletions

19
.github/issue_template.md vendored Normal file
View File

@ -0,0 +1,19 @@
### 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.
### Steps to reproduce
1. Go this JSFiddle, JSBin, CodePen, etc
2. Click on '....'
3. Scroll down to '....'
4. Refresh the page and wait 5 secs
5. Finally the error will magically happen (if it is raining)
### 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.2",
"version": "1.5.8",
"description": "Modern copy to clipboard. No Flash. Just 2kb",
"license": "MIT",
"main": "dist/clipboard.js",

View File

@ -6,7 +6,9 @@
</head>
<body>
<!-- 1. Define some markup -->
<button id="btn" data-clipboard-text="1">Copy</button>
<div id="btn" data-clipboard-text="1">
<span>Copy</span>
</div>
<!-- 2. Include library -->
<script src="../dist/clipboard.min.js"></script>

145
dist/clipboard.js vendored
View File

@ -1,5 +1,5 @@
/*!
* clipboard.js v1.5.2
* clipboard.js v1.5.8
* https://zenorocha.github.io/clipboard.js
*
* Licensed MIT © Zeno Rocha
@ -16,48 +16,7 @@ module.exports = function (element, selector, checkYoSelf) {
}
}
},{"matches-selector":2}],2:[function(require,module,exports){
/**
* Element prototype.
*/
var proto = Element.prototype;
/**
* Vendor function.
*/
var vendor = proto.matchesSelector
|| proto.webkitMatchesSelector
|| proto.mozMatchesSelector
|| proto.msMatchesSelector
|| proto.oMatchesSelector;
/**
* Expose `match()`.
*/
module.exports = match;
/**
* Match `el` to `selector`.
*
* @param {Element} el
* @param {String} selector
* @return {Boolean}
* @api public
*/
function match(el, selector) {
if (vendor) return vendor.call(el, selector);
var nodes = el.parentNode.querySelectorAll(selector);
for (var i = 0; i < nodes.length; ++i) {
if (nodes[i] == el) return true;
}
return false;
}
},{}],3:[function(require,module,exports){
},{"matches-selector":5}],2:[function(require,module,exports){
var closest = require('closest');
/**
@ -67,16 +26,17 @@ var closest = require('closest');
* @param {String} selector
* @param {String} type
* @param {Function} callback
* @param {Boolean} useCapture
* @return {Object}
*/
function delegate(element, selector, type, callback) {
function delegate(element, selector, type, callback, useCapture) {
var listenerFn = listener.apply(this, arguments);
element.addEventListener(type, listenerFn);
element.addEventListener(type, listenerFn, useCapture);
return {
destroy: function() {
element.removeEventListener(type, listenerFn);
element.removeEventListener(type, listenerFn, useCapture);
}
}
}
@ -92,13 +52,9 @@ function delegate(element, selector, type, callback) {
*/
function listener(element, selector, type, callback) {
return function(e) {
var delegateTarget = closest(e.target, selector, true);
if (delegateTarget) {
Object.defineProperty(e, 'target', {
value: delegateTarget
});
e.delegateTarget = closest(e.target, selector, true);
if (e.delegateTarget) {
callback.call(element, e);
}
}
@ -106,7 +62,7 @@ function listener(element, selector, type, callback) {
module.exports = delegate;
},{"closest":1}],4:[function(require,module,exports){
},{"closest":1}],3:[function(require,module,exports){
/**
* Check if argument is a HTML element.
*
@ -151,13 +107,13 @@ exports.string = function(value) {
* @param {Object} value
* @return {Boolean}
*/
exports.function = function(value) {
exports.fn = function(value) {
var type = Object.prototype.toString.call(value);
return type === '[object Function]';
};
},{}],5:[function(require,module,exports){
},{}],4:[function(require,module,exports){
var is = require('./is');
var delegate = require('delegate');
@ -179,7 +135,7 @@ function listen(target, type, callback) {
throw new TypeError('Second argument must be a String');
}
if (!is.function(callback)) {
if (!is.fn(callback)) {
throw new TypeError('Third argument must be a Function');
}
@ -254,16 +210,62 @@ function listenSelector(selector, type, callback) {
module.exports = listen;
},{"./is":4,"delegate":3}],6:[function(require,module,exports){
},{"./is":3,"delegate":2}],5:[function(require,module,exports){
/**
* Element prototype.
*/
var proto = Element.prototype;
/**
* Vendor function.
*/
var vendor = proto.matchesSelector
|| proto.webkitMatchesSelector
|| proto.mozMatchesSelector
|| proto.msMatchesSelector
|| proto.oMatchesSelector;
/**
* Expose `match()`.
*/
module.exports = match;
/**
* Match `el` to `selector`.
*
* @param {Element} el
* @param {String} selector
* @return {Boolean}
* @api public
*/
function match(el, selector) {
if (vendor) return vendor.call(el, selector);
var nodes = el.parentNode.querySelectorAll(selector);
for (var i = 0; i < nodes.length; ++i) {
if (nodes[i] == el) return true;
}
return false;
}
},{}],6:[function(require,module,exports){
function select(element) {
var selectedText;
if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
element.select();
element.focus();
element.setSelectionRange(0, element.value.length);
selectedText = element.value;
}
else {
if (element.hasAttribute('contenteditable')) {
element.focus();
}
var selection = window.getSelection();
var range = document.createRange();
@ -421,6 +423,8 @@ var ClipboardAction = (function () {
ClipboardAction.prototype.selectFake = function selectFake() {
var _this = this;
var isRTL = document.documentElement.getAttribute('dir') == 'rtl';
this.removeFake();
this.fakeHandler = document.body.addEventListener('click', function () {
@ -428,8 +432,16 @@ var ClipboardAction = (function () {
});
this.fakeElem = document.createElement('textarea');
// Prevent zooming on iOS
this.fakeElem.style.fontSize = '12pt';
// Reset box model
this.fakeElem.style.border = '0';
this.fakeElem.style.padding = '0';
this.fakeElem.style.margin = '0';
// Move element out of screen horizontally
this.fakeElem.style.position = 'absolute';
this.fakeElem.style.left = '-9999px';
this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px';
// Move element to the same position vertically
this.fakeElem.style.top = (window.pageYOffset || document.documentElement.scrollTop) + 'px';
this.fakeElem.setAttribute('readonly', '');
this.fakeElem.value = this.text;
@ -665,15 +677,17 @@ var Clipboard = (function (_Emitter) {
*/
Clipboard.prototype.onClick = function onClick(e) {
var trigger = e.delegateTarget || e.currentTarget;
if (this.clipboardAction) {
this.clipboardAction = null;
}
this.clipboardAction = new _clipboardAction2['default']({
action: this.action(e.target),
target: this.target(e.target),
text: this.text(e.target),
trigger: e.target,
action: this.action(trigger),
target: this.target(trigger),
text: this.text(trigger),
trigger: trigger,
emitter: this
});
};
@ -725,6 +739,7 @@ var Clipboard = (function (_Emitter) {
return Clipboard;
})(_tinyEmitter2['default']);
exports['default'] = Clipboard;
function getAttributeValue(suffix, element) {
var attribute = 'data-clipboard-' + suffix;
@ -734,9 +749,7 @@ function getAttributeValue(suffix, element) {
return element.getAttribute(attribute);
}
exports['default'] = Clipboard;
module.exports = exports['default'];
},{"./clipboard-action":8,"good-listener":5,"tiny-emitter":7}]},{},[9])(9)
},{"./clipboard-action":8,"good-listener":4,"tiny-emitter":7}]},{},[9])(9)
});

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.1",
version: "1.5.8",
git: "https://github.com/zenorocha/clipboard.js"
});
Package.onUse(function(api) {
api.addFiles("dist/clipboard.min.js", "client");
api.addFiles("dist/clipboard.js", "client");
});

View File

@ -1,6 +1,6 @@
{
"name": "clipboard",
"version": "1.5.2",
"version": "1.5.8",
"description": "Modern copy to clipboard. No Flash. Just 2kb",
"repository": "zenorocha/clipboard.js",
"license": "MIT",
@ -11,14 +11,16 @@
"cut"
],
"dependencies": {
"good-listener": "^1.1.2",
"select": "^1.0.4",
"good-listener": "^1.1.6",
"select": "^1.0.6",
"tiny-emitter": "^1.0.0"
},
"devDependencies": {
"babel": "^5.8.29",
"babelify": "^6.3.0",
"bannerify": "Vekat/bannerify#feature-option",
"browserify": "^11.2.0",
"chai": "^3.4.1",
"karma": "^0.13.10",
"karma-browserify": "^4.4.0",
"karma-chai": "^0.1.0",
@ -26,10 +28,11 @@
"karma-phantomjs-launcher": "^0.2.1",
"karma-sinon": "^1.0.4",
"mocha": "^2.3.3",
"phantomjs": "^1.9.18",
"phantomjs-polyfill": "0.0.1",
"sinon": "^1.17.2",
"uglify-js": "^2.4.24",
"watchify": "^3.4.0",
"bannerify": "Vekat/bannerify#feature-option"
"watchify": "^3.4.0"
},
"scripts": {
"build": "npm run build-debug && npm run build-min",

View File

@ -151,7 +151,7 @@ new Clipboard('.btn', {
});
```
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.
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 Clipboard('.btn');

View File

@ -4,7 +4,7 @@ import select from 'select';
* Inner class which performs selection from either `text` or `target`
* properties and then executes copy or cut operations.
*/
class ClipboardAction {
export default class ClipboardAction {
/**
* @param {Object} options
*/
@ -51,13 +51,23 @@ class ClipboardAction {
* and makes a selection on it.
*/
selectFake() {
let isRTL = document.documentElement.getAttribute('dir') == 'rtl';
this.removeFake();
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.left = '-9999px';
this.fakeElem.style[ isRTL ? 'right' : 'left' ] = '-9999px';
// Move element to the same position vertically
this.fakeElem.style.top = (window.pageYOffset || document.documentElement.scrollTop) + 'px';
this.fakeElem.setAttribute('readonly', '');
this.fakeElem.value = this.text;
@ -192,5 +202,3 @@ class ClipboardAction {
this.removeFake();
}
}
export default ClipboardAction;

View File

@ -6,7 +6,7 @@ import listen from 'good-listener';
* Base class which takes one or more elements, adds event listeners to them,
* and instantiates a new `ClipboardAction` on each click.
*/
class Clipboard extends Emitter {
export default class Clipboard extends Emitter {
/**
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
* @param {Object} options
@ -42,15 +42,17 @@ class Clipboard extends Emitter {
* @param {Event} e
*/
onClick(e) {
let trigger = e.delegateTarget || e.currentTarget;
if (this.clipboardAction) {
this.clipboardAction = null;
}
this.clipboardAction = new ClipboardAction({
action : this.action(e.target),
target : this.target(e.target),
text : this.text(e.target),
trigger : e.target,
action : this.action(trigger),
target : this.target(trigger),
text : this.text(trigger),
trigger : trigger,
emitter : this
});
}
@ -111,5 +113,3 @@ function getAttributeValue(suffix, element) {
return element.getAttribute(attribute);
}
export default Clipboard;

View File

@ -57,6 +57,19 @@ describe('ClipboardAction', () => {
done();
}
});
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'
});
assert.equal(clip.fakeElem.style.right, '-9999px');
done();
});
});
describe('#set action', () => {

View File

@ -9,8 +9,14 @@ describe('Clipboard', () => {
global.button.setAttribute('data-clipboard-text', 'foo');
document.body.appendChild(global.button);
global.span = document.createElement('span');
global.span.innerHTML = 'bar';
global.button.appendChild(span);
global.event = {
target: global.button
target: global.button,
currentTarget: global.button
};
});
@ -63,7 +69,15 @@ describe('Clipboard', () => {
assert.instanceOf(clipboard.clipboardAction, ClipboardAction);
});
it('should throws exception target', done => {
it('should use an event\'s currentTarget when not equal to target', () => {
let clipboard = new Clipboard('.btn');
let bubbledEvent = { target: global.span, currentTarget: global.button };
clipboard.onClick(bubbledEvent);
assert.instanceOf(clipboard.clipboardAction, ClipboardAction);
});
it('should throw an exception when target is invalid', done => {
try {
var clipboard = new Clipboard('.btn', {
target: function() {