| 'use strict'; |
| |
| var EventEmitter = require('events').EventEmitter; |
| var inherits = require('inherits'); |
| var isChromeApp = require('./deps/env/isChromeApp'); |
| var hasLocalStorage = require('./deps/env/hasLocalStorage'); |
| var pick = require('./deps/pick'); |
| var call = require('./deps/call'); |
| |
| inherits(Changes, EventEmitter); |
| |
| /* istanbul ignore next */ |
| function attachBrowserEvents(self) { |
| if (isChromeApp()) { |
| chrome.storage.onChanged.addListener(function (e) { |
| // make sure it's event addressed to us |
| if (e.db_name != null) { |
| //object only has oldValue, newValue members |
| self.emit(e.dbName.newValue); |
| } |
| }); |
| } else if (hasLocalStorage()) { |
| if (typeof addEventListener !== 'undefined') { |
| addEventListener("storage", function (e) { |
| self.emit(e.key); |
| }); |
| } else { // old IE |
| window.attachEvent("storage", function (e) { |
| self.emit(e.key); |
| }); |
| } |
| } |
| } |
| |
| function Changes() { |
| EventEmitter.call(this); |
| this._listeners = {}; |
| |
| attachBrowserEvents(this); |
| } |
| Changes.prototype.addListener = function (dbName, id, db, opts) { |
| if (this._listeners[id]) { |
| return; |
| } |
| var self = this; |
| var inprogress = false; |
| function eventFunction() { |
| if (!self._listeners[id]) { |
| return; |
| } |
| if (inprogress) { |
| inprogress = 'waiting'; |
| return; |
| } |
| inprogress = true; |
| var changesOpts = pick(opts, [ |
| 'style', 'include_docs', 'attachments', 'conflicts', 'filter', |
| 'doc_ids', 'view', 'since', 'query_params', 'binary' |
| ]); |
| |
| db.changes(changesOpts).on('change', function (c) { |
| if (c.seq > opts.since && !opts.cancelled) { |
| opts.since = c.seq; |
| call(opts.onChange, c); |
| } |
| }).on('complete', function () { |
| if (inprogress === 'waiting') { |
| setTimeout(function(){ |
| eventFunction(); |
| },0); |
| } |
| inprogress = false; |
| }).on('error', function () { |
| inprogress = false; |
| }); |
| } |
| this._listeners[id] = eventFunction; |
| this.on(dbName, eventFunction); |
| }; |
| |
| Changes.prototype.removeListener = function (dbName, id) { |
| if (!(id in this._listeners)) { |
| return; |
| } |
| EventEmitter.prototype.removeListener.call(this, dbName, |
| this._listeners[id]); |
| }; |
| |
| |
| /* istanbul ignore next */ |
| Changes.prototype.notifyLocalWindows = function (dbName) { |
| //do a useless change on a storage thing |
| //in order to get other windows's listeners to activate |
| if (isChromeApp()) { |
| chrome.storage.local.set({dbName: dbName}); |
| } else if (hasLocalStorage()) { |
| localStorage[dbName] = (localStorage[dbName] === "a") ? "b" : "a"; |
| } |
| }; |
| |
| Changes.prototype.notify = function (dbName) { |
| this.emit(dbName); |
| this.notifyLocalWindows(dbName); |
| }; |
| |
| module.exports = Changes; |