Compare commits

...

10 Commits

Author SHA1 Message Date
Zeno Rocha
bb6c4c0e7c Release v1.5.2 2015-10-28 12:57:47 -07:00
Zeno Rocha
03ee9829e0 Removes browserify tests 2015-10-28 12:56:52 -07:00
Zeno Rocha
24f6e1f541 Moves babel to devDependencies 2015-10-28 12:56:38 -07:00
Zeno Rocha
5bdfff6375 Ignores unnecessary files on bower and npm 2015-10-28 12:56:21 -07:00
Zeno Rocha
4b73122f81 Provides a transpiled version to npm so browserify and webpack users do not have to transpile by themselves
Fixes #15 #27 #105
2015-10-28 12:54:29 -07:00
Zeno Rocha
171f438f22 Release v1.5.1 2015-10-27 09:32:43 -07:00
Zeno Rocha
42a459402c Release v1.5.0 2015-10-26 23:04:54 -07:00
Zeno Rocha
b26cdb3b41 Allows HTML elements to be passed in the constructor - Fixes #25 2015-10-26 23:02:29 -07:00
Zeno Rocha
57c7fcf9a4 Adds a bunch of demos 2015-10-26 22:53:03 -07:00
Zeno Rocha
6b1f6b22a6 Allows HTML elements to be passed in the constructor - Fixes #25 2015-10-26 01:06:29 -07:00
19 changed files with 488 additions and 159 deletions

1
.gitignore vendored
View File

@@ -1,3 +1,4 @@
lib
npm-debug.log
bower_components
node_modules

View File

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

View File

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

View File

@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>constructor-node</title>
</head>
<body>
<!-- 1. Define some markup -->
<button id="btn" data-clipboard-text="1">Copy</button>
<!-- 2. Include library -->
<script src="../dist/clipboard.min.js"></script>
<!-- 3. Instantiate clipboard by passing a HTML element -->
<script>
var btn = document.getElementById('btn');
var clipboard = new Clipboard(btn);
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
</script>
</body>
</html>

View File

@@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>constructor-nodelist</title>
</head>
<body>
<!-- 1. Define some markup -->
<button data-clipboard-text="1">Copy</button>
<button data-clipboard-text="2">Copy</button>
<button data-clipboard-text="3">Copy</button>
<!-- 2. Include library -->
<script src="../dist/clipboard.min.js"></script>
<!-- 3. Instantiate clipboard by passing a list of HTML elements -->
<script>
var btns = document.querySelectorAll('button');
var clipboard = new Clipboard(btns);
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
</script>
</body>
</html>

View File

@@ -0,0 +1,29 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>constructor-selector</title>
</head>
<body>
<!-- 1. Define some markup -->
<button class="btn" data-clipboard-text="1">Copy</button>
<button class="btn" data-clipboard-text="2">Copy</button>
<button class="btn" data-clipboard-text="3">Copy</button>
<!-- 2. Include library -->
<script src="../dist/clipboard.min.js"></script>
<!-- 3. Instantiate clipboard by passing a string selector -->
<script>
var clipboard = new Clipboard('.btn');
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
</script>
</body>
</html>

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

@@ -0,0 +1,32 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>function-target</title>
</head>
<body>
<!-- 1. Define some markup -->
<button class="btn">Copy</button>
<div>hello</div>
<!-- 2. Include library -->
<script src="../dist/clipboard.min.js"></script>
<!-- 3. Instantiate clipboard -->
<script>
var clipboard = new Clipboard('.btn', {
target: function() {
return document.querySelector('div');
}
});
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
</script>
</body>
</html>

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

@@ -0,0 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>function-text</title>
</head>
<body>
<!-- 1. Define some markup -->
<button class="btn">Copy</button>
<!-- 2. Include library -->
<script src="../dist/clipboard.min.js"></script>
<!-- 3. Instantiate clipboard -->
<script>
var clipboard = new Clipboard('.btn', {
text: function() {
return 'to be or not to be';
}
});
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
</script>
</body>
</html>

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

@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>target-div</title>
</head>
<body>
<!-- 1. Define some markup -->
<div>hello</div>
<button class="btn" data-clipboard-action="copy" data-clipboard-target="div">Copy</button>
<!-- 2. Include library -->
<script src="../dist/clipboard.min.js"></script>
<!-- 3. Instantiate clipboard -->
<script>
var clipboard = new Clipboard('.btn');
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
</script>
</body>
</html>

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

