From d7d94216bc7217ccec0e34e4fe3dcd820bb7294b Mon Sep 17 00:00:00 2001 From: Peter Dave Hello Date: Wed, 5 Mar 2014 17:07:50 +0800 Subject: [PATCH] add intercom.js v0.1.4 --- ajax/libs/intercom.js/0.1.4/intercom.js | 413 ++++++++++++++++++++ ajax/libs/intercom.js/0.1.4/intercom.min.js | 12 + ajax/libs/intercom.js/package.json | 24 ++ 3 files changed, 449 insertions(+) create mode 100644 ajax/libs/intercom.js/0.1.4/intercom.js create mode 100644 ajax/libs/intercom.js/0.1.4/intercom.min.js create mode 100644 ajax/libs/intercom.js/package.json diff --git a/ajax/libs/intercom.js/0.1.4/intercom.js b/ajax/libs/intercom.js/0.1.4/intercom.js new file mode 100644 index 000000000..ac61ecd37 --- /dev/null +++ b/ajax/libs/intercom.js/0.1.4/intercom.js @@ -0,0 +1,413 @@ +/*! intercom.js | https://github.com/diy/intercom.js | Apache License (v2) */ + +var Intercom = (function() { + + // --- lib/events.js --- + + var EventEmitter = function() {}; + + EventEmitter.createInterface = function(space) { + var methods = {}; + + methods.on = function(name, fn) { + if (typeof this[space] === 'undefined') { + this[space] = {}; + } + if (!this[space].hasOwnProperty(name)) { + this[space][name] = []; + } + this[space][name].push(fn); + }; + + methods.off = function(name, fn) { + if (typeof this[space] === 'undefined') return; + if (this[space].hasOwnProperty(name)) { + util.removeItem(fn, this[space][name]); + } + }; + + methods.trigger = function(name) { + if (typeof this[space] !== 'undefined' && this[space].hasOwnProperty(name)) { + var args = Array.prototype.slice.call(arguments, 1); + for (var i = 0; i < this[space][name].length; i++) { + this[space][name][i].apply(this[space][name][i], args); + } + } + }; + + return methods; + }; + + var pvt = EventEmitter.createInterface('_handlers'); + EventEmitter.prototype._on = pvt.on; + EventEmitter.prototype._off = pvt.off; + EventEmitter.prototype._trigger = pvt.trigger; + + var pub = EventEmitter.createInterface('handlers'); + EventEmitter.prototype.on = function() { + pub.on.apply(this, arguments); + Array.prototype.unshift.call(arguments, 'on'); + this._trigger.apply(this, arguments); + }; + EventEmitter.prototype.off = pub.off; + EventEmitter.prototype.trigger = pub.trigger; + + // --- lib/localstorage.js --- + + var localStorage = window.localStorage; + if (typeof localStorage === 'undefined') { + localStorage = { + getItem : function() {}, + setItem : function() {}, + removeItem : function() {} + }; + } + + // --- lib/util.js --- + + var util = {}; + + util.guid = (function() { + var S4 = function() { + return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); + }; + return function() { + return S4() + S4() + '-' + S4() + '-' + S4() + '-' + S4() + '-' + S4() + S4() + S4(); + }; + })(); + + util.throttle = function(delay, fn) { + var last = 0; + return function() { + var now = (new Date()).getTime(); + if (now - last > delay) { + last = now; + fn.apply(this, arguments); + } + }; + }; + + util.extend = function(a, b) { + if (typeof a === 'undefined' || !a) { a = {}; } + if (typeof b === 'object') { + for (var key in b) { + if (b.hasOwnProperty(key)) { + a[key] = b[key]; + } + } + } + return a; + }; + + util.removeItem = function(item, array) { + for (var i = array.length - 1; i >= 0; i--) { + if (array[i] === item) { + array.splice(i, 1); + } + } + return array; + }; + + // --- lib/intercom.js --- + + /** + * A cross-window broadcast service built on top + * of the HTML5 localStorage API. The interface + * mimic socket.io in design. + * + * @author Brian Reavis + * @constructor + */ + + var Intercom = function() { + var self = this; + var now = (new Date()).getTime(); + + this.origin = util.guid(); + this.lastMessage = now; + this.bindings = []; + this.receivedIDs = {}; + this.previousValues = {}; + + var storageHandler = function() { self._onStorageEvent.apply(self, arguments); }; + if (window.attachEvent) { document.attachEvent('onstorage', storageHandler); } + else { window.addEventListener('storage', storageHandler, false); }; + }; + + Intercom.prototype._transaction = function(fn) { + var TIMEOUT = 1000; + var WAIT = 20; + + var self = this; + var executed = false; + var listening = false; + var waitTimer = null; + + var lock = function() { + if (executed) return; + + var now = (new Date()).getTime(); + var activeLock = parseInt(localStorage.getItem(INDEX_LOCK) || 0); + if (activeLock && now - activeLock < TIMEOUT) { + if (!listening) { + self._on('storage', lock); + listening = true; + } + waitTimer = window.setTimeout(lock, WAIT); + return; + } + executed = true; + localStorage.setItem(INDEX_LOCK, now); + + fn(); + unlock(); + }; + + var unlock = function() { + if (listening) { self._off('storage', lock); } + if (waitTimer) { window.clearTimeout(waitTimer); } + localStorage.removeItem(INDEX_LOCK); + }; + + lock(); + }; + + Intercom.prototype._cleanup_emit = util.throttle(100, function() { + var self = this; + + this._transaction(function() { + var now = (new Date()).getTime(); + var threshold = now - THRESHOLD_TTL_EMIT; + var changed = 0; + + var messages = JSON.parse(localStorage.getItem(INDEX_EMIT) || '[]'); + for (var i = messages.length - 1; i >= 0; i--) { + if (messages[i].timestamp < threshold) { + messages.splice(i, 1); + changed++; + } + } + if (changed > 0) { + localStorage.setItem(INDEX_EMIT, JSON.stringify(messages)); + } + }); + }); + + Intercom.prototype._cleanup_once = util.throttle(100, function() { + var self = this; + + this._transaction(function() { + var timestamp, ttl, key; + var table = JSON.parse(localStorage.getItem(INDEX_ONCE) || '{}'); + var now = (new Date()).getTime(); + var changed = 0; + + for (key in table) { + if (self._once_expired(key, table)) { + delete table[key]; + changed++; + } + } + + if (changed > 0) { + localStorage.setItem(INDEX_ONCE, JSON.stringify(table)); + } + }); + }); + + Intercom.prototype._once_expired = function(key, table) { + if (!table) return true; + if (!table.hasOwnProperty(key)) return true; + if (typeof table[key] !== 'object') return true; + var ttl = table[key].ttl || THRESHOLD_TTL_ONCE; + var now = (new Date()).getTime(); + var timestamp = table[key].timestamp; + return timestamp < now - ttl; + }; + + Intercom.prototype._localStorageChanged = function(event, field) { + if (event && event.key) { + return event.key === field; + } + + var currentValue = localStorage.getItem(field); + if (currentValue === this.previousValues[field]) { + return false; + } + this.previousValues[field] = currentValue; + return true; + }; + + Intercom.prototype._onStorageEvent = function(event) { + event = event || window.event; + var self = this; + + if (this._localStorageChanged(event, INDEX_EMIT)) { + this._transaction(function() { + var now = (new Date()).getTime(); + var data = localStorage.getItem(INDEX_EMIT); + var messages = JSON.parse(data || '[]'); + for (var i = 0; i < messages.length; i++) { + if (messages[i].origin === self.origin) continue; + if (messages[i].timestamp < self.lastMessage) continue; + if (messages[i].id) { + if (self.receivedIDs.hasOwnProperty(messages[i].id)) continue; + self.receivedIDs[messages[i].id] = true; + } + self.trigger(messages[i].name, messages[i].payload); + } + self.lastMessage = now; + }); + } + + this._trigger('storage', event); + }; + + Intercom.prototype._emit = function(name, message, id) { + id = (typeof id === 'string' || typeof id === 'number') ? String(id) : null; + if (id && id.length) { + if (this.receivedIDs.hasOwnProperty(id)) return; + this.receivedIDs[id] = true; + } + + var packet = { + id : id, + name : name, + origin : this.origin, + timestamp : (new Date()).getTime(), + payload : message + }; + + var self = this; + this._transaction(function() { + var data = localStorage.getItem(INDEX_EMIT) || '[]'; + var delimiter = (data === '[]') ? '' : ','; + data = [data.substring(0, data.length - 1), delimiter, JSON.stringify(packet), ']'].join(''); + localStorage.setItem(INDEX_EMIT, data); + self.trigger(name, message); + + window.setTimeout(function() { self._cleanup_emit(); }, 50); + }); + }; + + Intercom.prototype.bind = function(object, options) { + for (var i = 0; i < Intercom.bindings.length; i++) { + var binding = Intercom.bindings[i].factory(object, options || null, this); + if (binding) { this.bindings.push(binding); } + } + }; + + Intercom.prototype.emit = function(name, message) { + this._emit.apply(this, arguments); + this._trigger('emit', name, message); + }; + + Intercom.prototype.once = function(key, fn, ttl) { + if (!Intercom.supported) return; + + var self = this; + this._transaction(function() { + var data = JSON.parse(localStorage.getItem(INDEX_ONCE) || '{}'); + if (!self._once_expired(key, data)) return; + + data[key] = {}; + data[key].timestamp = (new Date()).getTime(); + if (typeof ttl === 'number') { + data[key].ttl = ttl * 1000; + } + + localStorage.setItem(INDEX_ONCE, JSON.stringify(data)); + fn(); + + window.setTimeout(function() { self._cleanup_once(); }, 50); + }); + }; + + util.extend(Intercom.prototype, EventEmitter.prototype); + + Intercom.bindings = []; + Intercom.supported = (typeof localStorage !== 'undefined'); + + var INDEX_EMIT = 'intercom'; + var INDEX_ONCE = 'intercom_once'; + var INDEX_LOCK = 'intercom_lock'; + + var THRESHOLD_TTL_EMIT = 50000; + var THRESHOLD_TTL_ONCE = 1000 * 3600; + + Intercom.destroy = function() { + localStorage.removeItem(INDEX_LOCK); + localStorage.removeItem(INDEX_EMIT); + localStorage.removeItem(INDEX_ONCE); + }; + + Intercom.getInstance = (function() { + var intercom = null; + return function() { + if (!intercom) { + intercom = new Intercom(); + } + return intercom; + }; + })(); + + // --- lib/bindings/socket.js --- + + /** + * Socket.io binding for intercom.js. + * + * - When a message is received on the socket, it's emitted on intercom. + * - When a message is emitted via intercom, it's sent over the socket. + * + * @author Brian Reavis + */ + + var SocketBinding = function(socket, options, intercom) { + options = util.extend({ + id : null, + send : true, + receive : true + }, options); + + if (options.receive) { + var watchedEvents = []; + var onEventAdded = function(name, fn) { + if (watchedEvents.indexOf(name) === -1) { + watchedEvents.push(name); + socket.on(name, function(data) { + var id = (typeof options.id === 'function') ? options.id(name, data) : null; + var emit = (typeof options.receive === 'function') ? options.receive(name, data) : true; + if (emit || typeof emit !== 'boolean') { + intercom._emit(name, data, id); + } + }); + } + }; + + for (var name in intercom.handlers) { + for (var i = 0; i < intercom.handlers[name].length; i++) { + onEventAdded(name, intercom.handlers[name][i]); + } + } + + intercom._on('on', onEventAdded); + } + + if (options.send) { + intercom._on('emit', function(name, message) { + var emit = (typeof options.send === 'function') ? options.send(name, message) : true; + if (emit || typeof emit !== 'boolean') { + socket.emit(name, message); + } + }); + } + }; + + SocketBinding.factory = function(object, options, intercom) { + if (typeof object.socket === 'undefined') { return false }; + return new SocketBinding(object, options, intercom); + }; + + Intercom.bindings.push(SocketBinding); + return Intercom; +})(); diff --git a/ajax/libs/intercom.js/0.1.4/intercom.min.js b/ajax/libs/intercom.js/0.1.4/intercom.min.js new file mode 100644 index 000000000..eae56df6e --- /dev/null +++ b/ajax/libs/intercom.js/0.1.4/intercom.min.js @@ -0,0 +1,12 @@ +/*! intercom.js | https://github.com/diy/intercom.js | Apache License (v2) */ +var Intercom=function(){var g=function(){};g.createInterface=function(b){return{on:function(a,c){"undefined"===typeof this[b]&&(this[b]={});this[b].hasOwnProperty(a)||(this[b][a]=[]);this[b][a].push(c)},off:function(a,c){"undefined"!==typeof this[b]&&this[b].hasOwnProperty(a)&&i.removeItem(c,this[b][a])},trigger:function(a){if("undefined"!==typeof this[b]&&this[b].hasOwnProperty(a))for(var c=Array.prototype.slice.call(arguments,1),e=0;eb&&(c=e,a.apply(this,arguments))}};i.extend=function(b,a){if("undefined"===typeof b||!b)b={};if("object"===typeof a)for(var c in a)a.hasOwnProperty(c)&&(b[c]=a[c]);return b};i.removeItem=function(b,a){for(var c=a.length-1;0<=c;c--)a[c]===b&&a.splice(c,1);return a};var d=function(){var b=this,a=(new Date).getTime(); +this.origin=i.guid();this.lastMessage=a;this.bindings=[];this.receivedIDs={};this.previousValues={};a=function(){b._onStorageEvent.apply(b,arguments)};window.attachEvent?document.attachEvent("onstorage",a):window.addEventListener("storage",a,!1)};d.prototype._transaction=function(b){var a=this,c=!1,e=!1,p=null,d=function(){if(!c){var g=(new Date).getTime(),s=parseInt(f.getItem(l)||0);s&&1E3>g-s?(e||(a._on("storage",d),e=!0),p=window.setTimeout(d,20)):(c=!0,f.setItem(l,g),b(),e&&a._off("storage",d), +p&&window.clearTimeout(p),f.removeItem(l))}};d()};d.prototype._cleanup_emit=i.throttle(100,function(){this._transaction(function(){for(var b=(new Date).getTime()-t,a=0,c=JSON.parse(f.getItem(j)||"[]"),e=c.length-1;0<=e;e--)c[e].timestamp