Compare commits

..

11 Commits

Author SHA1 Message Date
Zeno Rocha
a00f1fe327 Release v1.6.0 2017-02-08 00:02:14 -08:00
Zeno Rocha
38ae5b34f3 Adds documentation about #355 2017-02-07 23:59:27 -08:00
Zeno Rocha
41b7234d50 Prevents keyboard from opening on iOS when using "data-clipboard-target" 2017-02-07 23:41:46 -08:00
Zeno Rocha
3696739e5e Prevents scroll jump on iOS when using "data-clipboard-text"
Thanks to @geraldarthur at #369
2017-02-07 23:37:43 -08:00
Itai Steinherz
63d1b0f014 Add isSupported method #355 2017-02-07 23:36:29 -08:00
Zeno Rocha
402c9ee17b Release v1.5.16 2016-12-12 11:54:35 -02:00
Zeno Rocha
0538f6e212 Ensures that event delegation works with multiple documents (a page with iframes) 2016-12-12 11:53:41 -02:00
Zeno Rocha
8ad16a2c6c Release v1.5.15 2016-10-22 19:44:43 -07:00
Zeno Rocha
ce0829054b Updates dev dependencies 2016-10-22 19:37:37 -07:00
Zeno Rocha
223d30c110 Removes babel loose mode 2016-10-22 19:37:18 -07:00
Zeno Rocha
b1a68df6e9 Release v1.5.14 2016-10-22 19:36:49 -07:00
10 changed files with 262 additions and 174 deletions

View File

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

View File

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

381
dist/clipboard.js vendored
View File