@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>target-input</title>
</head>
<body>
<!-- 1. Define some markup -->
<input id="foo" type="text" value="hello">
<button class="btn" data-clipboard-action="copy" data-clipboard-target="#foo">Copy</button>
<!-- 2. Include library -->
<script src="../dist/clipboard.min.js"></script>
<!-- 3. Instantiate clipboard -->
<script>
var clipboard = new Clipboard('.btn');
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
</script>
</body>
</html>

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

@@ -0,0 +1,28 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>target-textarea</title>
</head>
<body>
<!-- 1. Define some markup -->
<textarea id="bar">hello</textarea>
<button class="btn" data-clipboard-action="cut" data-clipboard-target="#bar">Cut</button>
<!-- 2. Include library -->
<script src="../dist/clipboard.min.js"></script>
<!-- 3. Instantiate clipboard -->
<script>
var clipboard = new Clipboard('.btn');
clipboard.on('success', function(e) {
console.log(e);
});
clipboard.on('error', function(e) {
console.log(e);
});
</script>
</body>
</html>

269
dist/clipboard.js vendored
View File

@@ -1,5 +1,5 @@
/*!
* clipboard.js v1.4.3
* clipboard.js v1.5.2
* https://zenorocha.github.io/clipboard.js
*
* Licensed MIT © Zeno Rocha
@@ -61,61 +61,225 @@ function match(el, selector) {
var closest = require('closest');
/**
* Delegate event `type` to `selector`
* and invoke `fn(e)`. A callback function
* is returned which may be passed to `.unbind()`.
* Delegates event to a selector.
*
* @param {Element} el
* @param {Element} element
* @param {String} selector
* @param {String} type
* @param {Function} fn
* @param {Boolean} capture
* @param {Function} callback
* @return {Object}
*/
function delegate(element, selector, type, callback) {
var listenerFn = listener.apply(this, arguments);
element.addEventListener(type, listenerFn);
return {
destroy: function() {
element.removeEventListener(type, listenerFn);
}
}
}
/**
* Finds closest match and invokes callback.
*
* @param {Element} element
* @param {String} selector
* @param {String} type
* @param {Function} callback
* @return {Function}
*/
function listener(element, selector, type, callback) {
return function(e) {
var delegateTarget = closest(e.target, selector, true);
exports.bind = function(el, selector, type, fn, capture){
return el.addEventListener(type, function(e){
var target = e.target || e.srcElement;
e.delegateTarget = closest(target, selector, true, el);
if (e.delegateTarget) fn.call(el, e);
}, capture);
if (delegateTarget) {
Object.defineProperty(e, 'target', {
value: delegateTarget
});
callback.call(element, e);
}
}
}
module.exports = delegate;
},{"closest":1}],4:[function(require,module,exports){
/**
* Check if argument is a HTML element.
*
* @param {Object} value
* @return {Boolean}
*/
exports.node = function(value) {
return value !== undefined
&& value instanceof HTMLElement
&& value.nodeType === 1;
};
/**
* Unbind event `type`'s callback `fn`.
* Check if argument is a list of HTML elements.
*
* @param {Element} el
* @param {String} type
* @param {Function} fn
* @param {Boolean} capture
* @param {Object} value
* @return {Boolean}
*/
exports.nodeList = function(value) {
var type = Object.prototype.toString.call(value);
exports.unbind = function(el, type, fn, capture){
el.removeEventListener(type, fn, capture);
return value !== undefined
&& (type === '[object NodeList]' || type === '[object HTMLCollection]')
&& ('length' in value)
&& (value.length === 0 || exports.node(value[0]));
};
},{"closest":1}],4:[function(require,module,exports){
function select(element) {
var selection = window.getSelection();
/**
* Check if argument is a string.
*
* @param {Object} value
* @return {Boolean}
*/
exports.string = function(value) {
return typeof value === 'string'
|| value instanceof String;
};
if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
element.selectionStart = 0;
element.selectionEnd = element.value.length;
/**
* Check if argument is a function.
*
* @param {Object} value
* @return {Boolean}
*/
exports.function = function(value) {
var type = Object.prototype.toString.call(value);
return type === '[object Function]';
};
},{}],5:[function(require,module,exports){
var is = require('./is');
var delegate = require('delegate');
/**
* Validates all params and calls the right
* listener function based on its target type.
*
* @param {String|HTMLElement|HTMLCollection|NodeList} target
* @param {String} type
* @param {Function} callback
* @return {Object}
*/
function listen(target, type, callback) {
if (!target && !type && !callback) {
throw new Error('Missing required arguments');
}
if (!is.string(type)) {
throw new TypeError('Second argument must be a String');
}
if (!is.function(callback)) {
throw new TypeError('Third argument must be a Function');
}
if (is.node(target)) {
return listenNode(target, type, callback);
}
else if (is.nodeList(target)) {
return listenNodeList(target, type, callback);
}
else if (is.string(target)) {
return listenSelector(target, type, callback);
}
else {
throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');
}
}
/**
* Adds an event listener to a HTML element
* and returns a remove listener function.
*
* @param {HTMLElement} node
* @param {String} type
* @param {Function} callback
* @return {Object}
*/
function listenNode(node, type, callback) {
node.addEventListener(type, callback);
return {
destroy: function() {
node.removeEventListener(type, callback);
}
}
}
/**
* Add an event listener to a list of HTML elements
* and returns a remove listener function.
*
* @param {NodeList|HTMLCollection} nodeList
* @param {String} type
* @param {Function} callback
* @return {Object}
*/
function listenNodeList(nodeList, type, callback) {
Array.prototype.forEach.call(nodeList, function(node) {
node.addEventListener(type, callback);
});
return {
destroy: function() {
Array.prototype.forEach.call(nodeList, function(node) {
node.removeEventListener(type, callback);
});
}
}
}
/**
* Add an event listener to a selector
* and returns a remove listener function.
*
* @param {String} selector
* @param {String} type
* @param {Function} callback
* @return {Object}
*/
function listenSelector(selector, type, callback) {
return delegate(document.body, selector, type, callback);
}
module.exports = listen;
},{"./is":4,"delegate":3}],6:[function(require,module,exports){
function select(element) {
var selectedText;
if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
element.select();
selectedText = element.value;
}
else {
var selection = window.getSelection();
var range = document.createRange();
range.selectNodeContents(element);
selection.removeAllRanges();
selection.addRange(range);
selectedText = selection.toString();
}
return selection.toString();
return selectedText;
}
module.exports = select;
},{}],5:[function(require,module,exports){
},{}],7:[function(require,module,exports){
function E () {
// Keep this empty so it's easier to inherit from
// (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
@@ -183,7 +347,7 @@ E.prototype = {
module.exports = E;
},{}],6:[function(require,module,exports){
},{}],8:[function(require,module,exports){
'use strict';
exports.__esModule = true;
@@ -417,7 +581,7 @@ var ClipboardAction = (function () {
exports['default'] = ClipboardAction;
module.exports = exports['default'];
},{"select":4}],7:[function(require,module,exports){
},{"select":6}],9:[function(require,module,exports){
'use strict';
exports.__esModule = true;
@@ -432,16 +596,16 @@ var _clipboardAction = require('./clipboard-action');
var _clipboardAction2 = _interopRequireDefault(_clipboardAction);
var _delegate = require('delegate');
var _delegate2 = _interopRequireDefault(_delegate);
var _tinyEmitter = require('tiny-emitter');
var _tinyEmitter2 = _interopRequireDefault(_tinyEmitter);
var _goodListener = require('good-listener');
var _goodListener2 = _interopRequireDefault(_goodListener);
/**
* Base class which takes a selector, delegates a click event to it,
* Base class which takes one or more elements, adds event listeners to them,
* and instantiates a new `ClipboardAction` on each click.
*/
@@ -449,17 +613,17 @@ var Clipboard = (function (_Emitter) {
_inherits(Clipboard, _Emitter);
/**
* @param {String} selector
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
* @param {Object} options
*/
function Clipboard(selector, options) {
function Clipboard(trigger, options) {
_classCallCheck(this, Clipboard);
_Emitter.call(this);
this.resolveOptions(options);
this.delegateClick(selector);
this.listenClick(trigger);
}
/**
@@ -483,27 +647,18 @@ var Clipboard = (function (_Emitter) {
};
/**
* Delegates a click event on the passed selector.
* @param {String} selector
* Adds a click event listener to the passed trigger.
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
*/
Clipboard.prototype.delegateClick = function delegateClick(selector) {
Clipboard.prototype.listenClick = function listenClick(trigger) {
var _this = this;
this.binding = _delegate2['default'].bind(document.body, selector, 'click', function (e) {
this.listener = _goodListener2['default'](trigger, 'click', function (e) {
return _this.onClick(e);
});
};
/**
* Undelegates a click event on body.
* @param {String} selector
*/
Clipboard.prototype.undelegateClick = function undelegateClick() {
_delegate2['default'].unbind(document.body, 'click', this.binding);
};
/**
* Defines a new `ClipboardAction` on each click event.
* @param {Event} e
@@ -515,10 +670,10 @@ var Clipboard = (function (_Emitter) {
}
this.clipboardAction = new _clipboardAction2['default']({
action: this.action(e.delegateTarget),
target: this.target(e.delegateTarget),
text: this.text(e.delegateTarget),
trigger: e.delegateTarget,
action: this.action(e.target),
target: this.target(e.target),
text: this.text(e.target),
trigger: e.target,
emitter: this
});
};
@@ -559,7 +714,7 @@ var Clipboard = (function (_Emitter) {
*/
Clipboard.prototype.destroy = function destroy() {
this.undelegateClick();
this.listener.destroy();
if (this.clipboardAction) {
this.clipboardAction.destroy();
@@ -583,5 +738,5 @@ function getAttributeValue(suffix, element) {
exports['default'] = Clipboard;
module.exports = exports['default'];
},{"./clipboard-action":6,"delegate":3,"tiny-emitter":5}]},{},[7])(7)
},{"./clipboard-action":8,"good-listener":5,"tiny-emitter":7}]},{},[9])(9)
});

File diff suppressed because one or more lines are too long

View File

@@ -3,7 +3,7 @@
Package.describe({
name: "zenorocha:clipboard",
summary: "Modern copy to clipboard. No Flash. Just 2kb.",
version: "1.4.2",
version: "1.5.1",
git: "https://github.com/zenorocha/clipboard.js"
});

View File

@@ -1,11 +1,10 @@
{
"name": "clipboard",
"version": "1.4.3",
"version": "1.5.2",
"description": "Modern copy to clipboard. No Flash. Just 2kb",
"repository": "zenorocha/clipboard.js",
"license": "MIT",
"main": "dist/clipboard.js",
"browser": "src/clipboard.js",
"main": "lib/clipboard.js",
"browserify": {
"transform": [
[
@@ -22,13 +21,14 @@
"cut"
],
"dependencies": {
"babelify": "^6.3.0",
"browserify": "^11.2.0",
"delegate": "^1.0.0",
"select": "^1.0.0",
"good-listener": "^1.1.2",
"select": "^1.0.4",
"tiny-emitter": "^1.0.0"
},
"devDependencies": {
"babel": "^5.8.29",
"babelify": "^6.3.0",
"browserify": "^11.2.0",
"karma": "^0.13.10",
"karma-browserify": "^4.4.0",
"karma-chai": "^0.1.0",
@@ -46,8 +46,7 @@
"build-debug": "browserify src/clipboard.js -s Clipboard -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 -o dist/clipboard.js -v",
"test": "npm run test-browser && npm run test-server",
"test-browser": "karma start --single-run",
"test-server": "mocha test/module-systems.js"
"test": "karma start --single-run",
"prepublish": "babel src --out-dir lib --loose all"
}
}

View File

@@ -31,19 +31,13 @@ If you're not into package management, just [download a ZIP](https://github.com/
## Setup
First, include the script located on the `dist` folder.
First, include the script located on the `dist` folder or load it from [a third-party CDN provider](https://github.com/zenorocha/clipboard.js/wiki/CDN-Providers).
```html
<script src="dist/clipboard.min.js"></script>
```
Or load it from a CDN.
```html
<script src="https://cdn.rawgit.com/zenorocha/clipboard.js/master/dist/clipboard.min.js"></script>
```
Now, you need to instantiate it using a DOM selector. This selector corresponds to the trigger element(s), for example `<button class="btn">`.
Now, you need to instantiate it by [passing a DOM selector](https://github.com/zenorocha/clipboard.js/blob/master/demo/constructor-selector.html#L18), [HTML element](https://github.com/zenorocha/clipboard.js/blob/master/demo/constructor-node.html#L16-L17), or [list of HTML elements](https://github.com/zenorocha/clipboard.js/blob/master/demo/constructor-nodelist.html#L18-L19).
```js
new Clipboard('.btn');

View File

@@ -1,21 +1,21 @@
import ClipboardAction from './clipboard-action';
import Delegate from 'delegate';
import Emitter from 'tiny-emitter';
import listen from 'good-listener';
/**
* Base class which takes a selector, delegates a click event to it,
* Base class which takes one or more elements, adds event listeners to them,
* and instantiates a new `ClipboardAction` on each click.
*/
class Clipboard extends Emitter {
/**
* @param {String} selector
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
* @param {Object} options
*/
constructor(selector, options) {
constructor(trigger, options) {
super();
this.resolveOptions(options);
this.delegateClick(selector);
this.listenClick(trigger);
}
/**
@@ -30,19 +30,11 @@ class Clipboard extends Emitter {
}
/**
* Delegates a click event on the passed selector.
* @param {String} selector
* Adds a click event listener to the passed trigger.
* @param {String|HTMLElement|HTMLCollection|NodeList} trigger
*/
delegateClick(selector) {
this.binding = Delegate.bind(document.body, selector, 'click', (e) => this.onClick(e));
}
/**
* Undelegates a click event on body.
* @param {String} selector
*/
undelegateClick() {
Delegate.unbind(document.body, 'click', this.binding);
listenClick(trigger) {
this.listener = listen(trigger, 'click', (e) => this.onClick(e));
}
/**
@@ -55,10 +47,10 @@ class Clipboard extends Emitter {
}
this.clipboardAction = new ClipboardAction({
action : this.action(e.delegateTarget),
target : this.target(e.delegateTarget),
text : this.text(e.delegateTarget),
trigger : e.delegateTarget,
action : this.action(e.target),
target : this.target(e.target),
text : this.text(e.target),
trigger : e.target,
emitter : this
});
}
@@ -95,7 +87,7 @@ class Clipboard extends Emitter {
* Destroy lifecycle.
*/
destroy() {
this.undelegateClick();
this.listener.destroy();
if (this.clipboardAction) {
this.clipboardAction.destroy();

View File

@@ -1,6 +1,6 @@
import Clipboard from '../src/clipboard';
import ClipboardAction from '../src/clipboard-action';
import Delegate from 'delegate';
import listen from 'good-listener';
describe('Clipboard', () => {
before(() => {
@@ -10,7 +10,7 @@ describe('Clipboard', () => {
document.body.appendChild(global.button);
global.event = {
delegateTarget: global.button
target: global.button
};
});
@@ -48,45 +48,10 @@ describe('Clipboard', () => {
});
});
describe('#delegateClick', () => {
before(() => {
global.spy = sinon.spy(Delegate, 'bind');
});
after(() => {
global.spy.restore();
});
it('should delegate a click event to the passed selector', () => {
let element = document.body;
let selector = '.btn';
let event = 'click';
let clipboard = new Clipboard(selector);
assert.ok(global.spy.calledOnce);
assert.ok(global.spy.calledWith(element, selector, event));
});
});
describe('#undelegateClick', () => {
before(() => {
global.spy = sinon.spy(Delegate, 'unbind');
});
after(() => {
global.spy.restore();
});
it('should undelegate a click event', () => {
let element = document.body;
let event = 'click';
describe('#listenClick', () => {
it('should add a click event listener to the passed selector', () => {
let clipboard = new Clipboard('.btn');
clipboard.undelegateClick();
assert.ok(global.spy.calledOnce);
assert.ok(global.spy.calledWith(element, event));
assert.isObject(clipboard.listener);
});
});
@@ -105,6 +70,7 @@ describe('Clipboard', () => {
return null;
}
});
clipboard.onClick(global.event);
}
catch(e) {

View File

@@ -1,15 +0,0 @@
var assert = require('chai').assert;
var browserify = require('browserify');
describe('CommonJS', function() {
it('should import the lib in a commonjs env without babel', function(done) {
browserify('./dist/clipboard.js').bundle(function(err) {
assert.equal(err, null);
done();
});
});
});
describe('AMD', function() {
// TODO: Write test case
});