clipboard.js 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978
  1. /*!
  2. * clipboard.js v2.0.4
  3. * https://zenorocha.github.io/clipboard.js
  4. *
  5. * Licensed MIT © Zeno Rocha
  6. */
  7. (function webpackUniversalModuleDefinition(root, factory) {
  8. if(typeof exports === 'object' && typeof module === 'object')
  9. module.exports = factory();
  10. else if(typeof define === 'function' && define.amd)
  11. define([], factory);
  12. else if(typeof exports === 'object')
  13. exports["ClipboardJS"] = factory();
  14. else
  15. root["ClipboardJS"] = factory();
  16. })(this, function() {
  17. return /******/ (function(modules) { // webpackBootstrap
  18. /******/ // The module cache
  19. /******/ var installedModules = {};
  20. /******/
  21. /******/ // The require function
  22. /******/ function __webpack_require__(moduleId) {
  23. /******/
  24. /******/ // Check if module is in cache
  25. /******/ if(installedModules[moduleId]) {
  26. /******/ return installedModules[moduleId].exports;
  27. /******/ }
  28. /******/ // Create a new module (and put it into the cache)
  29. /******/ var module = installedModules[moduleId] = {
  30. /******/ i: moduleId,
  31. /******/ l: false,
  32. /******/ exports: {}
  33. /******/ };
  34. /******/
  35. /******/ // Execute the module function
  36. /******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  37. /******/
  38. /******/ // Flag the module as loaded
  39. /******/ module.l = true;
  40. /******/
  41. /******/ // Return the exports of the module
  42. /******/ return module.exports;
  43. /******/ }
  44. /******/
  45. /******/
  46. /******/ // expose the modules object (__webpack_modules__)
  47. /******/ __webpack_require__.m = modules;
  48. /******/
  49. /******/ // expose the module cache
  50. /******/ __webpack_require__.c = installedModules;
  51. /******/
  52. /******/ // define getter function for harmony exports
  53. /******/ __webpack_require__.d = function(exports, name, getter) {
  54. /******/ if(!__webpack_require__.o(exports, name)) {
  55. /******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
  56. /******/ }
  57. /******/ };
  58. /******/
  59. /******/ // define __esModule on exports
  60. /******/ __webpack_require__.r = function(exports) {
  61. /******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
  62. /******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  63. /******/ }
  64. /******/ Object.defineProperty(exports, '__esModule', { value: true });
  65. /******/ };
  66. /******/
  67. /******/ // create a fake namespace object
  68. /******/ // mode & 1: value is a module id, require it
  69. /******/ // mode & 2: merge all properties of value into the ns
  70. /******/ // mode & 4: return value when already ns object
  71. /******/ // mode & 8|1: behave like require
  72. /******/ __webpack_require__.t = function(value, mode) {
  73. /******/ if(mode & 1) value = __webpack_require__(value);
  74. /******/ if(mode & 8) return value;
  75. /******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
  76. /******/ var ns = Object.create(null);
  77. /******/ __webpack_require__.r(ns);
  78. /******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
  79. /******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
  80. /******/ return ns;
  81. /******/ };
  82. /******/
  83. /******/ // getDefaultExport function for compatibility with non-harmony modules
  84. /******/ __webpack_require__.n = function(module) {
  85. /******/ var getter = module && module.__esModule ?
  86. /******/ function getDefault() { return module['default']; } :
  87. /******/ function getModuleExports() { return module; };
  88. /******/ __webpack_require__.d(getter, 'a', getter);
  89. /******/ return getter;
  90. /******/ };
  91. /******/
  92. /******/ // Object.prototype.hasOwnProperty.call
  93. /******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
  94. /******/
  95. /******/ // __webpack_public_path__
  96. /******/ __webpack_require__.p = "";
  97. /******/
  98. /******/
  99. /******/ // Load entry module and return exports
  100. /******/ return __webpack_require__(__webpack_require__.s = 0);
  101. /******/ })
  102. /************************************************************************/
  103. /******/ ([
  104. /* 0 */
  105. /***/ (function(module, exports, __webpack_require__) {
  106. "use strict";
  107. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
  108. 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; }; }();
  109. var _clipboardAction = __webpack_require__(1);
  110. var _clipboardAction2 = _interopRequireDefault(_clipboardAction);
  111. var _tinyEmitter = __webpack_require__(3);
  112. var _tinyEmitter2 = _interopRequireDefault(_tinyEmitter);
  113. var _goodListener = __webpack_require__(4);
  114. var _goodListener2 = _interopRequireDefault(_goodListener);
  115. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  116. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  117. function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
  118. function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; }
  119. /**
  120. * Base class which takes one or more elements, adds event listeners to them,
  121. * and instantiates a new `ClipboardAction` on each click.
  122. */
  123. var Clipboard = function (_Emitter) {
  124. _inherits(Clipboard, _Emitter);
  125. /**
  126. * @param {String|HTMLElement|HTMLCollection|NodeList} trigger
  127. * @param {Object} options
  128. */
  129. function Clipboard(trigger, options) {
  130. _classCallCheck(this, Clipboard);
  131. var _this = _possibleConstructorReturn(this, (Clipboard.__proto__ || Object.getPrototypeOf(Clipboard)).call(this));
  132. _this.resolveOptions(options);
  133. _this.listenClick(trigger);
  134. return _this;
  135. }
  136. /**
  137. * Defines if attributes would be resolved using internal setter functions
  138. * or custom functions that were passed in the constructor.
  139. * @param {Object} options
  140. */
  141. _createClass(Clipboard, [{
  142. key: 'resolveOptions',
  143. value: function resolveOptions() {
  144. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  145. this.action = typeof options.action === 'function' ? options.action : this.defaultAction;
  146. this.target = typeof options.target === 'function' ? options.target : this.defaultTarget;
  147. this.text = typeof options.text === 'function' ? options.text : this.defaultText;
  148. this.container = _typeof(options.container) === 'object' ? options.container : document.body;
  149. }
  150. /**
  151. * Adds a click event listener to the passed trigger.
  152. * @param {String|HTMLElement|HTMLCollection|NodeList} trigger
  153. */
  154. }, {
  155. key: 'listenClick',
  156. value: function listenClick(trigger) {
  157. var _this2 = this;
  158. this.listener = (0, _goodListener2.default)(trigger, 'click', function (e) {
  159. return _this2.onClick(e);
  160. });
  161. }
  162. /**
  163. * Defines a new `ClipboardAction` on each click event.
  164. * @param {Event} e
  165. */
  166. }, {
  167. key: 'onClick',
  168. value: function onClick(e) {
  169. var trigger = e.delegateTarget || e.currentTarget;
  170. if (this.clipboardAction) {
  171. this.clipboardAction = null;
  172. }
  173. this.clipboardAction = new _clipboardAction2.default({
  174. action: this.action(trigger),
  175. target: this.target(trigger),
  176. text: this.text(trigger),
  177. container: this.container,
  178. trigger: trigger,
  179. emitter: this
  180. });
  181. }
  182. /**
  183. * Default `action` lookup function.
  184. * @param {Element} trigger
  185. */
  186. }, {
  187. key: 'defaultAction',
  188. value: function defaultAction(trigger) {
  189. return getAttributeValue('action', trigger);
  190. }
  191. /**
  192. * Default `target` lookup function.
  193. * @param {Element} trigger
  194. */
  195. }, {
  196. key: 'defaultTarget',
  197. value: function defaultTarget(trigger) {
  198. var selector = getAttributeValue('target', trigger);
  199. if (selector) {
  200. return document.querySelector(selector);
  201. }
  202. }
  203. /**
  204. * Returns the support of the given action, or all actions if no action is
  205. * given.
  206. * @param {String} [action]
  207. */
  208. }, {
  209. key: 'defaultText',
  210. /**
  211. * Default `text` lookup function.
  212. * @param {Element} trigger
  213. */
  214. value: function defaultText(trigger) {
  215. return getAttributeValue('text', trigger);
  216. }
  217. /**
  218. * Destroy lifecycle.
  219. */
  220. }, {
  221. key: 'destroy',
  222. value: function destroy() {
  223. this.listener.destroy();
  224. if (this.clipboardAction) {
  225. this.clipboardAction.destroy();
  226. this.clipboardAction = null;
  227. }
  228. }
  229. }], [{
  230. key: 'isSupported',
  231. value: function isSupported() {
  232. var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ['copy', 'cut'];
  233. var actions = typeof action === 'string' ? [action] : action;
  234. var support = !!document.queryCommandSupported;
  235. actions.forEach(function (action) {
  236. support = support && !!document.queryCommandSupported(action);
  237. });
  238. return support;
  239. }
  240. }]);
  241. return Clipboard;
  242. }(_tinyEmitter2.default);
  243. /**
  244. * Helper function to retrieve attribute value.
  245. * @param {String} suffix
  246. * @param {Element} element
  247. */
  248. function getAttributeValue(suffix, element) {
  249. var attribute = 'data-clipboard-' + suffix;
  250. if (!element.hasAttribute(attribute)) {
  251. return;
  252. }
  253. return element.getAttribute(attribute);
  254. }
  255. module.exports = Clipboard;
  256. /***/ }),
  257. /* 1 */
  258. /***/ (function(module, exports, __webpack_require__) {
  259. "use strict";
  260. var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
  261. 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; }; }();
  262. var _select = __webpack_require__(2);
  263. var _select2 = _interopRequireDefault(_select);
  264. function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
  265. function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
  266. /**
  267. * Inner class which performs selection from either `text` or `target`
  268. * properties and then executes copy or cut operations.
  269. */
  270. var ClipboardAction = function () {
  271. /**
  272. * @param {Object} options
  273. */
  274. function ClipboardAction(options) {
  275. _classCallCheck(this, ClipboardAction);
  276. this.resolveOptions(options);
  277. this.initSelection();
  278. }
  279. /**
  280. * Defines base properties passed from constructor.
  281. * @param {Object} options
  282. */
  283. _createClass(ClipboardAction, [{
  284. key: 'resolveOptions',
  285. value: function resolveOptions() {
  286. var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
  287. this.action = options.action;
  288. this.container = options.container;
  289. this.emitter = options.emitter;
  290. this.target = options.target;
  291. this.text = options.text;
  292. this.trigger = options.trigger;
  293. this.selectedText = '';
  294. }
  295. /**
  296. * Decides which selection strategy is going to be applied based
  297. * on the existence of `text` and `target` properties.
  298. */
  299. }, {
  300. key: 'initSelection',
  301. value: function initSelection() {
  302. if (this.text) {
  303. this.selectFake();
  304. } else if (this.target) {
  305. this.selectTarget();
  306. }
  307. }
  308. /**
  309. * Creates a fake textarea element, sets its value from `text` property,
  310. * and makes a selection on it.
  311. */
  312. }, {
  313. key: 'selectFake',
  314. value: function selectFake() {
  315. var _this = this;
  316. var isRTL = document.documentElement.getAttribute('dir') == 'rtl';
  317. this.removeFake();
  318. this.fakeHandlerCallback = function () {
  319. return _this.removeFake();
  320. };
  321. this.fakeHandler = this.container.addEventListener('click', this.fakeHandlerCallback) || true;
  322. this.fakeElem = document.createElement('textarea');
  323. // Prevent zooming on iOS
  324. this.fakeElem.style.fontSize = '12pt';
  325. // Reset box model
  326. this.fakeElem.style.border = '0';
  327. this.fakeElem.style.padding = '0';
  328. this.fakeElem.style.margin = '0';
  329. // Move element out of screen horizontally
  330. this.fakeElem.style.position = 'absolute';
  331. this.fakeElem.style[isRTL ? 'right' : 'left'] = '-9999px';
  332. // Move element to the same position vertically
  333. var yPosition = window.pageYOffset || document.documentElement.scrollTop;
  334. this.fakeElem.style.top = yPosition + 'px';
  335. this.fakeElem.setAttribute('readonly', '');
  336. this.fakeElem.value = this.text;
  337. this.container.appendChild(this.fakeElem);
  338. this.selectedText = (0, _select2.default)(this.fakeElem);
  339. this.copyText();
  340. }
  341. /**
  342. * Only removes the fake element after another click event, that way
  343. * a user can hit `Ctrl+C` to copy because selection still exists.
  344. */
  345. }, {
  346. key: 'removeFake',
  347. value: function removeFake() {
  348. if (this.fakeHandler) {
  349. this.container.removeEventListener('click', this.fakeHandlerCallback);
  350. this.fakeHandler = null;
  351. this.fakeHandlerCallback = null;
  352. }
  353. if (this.fakeElem) {
  354. this.container.removeChild(this.fakeElem);
  355. this.fakeElem = null;
  356. }
  357. }
  358. /**
  359. * Selects the content from element passed on `target` property.
  360. */
  361. }, {
  362. key: 'selectTarget',
  363. value: function selectTarget() {
  364. this.selectedText = (0, _select2.default)(this.target);
  365. this.copyText();
  366. }
  367. /**
  368. * Executes the copy operation based on the current selection.
  369. */
  370. }, {
  371. key: 'copyText',
  372. value: function copyText() {
  373. var succeeded = void 0;
  374. try {
  375. succeeded = document.execCommand(this.action);
  376. } catch (err) {
  377. succeeded = false;
  378. }
  379. this.handleResult(succeeded);
  380. }
  381. /**
  382. * Fires an event based on the copy operation result.
  383. * @param {Boolean} succeeded
  384. */
  385. }, {
  386. key: 'handleResult',
  387. value: function handleResult(succeeded) {
  388. this.emitter.emit(succeeded ? 'success' : 'error', {
  389. action: this.action,
  390. text: this.selectedText,
  391. trigger: this.trigger,
  392. clearSelection: this.clearSelection.bind(this)
  393. });
  394. }
  395. /**
  396. * Moves focus away from `target` and back to the trigger, removes current selection.
  397. */
  398. }, {
  399. key: 'clearSelection',
  400. value: function clearSelection() {
  401. if (this.trigger) {
  402. this.trigger.focus();
  403. }
  404. window.getSelection().removeAllRanges();
  405. }
  406. /**
  407. * Sets the `action` to be performed which can be either 'copy' or 'cut'.
  408. * @param {String} action
  409. */
  410. }, {
  411. key: 'destroy',
  412. /**
  413. * Destroy lifecycle.
  414. */
  415. value: function destroy() {
  416. this.removeFake();
  417. }
  418. }, {
  419. key: 'action',
  420. set: function set() {
  421. var action = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'copy';
  422. this._action = action;
  423. if (this._action !== 'copy' && this._action !== 'cut') {
  424. throw new Error('Invalid "action" value, use either "copy" or "cut"');
  425. }
  426. }
  427. /**
  428. * Gets the `action` property.
  429. * @return {String}
  430. */
  431. ,
  432. get: function get() {
  433. return this._action;
  434. }
  435. /**
  436. * Sets the `target` property using an element
  437. * that will be have its content copied.
  438. * @param {Element} target
  439. */
  440. }, {
  441. key: 'target',
  442. set: function set(target) {
  443. if (target !== undefined) {
  444. if (target && (typeof target === 'undefined' ? 'undefined' : _typeof(target)) === 'object' && target.nodeType === 1) {
  445. if (this.action === 'copy' && target.hasAttribute('disabled')) {
  446. throw new Error('Invalid "target" attribute. Please use "readonly" instead of "disabled" attribute');
  447. }
  448. if (this.action === 'cut' && (target.hasAttribute('readonly') || target.hasAttribute('disabled'))) {
  449. throw new Error('Invalid "target" attribute. You can\'t cut text from elements with "readonly" or "disabled" attributes');
  450. }
  451. this._target = target;
  452. } else {
  453. throw new Error('Invalid "target" value, use a valid Element');
  454. }
  455. }
  456. }
  457. /**
  458. * Gets the `target` property.
  459. * @return {String|HTMLElement}
  460. */
  461. ,
  462. get: function get() {
  463. return this._target;
  464. }
  465. }]);
  466. return ClipboardAction;
  467. }();
  468. module.exports = ClipboardAction;
  469. /***/ }),
  470. /* 2 */
  471. /***/ (function(module, exports) {
  472. function select(element) {
  473. var selectedText;
  474. if (element.nodeName === 'SELECT') {
  475. element.focus();
  476. selectedText = element.value;
  477. }
  478. else if (element.nodeName === 'INPUT' || element.nodeName === 'TEXTAREA') {
  479. var isReadOnly = element.hasAttribute('readonly');
  480. if (!isReadOnly) {
  481. element.setAttribute('readonly', '');
  482. }
  483. element.select();
  484. element.setSelectionRange(0, element.value.length);
  485. if (!isReadOnly) {
  486. element.removeAttribute('readonly');
  487. }
  488. selectedText = element.value;
  489. }
  490. else {
  491. if (element.hasAttribute('contenteditable')) {
  492. element.focus();
  493. }
  494. var selection = window.getSelection();
  495. var range = document.createRange();
  496. range.selectNodeContents(element);
  497. selection.removeAllRanges();
  498. selection.addRange(range);
  499. selectedText = selection.toString();
  500. }
  501. return selectedText;
  502. }
  503. module.exports = select;
  504. /***/ }),
  505. /* 3 */
  506. /***/ (function(module, exports) {
  507. function E () {
  508. // Keep this empty so it's easier to inherit from
  509. // (via https://github.com/lipsmack from https://github.com/scottcorgan/tiny-emitter/issues/3)
  510. }
  511. E.prototype = {
  512. on: function (name, callback, ctx) {
  513. var e = this.e || (this.e = {});
  514. (e[name] || (e[name] = [])).push({
  515. fn: callback,
  516. ctx: ctx
  517. });
  518. return this;
  519. },
  520. once: function (name, callback, ctx) {
  521. var self = this;
  522. function listener () {
  523. self.off(name, listener);
  524. callback.apply(ctx, arguments);
  525. };
  526. listener._ = callback
  527. return this.on(name, listener, ctx);
  528. },
  529. emit: function (name) {
  530. var data = [].slice.call(arguments, 1);
  531. var evtArr = ((this.e || (this.e = {}))[name] || []).slice();
  532. var i = 0;
  533. var len = evtArr.length;
  534. for (i; i < len; i++) {
  535. evtArr[i].fn.apply(evtArr[i].ctx, data);
  536. }
  537. return this;
  538. },
  539. off: function (name, callback) {
  540. var e = this.e || (this.e = {});
  541. var evts = e[name];
  542. var liveEvents = [];
  543. if (evts && callback) {
  544. for (var i = 0, len = evts.length; i < len; i++) {
  545. if (evts[i].fn !== callback && evts[i].fn._ !== callback)
  546. liveEvents.push(evts[i]);
  547. }
  548. }
  549. // Remove event from queue to prevent memory leak
  550. // Suggested by https://github.com/lazd
  551. // Ref: https://github.com/scottcorgan/tiny-emitter/commit/c6ebfaa9bc973b33d110a84a307742b7cf94c953#commitcomment-5024910
  552. (liveEvents.length)
  553. ? e[name] = liveEvents
  554. : delete e[name];
  555. return this;
  556. }
  557. };
  558. module.exports = E;
  559. /***/ }),
  560. /* 4 */
  561. /***/ (function(module, exports, __webpack_require__) {
  562. var is = __webpack_require__(5);
  563. var delegate = __webpack_require__(6);
  564. /**
  565. * Validates all params and calls the right
  566. * listener function based on its target type.
  567. *
  568. * @param {String|HTMLElement|HTMLCollection|NodeList} target
  569. * @param {String} type
  570. * @param {Function} callback
  571. * @return {Object}
  572. */
  573. function listen(target, type, callback) {
  574. if (!target && !type && !callback) {
  575. throw new Error('Missing required arguments');
  576. }
  577. if (!is.string(type)) {
  578. throw new TypeError('Second argument must be a String');
  579. }
  580. if (!is.fn(callback)) {
  581. throw new TypeError('Third argument must be a Function');
  582. }
  583. if (is.node(target)) {
  584. return listenNode(target, type, callback);
  585. }
  586. else if (is.nodeList(target)) {
  587. return listenNodeList(target, type, callback);
  588. }
  589. else if (is.string(target)) {
  590. return listenSelector(target, type, callback);
  591. }
  592. else {
  593. throw new TypeError('First argument must be a String, HTMLElement, HTMLCollection, or NodeList');
  594. }
  595. }
  596. /**
  597. * Adds an event listener to a HTML element
  598. * and returns a remove listener function.
  599. *
  600. * @param {HTMLElement} node
  601. * @param {String} type
  602. * @param {Function} callback
  603. * @return {Object}
  604. */
  605. function listenNode(node, type, callback) {
  606. node.addEventListener(type, callback);
  607. return {
  608. destroy: function() {
  609. node.removeEventListener(type, callback);
  610. }
  611. }
  612. }
  613. /**
  614. * Add an event listener to a list of HTML elements
  615. * and returns a remove listener function.
  616. *
  617. * @param {NodeList|HTMLCollection} nodeList
  618. * @param {String} type
  619. * @param {Function} callback
  620. * @return {Object}
  621. */
  622. function listenNodeList(nodeList, type, callback) {
  623. Array.prototype.forEach.call(nodeList, function(node) {
  624. node.addEventListener(type, callback);
  625. });
  626. return {
  627. destroy: function() {
  628. Array.prototype.forEach.call(nodeList, function(node) {
  629. node.removeEventListener(type, callback);
  630. });
  631. }
  632. }
  633. }
  634. /**
  635. * Add an event listener to a selector
  636. * and returns a remove listener function.
  637. *
  638. * @param {String} selector
  639. * @param {String} type
  640. * @param {Function} callback
  641. * @return {Object}
  642. */
  643. function listenSelector(selector, type, callback) {
  644. return delegate(document.body, selector, type, callback);
  645. }
  646. module.exports = listen;
  647. /***/ }),
  648. /* 5 */
  649. /***/ (function(module, exports) {
  650. /**
  651. * Check if argument is a HTML element.
  652. *
  653. * @param {Object} value
  654. * @return {Boolean}
  655. */
  656. exports.node = function(value) {
  657. return value !== undefined
  658. && value instanceof HTMLElement
  659. && value.nodeType === 1;
  660. };
  661. /**
  662. * Check if argument is a list of HTML elements.
  663. *
  664. * @param {Object} value
  665. * @return {Boolean}
  666. */
  667. exports.nodeList = function(value) {
  668. var type = Object.prototype.toString.call(value);
  669. return value !== undefined
  670. && (type === '[object NodeList]' || type === '[object HTMLCollection]')
  671. && ('length' in value)
  672. && (value.length === 0 || exports.node(value[0]));
  673. };
  674. /**
  675. * Check if argument is a string.
  676. *
  677. * @param {Object} value
  678. * @return {Boolean}
  679. */
  680. exports.string = function(value) {
  681. return typeof value === 'string'
  682. || value instanceof String;
  683. };
  684. /**
  685. * Check if argument is a function.
  686. *
  687. * @param {Object} value
  688. * @return {Boolean}
  689. */
  690. exports.fn = function(value) {
  691. var type = Object.prototype.toString.call(value);
  692. return type === '[object Function]';
  693. };
  694. /***/ }),
  695. /* 6 */
  696. /***/ (function(module, exports, __webpack_require__) {
  697. var closest = __webpack_require__(7);
  698. /**
  699. * Delegates event to a selector.
  700. *
  701. * @param {Element} element
  702. * @param {String} selector
  703. * @param {String} type
  704. * @param {Function} callback
  705. * @param {Boolean} useCapture
  706. * @return {Object}
  707. */
  708. function _delegate(element, selector, type, callback, useCapture) {
  709. var listenerFn = listener.apply(this, arguments);
  710. element.addEventListener(type, listenerFn, useCapture);
  711. return {
  712. destroy: function() {
  713. element.removeEventListener(type, listenerFn, useCapture);
  714. }
  715. }
  716. }
  717. /**
  718. * Delegates event to a selector.
  719. *
  720. * @param {Element|String|Array} [elements]
  721. * @param {String} selector
  722. * @param {String} type
  723. * @param {Function} callback
  724. * @param {Boolean} useCapture
  725. * @return {Object}
  726. */
  727. function delegate(elements, selector, type, callback, useCapture) {
  728. // Handle the regular Element usage
  729. if (typeof elements.addEventListener === 'function') {
  730. return _delegate.apply(null, arguments);
  731. }
  732. // Handle Element-less usage, it defaults to global delegation
  733. if (typeof type === 'function') {
  734. // Use `document` as the first parameter, then apply arguments
  735. // This is a short way to .unshift `arguments` without running into deoptimizations
  736. return _delegate.bind(null, document).apply(null, arguments);
  737. }
  738. // Handle Selector-based usage
  739. if (typeof elements === 'string') {
  740. elements = document.querySelectorAll(elements);
  741. }
  742. // Handle Array-like based usage
  743. return Array.prototype.map.call(elements, function (element) {
  744. return _delegate(element, selector, type, callback, useCapture);
  745. });
  746. }
  747. /**
  748. * Finds closest match and invokes callback.
  749. *
  750. * @param {Element} element
  751. * @param {String} selector
  752. * @param {String} type
  753. * @param {Function} callback
  754. * @return {Function}
  755. */
  756. function listener(element, selector, type, callback) {
  757. return function(e) {
  758. e.delegateTarget = closest(e.target, selector);
  759. if (e.delegateTarget) {
  760. callback.call(element, e);
  761. }
  762. }
  763. }
  764. module.exports = delegate;
  765. /***/ }),
  766. /* 7 */
  767. /***/ (function(module, exports) {
  768. var DOCUMENT_NODE_TYPE = 9;
  769. /**
  770. * A polyfill for Element.matches()
  771. */
  772. if (typeof Element !== 'undefined' && !Element.prototype.matches) {
  773. var proto = Element.prototype;
  774. proto.matches = proto.matchesSelector ||
  775. proto.mozMatchesSelector ||
  776. proto.msMatchesSelector ||
  777. proto.oMatchesSelector ||
  778. proto.webkitMatchesSelector;
  779. }
  780. /**
  781. * Finds the closest parent that matches a selector.
  782. *
  783. * @param {Element} element
  784. * @param {String} selector
  785. * @return {Function}
  786. */
  787. function closest (element, selector) {
  788. while (element && element.nodeType !== DOCUMENT_NODE_TYPE) {
  789. if (typeof element.matches === 'function' &&
  790. element.matches(selector)) {
  791. return element;
  792. }
  793. element = element.parentNode;
  794. }
  795. }
  796. module.exports = closest;
  797. /***/ })
  798. /******/ ]);
  799. });