eve.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426
  1. // Copyright (c) 2013 Adobe Systems Incorporated. All rights reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // ┌────────────────────────────────────────────────────────────┐ \\
  15. // │ Eve 0.5.0 - JavaScript Events Library │ \\
  16. // ├────────────────────────────────────────────────────────────┤ \\
  17. // │ Author Dmitry Baranovskiy (http://dmitry.baranovskiy.com/) │ \\
  18. // └────────────────────────────────────────────────────────────┘ \\
  19. (function (glob) {
  20. var version = "0.5.0",
  21. has = "hasOwnProperty",
  22. separator = /[\.\/]/,
  23. comaseparator = /\s*,\s*/,
  24. wildcard = "*",
  25. fun = function () {},
  26. numsort = function (a, b) {
  27. return a - b;
  28. },
  29. current_event,
  30. stop,
  31. events = {n: {}},
  32. firstDefined = function () {
  33. for (var i = 0, ii = this.length; i < ii; i++) {
  34. if (typeof this[i] != "undefined") {
  35. return this[i];
  36. }
  37. }
  38. },
  39. lastDefined = function () {
  40. var i = this.length;
  41. while (--i) {
  42. if (typeof this[i] != "undefined") {
  43. return this[i];
  44. }
  45. }
  46. },
  47. objtos = Object.prototype.toString,
  48. Str = String,
  49. isArray = Array.isArray || function (ar) {
  50. return ar instanceof Array || objtos.call(ar) == "[object Array]";
  51. };
  52. /*\
  53. * eve
  54. [ method ]
  55. * Fires event with given `name`, given scope and other parameters.
  56. > Arguments
  57. - name (string) name of the *event*, dot (`.`) or slash (`/`) separated
  58. - scope (object) context for the event handlers
  59. - varargs (...) the rest of arguments will be sent to event handlers
  60. = (object) array of returned values from the listeners. Array has two methods `.firstDefined()` and `.lastDefined()` to get first or last not `undefined` value.
  61. \*/
  62. eve = function (name, scope) {
  63. var e = events,
  64. oldstop = stop,
  65. args = Array.prototype.slice.call(arguments, 2),
  66. listeners = eve.listeners(name),
  67. z = 0,
  68. f = false,
  69. l,
  70. indexed = [],
  71. queue = {},
  72. out = [],
  73. ce = current_event,
  74. errors = [];
  75. out.firstDefined = firstDefined;
  76. out.lastDefined = lastDefined;
  77. current_event = name;
  78. stop = 0;
  79. for (var i = 0, ii = listeners.length; i < ii; i++) if ("zIndex" in listeners[i]) {
  80. indexed.push(listeners[i].zIndex);
  81. if (listeners[i].zIndex < 0) {
  82. queue[listeners[i].zIndex] = listeners[i];
  83. }
  84. }
  85. indexed.sort(numsort);
  86. while (indexed[z] < 0) {
  87. l = queue[indexed[z++]];
  88. out.push(l.apply(scope, args));
  89. if (stop) {
  90. stop = oldstop;
  91. return out;
  92. }
  93. }
  94. for (i = 0; i < ii; i++) {
  95. l = listeners[i];
  96. if ("zIndex" in l) {
  97. if (l.zIndex == indexed[z]) {
  98. out.push(l.apply(scope, args));
  99. if (stop) {
  100. break;
  101. }
  102. do {
  103. z++;
  104. l = queue[indexed[z]];
  105. l && out.push(l.apply(scope, args));
  106. if (stop) {
  107. break;
  108. }
  109. } while (l)
  110. } else {
  111. queue[l.zIndex] = l;
  112. }
  113. } else {
  114. out.push(l.apply(scope, args));
  115. if (stop) {
  116. break;
  117. }
  118. }
  119. }
  120. stop = oldstop;
  121. current_event = ce;
  122. return out;
  123. };
  124. // Undocumented. Debug only.
  125. eve._events = events;
  126. /*\
  127. * eve.listeners
  128. [ method ]
  129. * Internal method which gives you array of all event handlers that will be triggered by the given `name`.
  130. > Arguments
  131. - name (string) name of the event, dot (`.`) or slash (`/`) separated
  132. = (array) array of event handlers
  133. \*/
  134. eve.listeners = function (name) {
  135. var names = isArray(name) ? name : name.split(separator),
  136. e = events,
  137. item,
  138. items,
  139. k,
  140. i,
  141. ii,
  142. j,
  143. jj,
  144. nes,
  145. es = [e],
  146. out = [];
  147. for (i = 0, ii = names.length; i < ii; i++) {
  148. nes = [];
  149. for (j = 0, jj = es.length; j < jj; j++) {
  150. e = es[j].n;
  151. items = [e[names[i]], e[wildcard]];
  152. k = 2;
  153. while (k--) {
  154. item = items[k];
  155. if (item) {
  156. nes.push(item);
  157. out = out.concat(item.f || []);
  158. }
  159. }
  160. }
  161. es = nes;
  162. }
  163. return out;
  164. };
  165. /*\
  166. * eve.separator
  167. [ method ]
  168. * If for some reasons you don’t like default separators (`.` or `/`) you can specify yours
  169. * here. Be aware that if you pass a string longer than one character it will be treated as
  170. * a list of characters.
  171. - separator (string) new separator. Empty string resets to default: `.` or `/`.
  172. \*/
  173. eve.separator = function (sep) {
  174. if (sep) {
  175. sep = Str(sep).replace(/(?=[\.\^\]\[\-])/g, "\\");
  176. sep = "[" + sep + "]";
  177. separator = new RegExp(sep);
  178. } else {
  179. separator = /[\.\/]/;
  180. }
  181. };
  182. /*\
  183. * eve.on
  184. [ method ]
  185. **
  186. * Binds given event handler with a given name. You can use wildcards “`*`” for the names:
  187. | eve.on("*.under.*", f);
  188. | eve("mouse.under.floor"); // triggers f
  189. * Use @eve to trigger the listener.
  190. **
  191. - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
  192. - f (function) event handler function
  193. **
  194. - name (array) if you don’t want to use separators, you can use array of strings
  195. - f (function) event handler function
  196. **
  197. = (function) returned function accepts a single numeric parameter that represents z-index of the handler. It is an optional feature and only used when you need to ensure that some subset of handlers will be invoked in a given order, despite of the order of assignment.
  198. > Example:
  199. | eve.on("mouse", eatIt)(2);
  200. | eve.on("mouse", scream);
  201. | eve.on("mouse", catchIt)(1);
  202. * This will ensure that `catchIt` function will be called before `eatIt`.
  203. *
  204. * If you want to put your handler before non-indexed handlers, specify a negative value.
  205. * Note: I assume most of the time you don’t need to worry about z-index, but it’s nice to have this feature “just in case”.
  206. \*/
  207. eve.on = function (name, f) {
  208. if (typeof f != "function") {
  209. return function () {};
  210. }
  211. var names = isArray(name) ? (isArray(name[0]) ? name : [name]) : Str(name).split(comaseparator);
  212. for (var i = 0, ii = names.length; i < ii; i++) {
  213. (function (name) {
  214. var names = isArray(name) ? name : Str(name).split(separator),
  215. e = events,
  216. exist;
  217. for (var i = 0, ii = names.length; i < ii; i++) {
  218. e = e.n;
  219. e = e.hasOwnProperty(names[i]) && e[names[i]] || (e[names[i]] = {n: {}});
  220. }
  221. e.f = e.f || [];
  222. for (i = 0, ii = e.f.length; i < ii; i++) if (e.f[i] == f) {
  223. exist = true;
  224. break;
  225. }
  226. !exist && e.f.push(f);
  227. }(names[i]));
  228. }
  229. return function (zIndex) {
  230. if (+zIndex == +zIndex) {
  231. f.zIndex = +zIndex;
  232. }
  233. };
  234. };
  235. /*\
  236. * eve.f
  237. [ method ]
  238. **
  239. * Returns function that will fire given event with optional arguments.
  240. * Arguments that will be passed to the result function will be also
  241. * concated to the list of final arguments.
  242. | el.onclick = eve.f("click", 1, 2);
  243. | eve.on("click", function (a, b, c) {
  244. | console.log(a, b, c); // 1, 2, [event object]
  245. | });
  246. > Arguments
  247. - event (string) event name
  248. - varargs (…) and any other arguments
  249. = (function) possible event handler function
  250. \*/
  251. eve.f = function (event) {
  252. var attrs = [].slice.call(arguments, 1);
  253. return function () {
  254. eve.apply(null, [event, null].concat(attrs).concat([].slice.call(arguments, 0)));
  255. };
  256. };
  257. /*\
  258. * eve.stop
  259. [ method ]
  260. **
  261. * Is used inside an event handler to stop the event, preventing any subsequent listeners from firing.
  262. \*/
  263. eve.stop = function () {
  264. stop = 1;
  265. };
  266. /*\
  267. * eve.nt
  268. [ method ]
  269. **
  270. * Could be used inside event handler to figure out actual name of the event.
  271. **
  272. > Arguments
  273. **
  274. - subname (string) #optional subname of the event
  275. **
  276. = (string) name of the event, if `subname` is not specified
  277. * or
  278. = (boolean) `true`, if current event’s name contains `subname`
  279. \*/
  280. eve.nt = function (subname) {
  281. var cur = isArray(current_event) ? current_event.join(".") : current_event;
  282. if (subname) {
  283. return new RegExp("(?:\\.|\\/|^)" + subname + "(?:\\.|\\/|$)").test(cur);
  284. }
  285. return cur;
  286. };
  287. /*\
  288. * eve.nts
  289. [ method ]
  290. **
  291. * Could be used inside event handler to figure out actual name of the event.
  292. **
  293. **
  294. = (array) names of the event
  295. \*/
  296. eve.nts = function () {
  297. return isArray(current_event) ? current_event : current_event.split(separator);
  298. };
  299. /*\
  300. * eve.off
  301. [ method ]
  302. **
  303. * Removes given function from the list of event listeners assigned to given name.
  304. * If no arguments specified all the events will be cleared.
  305. **
  306. > Arguments
  307. **
  308. - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
  309. - f (function) event handler function
  310. \*/
  311. /*\
  312. * eve.unbind
  313. [ method ]
  314. **
  315. * See @eve.off
  316. \*/
  317. eve.off = eve.unbind = function (name, f) {
  318. if (!name) {
  319. eve._events = events = {n: {}};
  320. return;
  321. }
  322. var names = isArray(name) ? (isArray(name[0]) ? name : [name]) : Str(name).split(comaseparator);
  323. if (names.length > 1) {
  324. for (var i = 0, ii = names.length; i < ii; i++) {
  325. eve.off(names[i], f);
  326. }
  327. return;
  328. }
  329. names = isArray(name) ? name : Str(name).split(separator);
  330. var e,
  331. key,
  332. splice,
  333. i, ii, j, jj,
  334. cur = [events];
  335. for (i = 0, ii = names.length; i < ii; i++) {
  336. for (j = 0; j < cur.length; j += splice.length - 2) {
  337. splice = [j, 1];
  338. e = cur[j].n;
  339. if (names[i] != wildcard) {
  340. if (e[names[i]]) {
  341. splice.push(e[names[i]]);
  342. }
  343. } else {
  344. for (key in e) if (e[has](key)) {
  345. splice.push(e[key]);
  346. }
  347. }
  348. cur.splice.apply(cur, splice);
  349. }
  350. }
  351. for (i = 0, ii = cur.length; i < ii; i++) {
  352. e = cur[i];
  353. while (e.n) {
  354. if (f) {
  355. if (e.f) {
  356. for (j = 0, jj = e.f.length; j < jj; j++) if (e.f[j] == f) {
  357. e.f.splice(j, 1);
  358. break;
  359. }
  360. !e.f.length && delete e.f;
  361. }
  362. for (key in e.n) if (e.n[has](key) && e.n[key].f) {
  363. var funcs = e.n[key].f;
  364. for (j = 0, jj = funcs.length; j < jj; j++) if (funcs[j] == f) {
  365. funcs.splice(j, 1);
  366. break;
  367. }
  368. !funcs.length && delete e.n[key].f;
  369. }
  370. } else {
  371. delete e.f;
  372. for (key in e.n) if (e.n[has](key) && e.n[key].f) {
  373. delete e.n[key].f;
  374. }
  375. }
  376. e = e.n;
  377. }
  378. }
  379. };
  380. /*\
  381. * eve.once
  382. [ method ]
  383. **
  384. * Binds given event handler with a given name to only run once then unbind itself.
  385. | eve.once("login", f);
  386. | eve("login"); // triggers f
  387. | eve("login"); // no listeners
  388. * Use @eve to trigger the listener.
  389. **
  390. > Arguments
  391. **
  392. - name (string) name of the event, dot (`.`) or slash (`/`) separated, with optional wildcards
  393. - f (function) event handler function
  394. **
  395. = (function) same return function as @eve.on
  396. \*/
  397. eve.once = function (name, f) {
  398. var f2 = function () {
  399. eve.off(name, f2);
  400. return f.apply(this, arguments);
  401. };
  402. return eve.on(name, f2);
  403. };
  404. /*\
  405. * eve.version
  406. [ property (string) ]
  407. **
  408. * Current version of the library.
  409. \*/
  410. eve.version = version;
  411. eve.toString = function () {
  412. return "You are running Eve " + version;
  413. };
  414. (typeof module != "undefined" && module.exports) ? (module.exports = eve) : (typeof define === "function" && define.amd ? (define("eve", [], function() { return eve; })) : (glob.eve = eve));
  415. })(this);