@@ -1,10 +1,12 @@
/*! /*!
* clipboard.js v1.5.14 * clipboard.js v1.6.0
* https://zenorocha.github.io/clipboard.js * https://zenorocha.github.io/clipboard.js
* *
* Licensed MIT © Zeno Rocha * Licensed MIT © Zeno Rocha
*/ */
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Clipboard = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){ (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.Clipboard = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
var DOCUMENT_NODE_TYPE = 9;
/** /**
* A polyfill for Element.matches() * A polyfill for Element.matches()
*/ */
@@ -26,7 +28,7 @@ if (Element && !Element.prototype.matches) {
* @return {Function} * @return {Function}
*/ */
function closest (element, selector) { function closest (element, selector) {
while (element && element !== document) { while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {
if (element.matches(selector)) return element; if (element.matches(selector)) return element;
element = element.parentNode; element = element.parentNode;
} }
@@ -238,9 +240,19 @@ function select(element) {
selectedText = element.value; selectedText = element.value;
} }
else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') { else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
element.focus(); var isReadOnly = element.hasAttribute('readonly');
if (!isReadOnly) {
element.setAttribute('readonly', '');
}
element.select();
element.setSelectionRange(0, element.value.length); element.setSelectionRange(0, element.value.length);
if (!isReadOnly) {
element.removeAttribute('readonly');
}
selectedText = element.value; selectedText = element.value;
} }
else { else {
@@ -402,114 +414,122 @@ module.exports = E;
*/ */
ClipboardAction.prototype.resolveOptions = function resolveOptions() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
this.action = options.action;
this.emitter = options.emitter;
this.target = options.target;
this.text = options.text;
this.trigger = options.trigger;
this.selectedText = '';
};
ClipboardAction.prototype.initSelection = function initSelection() {
if (this.text) {
this.selectFake();
} else if (this.target) {
this.selectTarget();
}
};
ClipboardAction.prototype.selectFake = function selectFake() {
var _this = this;
var isRTL = document.documentElement.getAttribute('dir') == 'rtl';
this.removeFake();
this.fakeHandlerCallback = function () {
return _this.removeFake();
};
this.fakeHandler = document.body.addEventListener('click', this.fakeHandlerCallback) || true;
this.fakeElem = document.createElement('textarea');
// Prevent zooming on iOS
this.fakeElem.style.fontSize = '12pt';
// Reset box model
this.fakeElem.style.border = '0';
this.fakeElem.style.padding = '0';
this.fakeElem.style.margin = '0';
// Move element out of screen horizontally
this.fakeElem.style.position = 'absolute';
this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px';
// Move element to the same position vertically
var yPosition = window.pageYOffset || document.documentElement.scrollTop;
this.fakeElem.addEventListener('focus', window.scrollTo(0, yPosition));
this.fakeElem.style.top = yPosition + 'px';
this.fakeElem.setAttribute('readonly', '');
this.fakeElem.value = this.text;
document.body.appendChild(this.fakeElem);
this.selectedText = (0, _select2.default)(this.fakeElem);
this.copyText();
};
ClipboardAction.prototype.removeFake = function removeFake() {
if (this.fakeHandler) {
document.body.removeEventListener('click', this.fakeHandlerCallback);
this.fakeHandler = null;
this.fakeHandlerCallback = null;
}
if (this.fakeElem) {
document.body.removeChild(this.fakeElem);
this.fakeElem = null;
}
};
ClipboardAction.prototype.selectTarget = function selectTarget() {
this.selectedText = (0, _select2.default)(this.target);
this.copyText();
};
ClipboardAction.prototype.copyText = function copyText() {
var succeeded = void 0;
try {
succeeded = document.execCommand(this.action);
} catch (err) {
succeeded = false;
}
this.handleResult(succeeded);
};
ClipboardAction.prototype.handleResult = function handleResult(succeeded) {
this.emitter.emit(succeeded ? 'success' : 'error', {
action: this.action,
text: this.selectedText,
trigger: this.trigger,
clearSelection: this.clearSelection.bind(this)
});
};
ClipboardAction.prototype.clearSelection = function clearSelection() {
if (this.target) {
this.target.blur();
}
window.getSelection().removeAllRanges();
};
ClipboardAction.prototype.destroy = function destroy() {
this.removeFake();
};
_createClass(ClipboardAction, [{ _createClass(ClipboardAction, [{
key: 'resolveOptions',
value: function resolveOptions() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
this.action = options.action;
this.emitter = options.emitter;
this.target = options.target;
this.text = options.text;
this.trigger = options.trigger;
this.selectedText = '';
}
}, {
key: 'initSelection',
value: function initSelection() {
if (this.text) {
this.selectFake();
} else if (this.target) {
this.selectTarget();
}
}
}, {
key: 'selectFake',
value: function selectFake() {
var _this = this;
var isRTL = document.documentElement.getAttribute('dir') == 'rtl';
this.removeFake();
this.fakeHandlerCallback = function () {
return _this.removeFake();
};
this.fakeHandler = document.body.addEventListener('click', this.fakeHandlerCallback) || true;
this.fakeElem = document.createElement('textarea');
// Prevent zooming on iOS
this.fakeElem.style.fontSize = '12pt';
// Reset box model
this.fakeElem.style.border = '0';
this.fakeElem.style.padding = '0';
this.fakeElem.style.margin = '0';
// Move element out of screen horizontally
this.fakeElem.style.position = 'absolute';
this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px';
// Move element to the same position vertically
var yPosition = window.pageYOffset || document.documentElement.scrollTop;
this.fakeElem.style.top = yPosition + 'px';
this.fakeElem.setAttribute('readonly', '');
this.fakeElem.value = this.text;
document.body.appendChild(this.fakeElem);
this.selectedText = (0, _select2.default)(this.fakeElem);
this.copyText();
}
}, {
key: 'removeFake',
value: function removeFake() {
if (this.fakeHandler) {
document.body.removeEventListener('click', this.fakeHandlerCallback);
this.fakeHandler = null;
this.fakeHandlerCallback = null;
}
if (this.fakeElem) {
document.body.removeChild(this.fakeElem);
this.fakeElem = null;
}
}
}, {
key: 'selectTarget',
value: function selectTarget() {
this.selectedText = (0, _select2.default)(this.target);
this.copyText();
}
}, {
key: 'copyText',
value: function copyText() {
var succeeded = void 0;
try {
succeeded = document.execCommand(this.action);
} catch (err) {
succeeded = false;
}
this.handleResult(succeeded);
}
}, {
key: 'handleResult',
value: function handleResult(succeeded) {
this.emitter.emit(succeeded ? 'success' : 'error', {
action: this.action,
text: this.selectedText,
trigger: this.trigger,
clearSelection: this.clearSelection.bind(this)
});
}
}, {
key: 'clearSelection',
value: function clearSelection() {
if (this.target) {
this.target.blur();
}
window.getSelection().removeAllRanges();
}
}, {
key: 'destroy',
value: function destroy() {
this.removeFake();
}
}, {
key: 'action', key: 'action',
set: function set() { set: function set() {
var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy'; var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy';
@@ -587,6 +607,24 @@ module.exports = E;
} }
} }
var _createClass = function () {
function defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
return function (Constructor, protoProps, staticProps) {
if (protoProps) defineProperties(Constructor.prototype, protoProps);
if (staticProps) defineProperties(Constructor, staticProps);
return Constructor;
};
}();
function _possibleConstructorReturn(self, call) { function _possibleConstructorReturn(self, call) {
if (!self) { if (!self) {
throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); throw new ReferenceError("this hasn't been initialised - super() hasn't been called");
@@ -621,7 +659,7 @@ module.exports = E;
function Clipboard(trigger, options) { function Clipboard(trigger, options) {
_classCallCheck(this, Clipboard); _classCallCheck(this, Clipboard);
var _this = _possibleConstructorReturn(this, _Emitter.call(this)); var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this));
_this.resolveOptions(options); _this.resolveOptions(options);
_this.listenClick(trigger); _this.listenClick(trigger);
@@ -635,62 +673,85 @@ module.exports = E;
*/ */
Clipboard.prototype.resolveOptions = function resolveOptions() { _createClass(Clipboard, [{
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; key: 'resolveOptions',
value: function resolveOptions() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
this.action = typeof options.action === 'function' ? options.action : this.defaultAction; this.action = typeof options.action === 'function' ? options.action : this.defaultAction;
this.target = typeof options.target === 'function' ? options.target : this.defaultTarget; this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;
this.text = typeof options.text === 'function' ? options.text : this.defaultText; this.text = typeof options.text === 'function' ? options.text : this.defaultText;
};
Clipboard.prototype.listenClick = function listenClick(trigger) {
var _this2 = this;
this.listener = (0, _goodListener2.default)(trigger, 'click', function (e) {
return _this2.onClick(e);
});
};
Clipboard.prototype.onClick = function onClick(e) {
var trigger = e.delegateTarget || e.currentTarget;
if (this.clipboardAction) {
this.clipboardAction = null;
} }
}, {
key: 'listenClick',
value: function listenClick(trigger) {
var _this2 = this;
this.clipboardAction = new _clipboardAction2.default({ this.listener = (0, _goodListener2.default)(trigger, 'click', function (e) {
action: this.action(trigger), return _this2.onClick(e);
target: this.target(trigger), });
text: this.text(trigger),
trigger: trigger,
emitter: this
});
};
Clipboard.prototype.defaultAction = function defaultAction(trigger) {
return getAttributeValue('action', trigger);
};
Clipboard.prototype.defaultTarget = function defaultTarget(trigger) {
var selector = getAttributeValue('target', trigger);
if (selector) {
return document.querySelector(selector);
} }
}; }, {
key: 'onClick',
value: function onClick(e) {
var trigger = e.delegateTarget || e.currentTarget;
Clipboard.prototype.defaultText = function defaultText(trigger) { if (this.clipboardAction) {
return getAttributeValue('text', trigger); this.clipboardAction = null;
}; }
Clipboard.prototype.destroy = function destroy() { this.clipboardAction = new _clipboardAction2.default({
this.listener.destroy(); action: this.action(trigger),
target: this.target(trigger),
if (this.clipboardAction) { text: this.text(trigger),
this.clipboardAction.destroy(); trigger: trigger,
this.clipboardAction = null; emitter: this
});
} }
}; }, {
key: 'defaultAction',
value: function defaultAction(trigger) {
return getAttributeValue('action', trigger);
}
}, {
key: 'defaultTarget',
value: function defaultTarget(trigger) {
var selector = getAttributeValue('target', trigger);
if (selector) {
return document.querySelector(selector);
}
}
}, {
key: 'defaultText',
value: function defaultText(trigger) {
return getAttributeValue('text', trigger);
}
}, {
key: 'destroy',
value: function destroy() {
this.listener.destroy();
if (this.clipboardAction) {
this.clipboardAction.destroy();
this.clipboardAction = null;
}
}
}], [{
key: 'isSupported',
value: function isSupported() {
var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];
var actions = typeof action === 'string' ? [action] : action;
var support = !!document.queryCommandSupported;
actions.forEach(function (action) {
support = support && !!document.queryCommandSupported(action);
});
return support;
}
}]);
return Clipboard; return Clipboard;
}(_tinyEmitter2.default); }(_tinyEmitter2.default);

File diff suppressed because one or more lines are too long

View File

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

View File

@@ -1,6 +1,6 @@
{ {
"name": "clipboard", "name": "clipboard",
"version": "1.5.14", "version": "1.6.0",
"description": "Modern copy to clipboard. No Flash. Just 2kb", "description": "Modern copy to clipboard. No Flash. Just 2kb",
"repository": "zenorocha/clipboard.js", "repository": "zenorocha/clipboard.js",
"license": "MIT", "license": "MIT",
@@ -12,7 +12,7 @@
], ],
"dependencies": { "dependencies": {
"good-listener": "^1.2.0", "good-listener": "^1.2.0",
"select": "^1.0.6", "select": "^1.1.2",
"tiny-emitter": "^1.0.0" "tiny-emitter": "^1.0.0"
}, },
"devDependencies": { "devDependencies": {
@@ -20,19 +20,18 @@
"babel-core": "^6.5.2", "babel-core": "^6.5.2",
"babel-plugin-transform-es2015-modules-umd": "^6.5.0", "babel-plugin-transform-es2015-modules-umd": "^6.5.0",
"babel-preset-es2015": "^6.5.0", "babel-preset-es2015": "^6.5.0",
"babel-preset-es2015-loose": "^7.0.0",
"babelify": "^7.2.0", "babelify": "^7.2.0",
"bannerify": "Vekat/bannerify#feature-option", "bannerify": "Vekat/bannerify#feature-option",
"browserify": "^13.0.0", "browserify": "^13.0.0",
"chai": "^3.4.1", "chai": "^3.4.1",
"install": "^0.4.4", "install": "^0.8.1",
"karma": "^0.13.10", "karma": "^1.3.0",
"karma-browserify": "^5.0.1", "karma-browserify": "^5.0.1",
"karma-chai": "^0.1.0", "karma-chai": "^0.1.0",
"karma-mocha": "^0.2.0", "karma-mocha": "^1.2.0",
"karma-phantomjs-launcher": "^1.0.0", "karma-phantomjs-launcher": "^1.0.0",
"karma-sinon": "^1.0.4", "karma-sinon": "^1.0.4",
"mocha": "^2.3.3", "mocha": "^3.1.2",
"phantomjs-prebuilt": "^2.1.4", "phantomjs-prebuilt": "^2.1.4",
"sinon": "^1.17.2", "sinon": "^1.17.2",
"uglify-js": "^2.4.24", "uglify-js": "^2.4.24",
@@ -44,6 +43,6 @@
"build-min": "uglifyjs dist/clipboard.js --comments '/!/' -m screw_ie8=true -c screw_ie8=true,unused=false -o dist/clipboard.min.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] -o dist/clipboard.js -v",
"test": "karma start --single-run", "test": "karma start --single-run",
"prepublish": "babel src --out-dir lib --loose all" "prepublish": "babel src --out-dir lib"
} }
} }

View File

@@ -168,6 +168,8 @@ This library relies on both [Selection](https://developer.mozilla.org/en-US/docs
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.
You can also check if clipboard.js is supported or not by running `Clipboard.isSupported()`, that way you can hide copy/cut buttons from the UI.
## License ## License
[MIT License](http://zenorocha.mit-license.org/) © Zeno Rocha [MIT License](http://zenorocha.mit-license.org/) © Zeno Rocha

View File

@@ -64,7 +64,6 @@ class ClipboardAction {
this.fakeElem.style[ isRTL ? 'right' : 'left' ] = '-9999px'; this.fakeElem.style[ isRTL ? 'right' : 'left' ] = '-9999px';
// Move element to the same position vertically // Move element to the same position vertically
let yPosition = window.pageYOffset || document.documentElement.scrollTop; let yPosition = window.pageYOffset || document.documentElement.scrollTop;
this.fakeElem.addEventListener('focus', window.scrollTo(0, yPosition));
this.fakeElem.style.top = yPosition + 'px'; this.fakeElem.style.top = yPosition + 'px';
this.fakeElem.setAttribute('readonly', ''); this.fakeElem.setAttribute('readonly', '');

View File

@@ -77,6 +77,22 @@ class Clipboard extends Emitter {
} }
} }
/**
* 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. * Default `text` lookup function.
* @param {Element} trigger * @param {Element} trigger

View File

@@ -94,6 +94,17 @@ describe('Clipboard', () => {
}); });
}); });
describe('#static isSupported', () => {
it('should return the support of the given action', () => {
assert.equal(Clipboard.isSupported('copy'), false);
assert.equal(Clipboard.isSupported('cut'), false);
});
it('should return the support of the cut and copy actions', () => {
assert.equal(Clipboard.isSupported(), false);
});
});
describe('#destroy', () => { describe('#destroy', () => {
it('should destroy an existing instance of ClipboardAction', () => { it('should destroy an existing instance of ClipboardAction', () => {
let clipboard = new Clipboard('.btn'); let clipboard = new Clipboard('.btn');