| 'use strict'; |
| var utils = require('./utils'); |
| var replicate = require('./replicate').replicate; |
| var EE = require('events').EventEmitter; |
| |
| module.exports = Sync; |
| utils.inherits(Sync, EE); |
| function Sync(src, target, opts, callback) { |
| if (!(this instanceof Sync)) { |
| return new Sync(src, target, opts, callback); |
| } |
| var self = this; |
| if (typeof opts === 'function') { |
| callback = opts; |
| opts = {}; |
| } |
| if (typeof opts === 'undefined') { |
| opts = {}; |
| } |
| this.canceled = false; |
| opts = utils.clone(opts); |
| var onChange, complete; |
| if ('onChange' in opts) { |
| onChange = opts.onChange; |
| delete opts.onChange; |
| } |
| if (typeof callback === 'function' && !opts.complete) { |
| complete = callback; |
| } else if ('complete' in opts) { |
| complete = opts.complete; |
| delete opts.complete; |
| } |
| this.push = replicate(src, target, opts); |
| |
| this.pull = replicate(target, src, opts); |
| var emittedCancel = false; |
| function onCancel(data) { |
| if (!emittedCancel) { |
| emittedCancel = true; |
| self.emit('cancel', data); |
| } |
| } |
| |
| function pullChange(change) { |
| self.emit('change', { |
| direction: 'pull', |
| change: change |
| }); |
| } |
| function pushChange(change) { |
| self.emit('change', { |
| direction: 'push', |
| change: change |
| }); |
| } |
| var listeners = {}; |
| |
| function removal(event, func) { |
| if (event === 'change' && |
| (func === pullChange || |
| func === pushChange)) { |
| self.removeAllListeners('change'); |
| } else if (event === 'cancel' && |
| func === onCancel) { |
| self.removeAllListeners('cancel'); |
| } else if (event in listeners && func === listeners[event]) { |
| self.removeAllListeners(event); |
| } |
| } |
| |
| this.on('newListener', function (event) { |
| if (event === 'change') { |
| self.pull.on('change', pullChange); |
| self.push.on('change', pushChange); |
| } else if (event === 'cancel') { |
| self.pull.on('cancel', onCancel); |
| self.push.on('cancel', onCancel); |
| } else if (event !== 'error' && |
| event !== 'complete' && !(event in listeners)) { |
| listeners[event] = function (e) { |
| self.emit(event, e); |
| }; |
| self.pull.on(event, listeners[event]); |
| self.push.on(event, listeners[event]); |
| } |
| }); |
| |
| this.on('removeListener', function (event) { |
| if (event === 'change') { |
| self.pull.removeListener('change', pullChange); |
| self.push.removeListener('change', pushChange); |
| } else if (event === 'cancel') { |
| self.pull.removeListener('cancel', onCancel); |
| self.push.removeListener('cancel', onCancel); |
| } else if (event in listeners) { |
| if (typeof listeners[event] === 'function') { |
| self.pull.removeListener(event, listeners[event]); |
| self.push.removeListener(event, listeners[event]); |
| delete listeners[event]; |
| } |
| } |
| }); |
| |
| this.pull.on('removeListener', removal); |
| this.push.on('removeListener', removal); |
| |
| var promise = utils.Promise.all([ |
| this.push, |
| this.pull |
| ]).then(function (resp) { |
| var out = { |
| push: resp[0], |
| pull: resp[1] |
| }; |
| self.emit('complete', out); |
| if (complete) { |
| complete(null, out); |
| } |
| self.removeAllListeners(); |
| return out; |
| }, function (err) { |
| self.cancel(); |
| self.emit('error', err); |
| if (complete) { |
| complete(err); |
| } |
| self.removeAllListeners(); |
| throw err; |
| }); |
| |
| this.then = function (success, err) { |
| return promise.then(success, err); |
| }; |
| |
| this.catch = function (err) { |
| return promise.catch(err); |
| }; |
| } |
| |
| Sync.prototype.cancel = function () { |
| if (!this.canceled) { |
| this.canceled = true; |
| this.push.cancel(); |
| this.pull.cancel(); |
| } |
| }; |