PageRenderTime 25ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/ajax/libs/stapes/0.4.0/stapes.js

https://gitlab.com/Mirros/cdnjs
JavaScript | 472 lines | 333 code | 83 blank | 56 comment | 60 complexity | 0d1cb0f83d8c6e175ca4277c073d0278 MD5 | raw file
  1. //
  2. // ____ _ _
  3. // / ___|| |_ __ _ _ __ ___ ___ (_)___ (*)
  4. // \___ \| __/ _` | '_ \ / _ \/ __| | / __|
  5. // ___) | || (_| | |_) | __/\__ \_ | \__ \
  6. // |____/ \__\__,_| .__/ \___||___(_)/ |___/
  7. // |_| |__/
  8. //
  9. // (*) a (really) tiny Javascript MVC microframework
  10. //
  11. // (c) Hay Kranen < hay@bykr.org >
  12. // Released under the terms of the MIT license
  13. // < http://en.wikipedia.org/wiki/MIT_License >
  14. //
  15. // Stapes.js : http://hay.github.com/stapes
  16. (function() {
  17. 'use strict';
  18. var VERSION = "0.4";
  19. /** Utility functions
  20. *
  21. * These are more or less modelled on the ones used in Underscore.js,
  22. * but might not be as extensive or failproof.
  23. * However, they are pretty damn useful and can be accessed by using
  24. * the Stapes.util global
  25. */
  26. var util = {
  27. "bind" : function(fn, ctx) {
  28. if (Function.prototype.bind) {
  29. // Native
  30. return fn.bind(ctx);
  31. } else {
  32. // Non-native
  33. return function() {
  34. return fn.apply(ctx, arguments);
  35. };
  36. }
  37. },
  38. "clone" : function(obj) {
  39. if (util.isArray(obj)) {
  40. return obj.slice();
  41. } else if (util.isObject(obj)) {
  42. var newObj = {};
  43. util.each(obj, function(value, key) {
  44. newObj[key] = value;
  45. });
  46. return newObj;
  47. } else {
  48. return obj;
  49. }
  50. },
  51. "create" : function(context) {
  52. var instance;
  53. if (typeof Object.create === "function") {
  54. // Native
  55. instance = Object.create(context);
  56. } else {
  57. // Non-native
  58. var F = function(){};
  59. F.prototype = context;
  60. instance = new F();
  61. }
  62. return instance;
  63. },
  64. "each" : function(list, fn, context) {
  65. if (util.isArray(list)) {
  66. if (Array.prototype.forEach) {
  67. // Native forEach
  68. list.forEach( fn, context );
  69. } else {
  70. for (var i = 0, l = list.length; i < l; i++) {
  71. fn.call(context, list[i], i);
  72. }
  73. }
  74. } else {
  75. for (var key in list) {
  76. fn.call(context, list[key], key);
  77. }
  78. }
  79. },
  80. "filter" : function(list, fn, context) {
  81. var results = [];
  82. if (util.isArray(list) && Array.prototype.filter) {
  83. return list.filter(fn, context);
  84. }
  85. util.each(list, function(value) {
  86. if (fn.call(context, value)) {
  87. results.push(value);
  88. }
  89. });
  90. return results;
  91. },
  92. "isArray" : function(val) {
  93. return util.typeOf(val) === "array";
  94. },
  95. "isObject" : function(val) {
  96. return util.typeOf(val) === "object";
  97. },
  98. "keys" : function(list) {
  99. return util.map(list, function(value, key) {
  100. return key;
  101. });
  102. },
  103. // from http://stackoverflow.com/a/2117523/152809
  104. "makeUuid" : function() {
  105. return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
  106. var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
  107. return v.toString(16);
  108. });
  109. },
  110. "map" : function(list, fn, context) {
  111. var results = [];
  112. if (util.isArray(list) && Array.prototype.map) {
  113. return list.map(fn, context);
  114. }
  115. util.each(list, function(value, index) {
  116. results.push( fn.call(context, value, index) );
  117. });
  118. return results;
  119. },
  120. "size" : function(list) {
  121. return (util.isArray(list)) ? list.length : util.keys(list).length;
  122. },
  123. "toArray" : function(val) {
  124. if (util.isObject(val)) {
  125. return util.values(val);
  126. } else {
  127. return Array.prototype.slice.call(val, 0);
  128. }
  129. },
  130. "typeOf" : function(val) {
  131. return Object.prototype.toString.call(val).replace(/\[object |\]/g, '').toLowerCase();
  132. },
  133. "values" : function(list) {
  134. return util.map(list, function(value, key) {
  135. return value;
  136. });
  137. }
  138. };
  139. /** Private helper functions */
  140. function addEvent(event) {
  141. // If we don't have any handlers for this type of event, add a new
  142. // array we can use to push new handlers
  143. if (!Stapes._eventHandlers[event.guid][event.type]) {
  144. Stapes._eventHandlers[event.guid][event.type] = [];
  145. }
  146. // Push an event object
  147. Stapes._eventHandlers[event.guid][event.type].push({
  148. "guid" : event.guid,
  149. "handler" : event.handler,
  150. "scope" : event.scope,
  151. "type" : event.type
  152. });
  153. }
  154. function addEventHandler(argTypeOrMap, argHandlerOrScope, argScope) {
  155. var eventMap = {},
  156. scope;
  157. if (typeof argTypeOrMap === "string") {
  158. scope = argScope || false;
  159. eventMap[ argTypeOrMap ] = argHandlerOrScope;
  160. } else {
  161. scope = argHandlerOrScope || false;
  162. eventMap = argTypeOrMap;
  163. }
  164. util.each(eventMap, function(handler, eventString) {
  165. var events = eventString.split(" ");
  166. util.each(events, function(eventType) {
  167. addEvent.call(this, {
  168. "guid" : this._guid,
  169. "handler" : handler,
  170. "scope" : scope,
  171. "type" : eventType
  172. });
  173. }, this);
  174. }, this);
  175. }
  176. // This is a really small utility function to save typing and producing
  177. // better optimized code
  178. function attr(guid) {
  179. return Stapes._attributes[guid];
  180. }
  181. // Stapes objects have some extra properties that are set on creation
  182. function createModule( context ) {
  183. var instance = util.create( context );
  184. instance._guid = guid++;
  185. Stapes._attributes[instance._guid] = {};
  186. Stapes._eventHandlers[instance._guid] = {};
  187. return instance;
  188. }
  189. function emitEvents(type, data, explicitType, explicitGuid) {
  190. explicitType = explicitType || false;
  191. explicitGuid = explicitGuid || this._guid;
  192. util.each(Stapes._eventHandlers[explicitGuid][type], function(event) {
  193. var scope = (event.scope) ? event.scope : this;
  194. if (explicitType) {
  195. event.type = explicitType;
  196. }
  197. event.scope = scope;
  198. event.handler.call(event.scope, data, event);
  199. }, this);
  200. }
  201. function setAttribute(key, value) {
  202. // We need to do this before we actually add the item :)
  203. var itemExists = this.has(key),
  204. oldValue = attr(this._guid)[key];
  205. // Is the value different than the oldValue? If not, ignore this call
  206. if (value === oldValue) {
  207. return;
  208. }
  209. // Actually add the item to the attributes
  210. attr(this._guid)[key] = value;
  211. // Throw a generic event
  212. this.emit('change', key);
  213. // And a namespaced event as well, NOTE that we pass value instead of
  214. // key here!
  215. this.emit('change:' + key, value);
  216. // Throw namespaced and non-namespaced 'mutate' events as well with
  217. // the old value data as well and some extra metadata such as the key
  218. var mutateData = {
  219. "key" : key,
  220. "newValue" : value,
  221. "oldValue" : oldValue || null
  222. };
  223. this.emit('mutate', mutateData);
  224. this.emit('mutate:' + key, mutateData);
  225. // Also throw a specific event for this type of set
  226. var specificEvent = itemExists ? 'update' : 'create';
  227. this.emit(specificEvent, key);
  228. // And a namespaced event as well, NOTE that we pass value instead of key
  229. this.emit(specificEvent + ':' + key, value);
  230. }
  231. function updateAttribute(key, fn) {
  232. var item = this.get(key),
  233. newValue = fn( util.clone(item) );
  234. setAttribute.call(this, key, newValue);
  235. }
  236. var guid = 1;
  237. var Module = {
  238. create : function() {
  239. return createModule( this );
  240. },
  241. each : function(fn, ctx) {
  242. util.each(attr(this._guid), fn, ctx || this);
  243. },
  244. emit : function(types, data) {
  245. data = (typeof data === "undefined") ? null : data;
  246. util.each(types.split(" "), function(type) {
  247. // First 'all' type events: is there an 'all' handler in the
  248. // global stack?
  249. if (Stapes._eventHandlers[-1].all) {
  250. emitEvents.call(this, "all", data, type, -1);
  251. }
  252. // Catch all events for this type?
  253. if (Stapes._eventHandlers[-1][type]) {
  254. emitEvents.call(this, type, data, type, -1);
  255. }
  256. if (typeof this._guid === 'number') {
  257. // 'all' event for this specific module?
  258. if (Stapes._eventHandlers[this._guid].all) {
  259. emitEvents.call(this, "all", data, type);
  260. }
  261. // Finally, normal events :)
  262. if (Stapes._eventHandlers[this._guid][type]) {
  263. emitEvents.call(this, type, data);
  264. }
  265. }
  266. }, this);
  267. },
  268. extend : function(objectOrValues, valuesIfObject) {
  269. var object = (valuesIfObject) ? objectOrValues : this,
  270. values = (valuesIfObject) ? valuesIfObject : objectOrValues;
  271. util.each(values, function(value, key) {
  272. object[key] = value;
  273. });
  274. return this;
  275. },
  276. filter : function(fn) {
  277. return util.filter(attr(this._guid), fn);
  278. },
  279. get : function(input) {
  280. if (typeof input === "string") {
  281. return this.has(input) ? attr(this._guid)[input] : null;
  282. } else if (typeof input === "function") {
  283. var items = this.filter(input);
  284. return (items.length) ? items[0] : null;
  285. }
  286. },
  287. getAll : function() {
  288. return util.clone( attr(this._guid) );
  289. },
  290. getAllAsArray : function() {
  291. var arr = util.map(attr(this._guid), function(value, key) {
  292. if (util.isObject(value)) {
  293. value.id = key;
  294. }
  295. return value;
  296. });
  297. return util.clone( arr );
  298. },
  299. has : function(key) {
  300. return (typeof attr(this._guid)[key] !== "undefined");
  301. },
  302. on : function() {
  303. addEventHandler.apply(this, arguments);
  304. },
  305. // Akin to set(), but makes a unique id
  306. push : function(input) {
  307. if (util.isArray(input)) {
  308. util.each(input, function(value) {
  309. setAttribute.call(this, util.makeUuid(), value);
  310. }, this);
  311. } else {
  312. setAttribute.call(this, util.makeUuid(), input);
  313. }
  314. },
  315. remove : function(input) {
  316. if (typeof input === "function") {
  317. this.each(function(item, key) {
  318. if (input(item)) {
  319. delete attr(this._guid)[key];
  320. this.emit('remove change');
  321. }
  322. });
  323. } else {
  324. if (this.has(input)) {
  325. delete attr(this._guid)[input];
  326. this.emit('remove change');
  327. }
  328. }
  329. },
  330. set : function(objOrKey, value) {
  331. if (util.isObject(objOrKey)) {
  332. util.each(objOrKey, function(value, key) {
  333. setAttribute.call(this, key, value);
  334. }, this);
  335. } else {
  336. setAttribute.call(this, objOrKey, value);
  337. }
  338. },
  339. size : function() {
  340. return util.size( Stapes._attributes[this._guid] );
  341. },
  342. update : function(keyOrFn, fn) {
  343. if (typeof keyOrFn === "string") {
  344. updateAttribute.call(this, keyOrFn, fn);
  345. } else if (typeof keyOrFn === "function") {
  346. this.each(function(value, key) {
  347. updateAttribute.call(this, key, keyOrFn);
  348. });
  349. }
  350. }
  351. };
  352. var Stapes = {
  353. "_attributes" : {},
  354. "_eventHandlers" : {
  355. "-1" : {} // '-1' is used for the global event handling
  356. },
  357. "_guid" : -1,
  358. "create" : function() {
  359. return createModule( Module );
  360. },
  361. "extend" : function(obj) {
  362. util.each(obj, function(value, key) {
  363. Module[key] = value;
  364. });
  365. },
  366. "on" : function() {
  367. addEventHandler.apply(this, arguments);
  368. },
  369. "util" : util,
  370. "version" : VERSION
  371. };
  372. // This library can be used as an AMD module, a Node.js module, or an
  373. // old fashioned global
  374. if (typeof exports !== "undefined") {
  375. // Server
  376. if (typeof module !== "undefined" && module.exports) {
  377. exports = module.exports = Stapes;
  378. }
  379. exports.Stapes = Stapes;
  380. } else if (typeof define === "function" && define.amd) {
  381. // AMD
  382. define(function() {
  383. return Stapes;
  384. });
  385. } else {
  386. // Global scope
  387. window.Stapes = Stapes;
  388. }
  389. })();