PageRenderTime 67ms CodeModel.GetById 23ms RepoModel.GetById 0ms app.codeStats 0ms

/ajax/libs//0.8.4/postal.js

https://gitlab.com/Mirros/cdnjs
JavaScript | 424 lines | 378 code | 37 blank | 9 comment | 54 complexity | f67247d750d7f023eabdc1604632af02 MD5 | raw file
  1. /*
  2. postal
  3. Author: Jim Cowart (http://freshbrewedcode.com/jimcowart)
  4. License: Dual licensed MIT (http://www.opensource.org/licenses/mit-license) & GPL (http://www.opensource.org/licenses/gpl-license)
  5. Version 0.8.4
  6. */
  7. (function ( root, factory ) {
  8. if ( typeof module === "object" && module.exports ) {
  9. // Node, or CommonJS-Like environments
  10. module.exports = function ( _ ) {
  11. _ = _ || require( "underscore" );
  12. return factory( _ );
  13. }
  14. } else if ( typeof define === "function" && define.amd ) {
  15. // AMD. Register as an anonymous module.
  16. define( ["underscore"], function ( _ ) {
  17. return factory( _, root );
  18. } );
  19. } else {
  20. // Browser globals
  21. root.postal = factory( root._, root );
  22. }
  23. }( this, function ( _, global, undefined ) {
  24. var DEFAULT_CHANNEL = "/",
  25. DEFAULT_DISPOSEAFTER = 0,
  26. SYSTEM_CHANNEL = "postal";
  27. var ConsecutiveDistinctPredicate = function () {
  28. var previous;
  29. return function ( data ) {
  30. var eq = false;
  31. if ( _.isString( data ) ) {
  32. eq = data === previous;
  33. previous = data;
  34. }
  35. else {
  36. eq = _.isEqual( data, previous );
  37. previous = _.clone( data );
  38. }
  39. return !eq;
  40. };
  41. };
  42. var DistinctPredicate = function () {
  43. var previous = [];
  44. return function ( data ) {
  45. var isDistinct = !_.any( previous, function ( p ) {
  46. if ( _.isObject( data ) || _.isArray( data ) ) {
  47. return _.isEqual( data, p );
  48. }
  49. return data === p;
  50. } );
  51. if ( isDistinct ) {
  52. previous.push( data );
  53. }
  54. return isDistinct;
  55. };
  56. };
  57. var ChannelDefinition = function ( channelName ) {
  58. this.channel = channelName || DEFAULT_CHANNEL;
  59. };
  60. ChannelDefinition.prototype.subscribe = function () {
  61. return arguments.length === 1 ?
  62. new SubscriptionDefinition( this.channel, arguments[0].topic, arguments[0].callback ) :
  63. new SubscriptionDefinition( this.channel, arguments[0], arguments[1] );
  64. };
  65. ChannelDefinition.prototype.publish = function () {
  66. var envelope = arguments.length === 1 ?
  67. (Object.prototype.toString.call(arguments[0]) === '[object String]' ?
  68. { topic: arguments[0] } : arguments[0]) : { topic : arguments[0], data : arguments[1] };
  69. envelope.channel = this.channel;
  70. return postal.configuration.bus.publish( envelope );
  71. };
  72. var SubscriptionDefinition = function ( channel, topic, callback ) {
  73. this.channel = channel;
  74. this.topic = topic;
  75. this.callback = callback;
  76. this.constraints = [];
  77. this.context = null;
  78. postal.configuration.bus.publish( {
  79. channel : SYSTEM_CHANNEL,
  80. topic : "subscription.created",
  81. data : {
  82. event : "subscription.created",
  83. channel : channel,
  84. topic : topic
  85. }
  86. } );
  87. postal.configuration.bus.subscribe( this );
  88. };
  89. SubscriptionDefinition.prototype = {
  90. unsubscribe : function () {
  91. this.inactive = true;
  92. postal.configuration.bus.unsubscribe( this );
  93. postal.configuration.bus.publish( {
  94. channel : SYSTEM_CHANNEL,
  95. topic : "subscription.removed",
  96. data : {
  97. event : "subscription.removed",
  98. channel : this.channel,
  99. topic : this.topic
  100. }
  101. } );
  102. },
  103. defer : function () {
  104. var fn = this.callback;
  105. this.callback = function ( data ) {
  106. setTimeout( function () {
  107. fn( data );
  108. }, 0 );
  109. };
  110. return this;
  111. },
  112. disposeAfter : function ( maxCalls ) {
  113. if ( _.isNaN( maxCalls ) || maxCalls <= 0 ) {
  114. throw "The value provided to disposeAfter (maxCalls) must be a number greater than zero.";
  115. }
  116. var fn = this.callback;
  117. var dispose = _.after( maxCalls, _.bind( function () {
  118. this.unsubscribe();
  119. }, this ) );
  120. this.callback = function () {
  121. fn.apply( this.context, arguments );
  122. dispose();
  123. };
  124. return this;
  125. },
  126. distinctUntilChanged : function () {
  127. this.withConstraint( new ConsecutiveDistinctPredicate() );
  128. return this;
  129. },
  130. distinct : function () {
  131. this.withConstraint( new DistinctPredicate() );
  132. return this;
  133. },
  134. once : function () {
  135. this.disposeAfter( 1 );
  136. return this;
  137. },
  138. withConstraint : function ( predicate ) {
  139. if ( !_.isFunction( predicate ) ) {
  140. throw "Predicate constraint must be a function";
  141. }
  142. this.constraints.push( predicate );
  143. return this;
  144. },
  145. withConstraints : function ( predicates ) {
  146. var self = this;
  147. if ( _.isArray( predicates ) ) {
  148. _.each( predicates, function ( predicate ) {
  149. self.withConstraint( predicate );
  150. } );
  151. }
  152. return self;
  153. },
  154. withContext : function ( context ) {
  155. this.context = context;
  156. return this;
  157. },
  158. withDebounce : function ( milliseconds ) {
  159. if ( _.isNaN( milliseconds ) ) {
  160. throw "Milliseconds must be a number";
  161. }
  162. var fn = this.callback;
  163. this.callback = _.debounce( fn, milliseconds );
  164. return this;
  165. },
  166. withDelay : function ( milliseconds ) {
  167. if ( _.isNaN( milliseconds ) ) {
  168. throw "Milliseconds must be a number";
  169. }
  170. var fn = this.callback;
  171. this.callback = function ( data ) {
  172. setTimeout( function () {
  173. fn( data );
  174. }, milliseconds );
  175. };
  176. return this;
  177. },
  178. withThrottle : function ( milliseconds ) {
  179. if ( _.isNaN( milliseconds ) ) {
  180. throw "Milliseconds must be a number";
  181. }
  182. var fn = this.callback;
  183. this.callback = _.throttle( fn, milliseconds );
  184. return this;
  185. },
  186. subscribe : function ( callback ) {
  187. this.callback = callback;
  188. return this;
  189. }
  190. };
  191. var bindingsResolver = {
  192. cache : {},
  193. regex : {},
  194. compare : function ( binding, topic ) {
  195. var pattern, rgx, prevSegment, result = (this.cache[topic] && this.cache[topic][binding]);
  196. if(typeof result !== "undefined") {
  197. return result;
  198. }
  199. if(!(rgx = this.regex[binding])) {
  200. pattern = "^" + _.map(binding.split('.'), function(segment) {
  201. var res = "";
  202. if (!!prevSegment) {
  203. res = prevSegment !== "#" ? "\\.\\b" : "\\b";
  204. }
  205. if(segment === "#") {
  206. res += "[\\s\\S]*"
  207. } else if (segment === "*") {
  208. res += "[^.]+"
  209. } else {
  210. res += segment;
  211. }
  212. prevSegment = segment;
  213. return res;
  214. } ).join('') + "$";
  215. rgx = this.regex[binding] = new RegExp( pattern );
  216. }
  217. this.cache[topic] = this.cache[topic] || {};
  218. this.cache[topic][binding] = result = rgx.test( topic );
  219. return result;
  220. },
  221. reset : function () {
  222. this.cache = {};
  223. this.regex = {};
  224. }
  225. };
  226. var fireSub = function(subDef, envelope) {
  227. if ( !subDef.inactive && postal.configuration.resolver.compare( subDef.topic, envelope.topic ) ) {
  228. if ( _.all( subDef.constraints, function ( constraint ) {
  229. return constraint.call( subDef.context, envelope.data, envelope );
  230. } ) ) {
  231. if ( typeof subDef.callback === 'function' ) {
  232. subDef.callback.call( subDef.context, envelope.data, envelope );
  233. }
  234. }
  235. }
  236. };
  237. var pubInProgress = false;
  238. var unSubQueue = [];
  239. var clearUnSubQueue = function() {
  240. while(unSubQueue.length) {
  241. unSubQueue.shift().unsubscribe();
  242. }
  243. };
  244. var localBus = {
  245. addWireTap : function ( callback ) {
  246. var self = this;
  247. self.wireTaps.push( callback );
  248. return function () {
  249. var idx = self.wireTaps.indexOf( callback );
  250. if ( idx !== -1 ) {
  251. self.wireTaps.splice( idx, 1 );
  252. }
  253. };
  254. },
  255. publish : function ( envelope ) {
  256. pubInProgress = true;
  257. envelope.timeStamp = new Date();
  258. _.each( this.wireTaps, function ( tap ) {
  259. tap( envelope.data, envelope );
  260. } );
  261. if ( this.subscriptions[envelope.channel] ) {
  262. _.each( this.subscriptions[envelope.channel], function ( subscribers ) {
  263. var idx = 0, len = subscribers.length, subDef;
  264. while ( idx < len ) {
  265. if ( subDef = subscribers[idx++] ) {
  266. fireSub( subDef, envelope );
  267. }
  268. }
  269. } );
  270. }
  271. pubInProgress = false;
  272. return envelope;
  273. },
  274. reset : function () {
  275. if ( this.subscriptions ) {
  276. _.each( this.subscriptions, function ( channel ) {
  277. _.each( channel, function ( topic ) {
  278. while ( topic.length ) {
  279. topic.pop().unsubscribe();
  280. }
  281. } );
  282. } );
  283. this.subscriptions = {};
  284. }
  285. },
  286. subscribe : function ( subDef ) {
  287. var idx, found, fn, channel = this.subscriptions[subDef.channel], subs;
  288. if ( !channel ) {
  289. channel = this.subscriptions[subDef.channel] = {};
  290. }
  291. subs = this.subscriptions[subDef.channel][subDef.topic];
  292. if ( !subs ) {
  293. subs = this.subscriptions[subDef.channel][subDef.topic] = [];
  294. }
  295. subs.push( subDef );
  296. return subDef;
  297. },
  298. subscriptions : {},
  299. wireTaps : [],
  300. unsubscribe : function ( config ) {
  301. if(pubInProgress) {
  302. unSubQueue.push(config);
  303. return;
  304. }
  305. if ( this.subscriptions[config.channel][config.topic] ) {
  306. var len = this.subscriptions[config.channel][config.topic].length,
  307. idx = 0;
  308. while(idx < len) {
  309. if ( this.subscriptions[config.channel][config.topic][idx] === config ) {
  310. this.subscriptions[config.channel][config.topic].splice( idx, 1 );
  311. break;
  312. }
  313. idx += 1;
  314. }
  315. }
  316. }
  317. };
  318. localBus.subscriptions[SYSTEM_CHANNEL] = {};
  319. var postal = {
  320. configuration : {
  321. bus : localBus,
  322. resolver : bindingsResolver,
  323. DEFAULT_CHANNEL : DEFAULT_CHANNEL,
  324. SYSTEM_CHANNEL : SYSTEM_CHANNEL
  325. },
  326. ChannelDefinition : ChannelDefinition,
  327. SubscriptionDefinition : SubscriptionDefinition,
  328. channel : function ( channelName ) {
  329. return new ChannelDefinition( channelName );
  330. },
  331. subscribe : function ( options ) {
  332. return new SubscriptionDefinition( options.channel || DEFAULT_CHANNEL, options.topic, options.callback );
  333. },
  334. publish : function ( envelope ) {
  335. envelope.channel = envelope.channel || DEFAULT_CHANNEL;
  336. return postal.configuration.bus.publish( envelope );
  337. },
  338. addWireTap : function ( callback ) {
  339. return this.configuration.bus.addWireTap( callback );
  340. },
  341. linkChannels : function ( sources, destinations ) {
  342. var result = [];
  343. sources = !_.isArray( sources ) ? [sources] : sources;
  344. destinations = !_.isArray( destinations ) ? [destinations] : destinations;
  345. _.each( sources, function ( source ) {
  346. var sourceTopic = source.topic || "#";
  347. _.each( destinations, function ( destination ) {
  348. var destChannel = destination.channel || DEFAULT_CHANNEL;
  349. result.push(
  350. postal.subscribe( {
  351. channel : source.channel || DEFAULT_CHANNEL,
  352. topic : source.topic || "#",
  353. callback : function ( data, env ) {
  354. var newEnv = _.clone( env );
  355. newEnv.topic = _.isFunction( destination.topic ) ? destination.topic( env.topic ) : destination.topic || env.topic;
  356. newEnv.channel = destChannel;
  357. newEnv.data = data;
  358. postal.publish( newEnv );
  359. }
  360. } )
  361. );
  362. } );
  363. } );
  364. return result;
  365. },
  366. utils : {
  367. getSubscribersFor : function () {
  368. var channel = arguments[ 0 ],
  369. tpc = arguments[ 1 ];
  370. if ( arguments.length === 1 ) {
  371. channel = arguments[ 0 ].channel || postal.configuration.DEFAULT_CHANNEL;
  372. tpc = arguments[ 0 ].topic;
  373. }
  374. if ( postal.configuration.bus.subscriptions[ channel ] &&
  375. postal.configuration.bus.subscriptions[ channel ].hasOwnProperty( tpc ) ) {
  376. return postal.configuration.bus.subscriptions[ channel ][ tpc ];
  377. }
  378. return [];
  379. },
  380. reset : function () {
  381. postal.configuration.bus.reset();
  382. postal.configuration.resolver.reset();
  383. }
  384. }
  385. };
  386. return postal;
  387. } ));