Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
248 lines
6.1 KiB
JavaScript
Executable file
248 lines
6.1 KiB
JavaScript
Executable file
define(function(require, exports, module) {
|
|
'use strict';
|
|
|
|
var Responder = require('common/responder');
|
|
var core = require('core');
|
|
var debug = require('common/debug')('controllers/recurring_events');
|
|
var nextTick = require('common/next_tick');
|
|
|
|
function RecurringEvents() {
|
|
this.accounts = core.storeFactory.get('Account');
|
|
Responder.call(this);
|
|
}
|
|
module.exports = RecurringEvents;
|
|
|
|
RecurringEvents.prototype = {
|
|
__proto__: Responder.prototype,
|
|
|
|
startEvent: 'expandStart',
|
|
completeEvent: 'expandComplete',
|
|
|
|
/**
|
|
* Adds N number of days to the window to expand
|
|
* events until. Its very important for this number
|
|
* to be greater then the maximum number of days displayed
|
|
* in the month view (or a view with more days) otherwise
|
|
* the view may be loaded without actually expanding all
|
|
* the visible days.
|
|
*
|
|
* @type Number
|
|
*/
|
|
paddingInDays: 85,
|
|
|
|
/**
|
|
* Amount of time (in MS) to wait between triggering
|
|
* the recurring event expansions.
|
|
*/
|
|
waitBeforeMove: 750,
|
|
|
|
/**
|
|
* We need to limit the number of tries on expansions
|
|
* otherwise its possible we never complete during error
|
|
* or long recurring event.
|
|
*/
|
|
maximumExpansions: 25,
|
|
|
|
/**
|
|
* private timeout (as in setTimeout id) use with waitBeforeMove.
|
|
*/
|
|
_moveTimeout: null,
|
|
|
|
/**
|
|
* True when queue is running...
|
|
*/
|
|
pending: false,
|
|
|
|
unobserve: function() {
|
|
core.timeController.removeEventListener(
|
|
'monthChange',
|
|
this
|
|
);
|
|
|
|
core.syncController.removeEventListener(
|
|
'syncComplete',
|
|
this
|
|
);
|
|
},
|
|
|
|
observe: function() {
|
|
var time = core.timeController;
|
|
|
|
// expand initial time this is necessary
|
|
// for cases where user has device off for long periods of time.
|
|
if (time.position) {
|
|
this.queueExpand(time.position);
|
|
}
|
|
|
|
// register observers
|
|
time.on('monthChange', this);
|
|
|
|
// we must re-expand after sync so events at least
|
|
// expand to the current position....
|
|
core.syncController.on('syncComplete', this);
|
|
},
|
|
|
|
handleEvent: function(event) {
|
|
switch (event.type) {
|
|
case 'syncComplete':
|
|
this.queueExpand(
|
|
core.timeController.position
|
|
);
|
|
break;
|
|
|
|
case 'monthChange':
|
|
if (this._moveTimeout !== null) {
|
|
clearTimeout(this._moveTimeout);
|
|
this._moveTimeout = null;
|
|
}
|
|
|
|
// trigger the event queue when we move
|
|
this._moveTimeout = setTimeout(
|
|
// data[0] is the new date.
|
|
this.queueExpand.bind(this, event.data[0]),
|
|
this.waitBeforeMove
|
|
);
|
|
break;
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Attempts to expand provider until no events require expansion.
|
|
*
|
|
* @param {Date} expandDate expands up to this date.
|
|
* @param {Calendar.Provider.Abstract} provider instance.
|
|
* @param {Function} callback
|
|
* fired when maximumExpansions is hit or
|
|
* no more events require expansion.
|
|
*
|
|
*/
|
|
_expandProvider: function(expandDate, provider, callback) {
|
|
debug('Will attempt to expand provider until:', expandDate);
|
|
var tries = 0;
|
|
var max = this.maximumExpansions;
|
|
|
|
function attemptCompleteExpand() {
|
|
debug('Will try to complete expansion (tries = ' + tries + ')');
|
|
if (tries >= max) {
|
|
return callback(new Error(
|
|
'could not complete expansion after "' + tries + '"'
|
|
));
|
|
}
|
|
|
|
provider.ensureRecurrencesExpanded(expandDate, function(err, didExpand) {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
|
|
debug('Expansion attempt did expand:', didExpand);
|
|
|
|
if (!didExpand) {
|
|
// successfully expanded and no events need expansion
|
|
// for this date anymore...
|
|
callback();
|
|
} else {
|
|
tries++;
|
|
// attempt another expand without stack.
|
|
nextTick(attemptCompleteExpand);
|
|
}
|
|
});
|
|
}
|
|
|
|
attemptCompleteExpand();
|
|
},
|
|
|
|
/**
|
|
* Queues an expansion. If the given date is before
|
|
* any dates in the stack it will be discarded.
|
|
*/
|
|
queueExpand: function(expandTo) {
|
|
if (this.pending) {
|
|
if (!this._next) {
|
|
this._next = expandTo;
|
|
} else if (expandTo > this._next) {
|
|
this._next = expandTo;
|
|
}
|
|
// don't start the queue if pending...
|
|
return;
|
|
}
|
|
|
|
// either way we need to process an event
|
|
// so increment pending for running and non-running cases.
|
|
this.pending = true;
|
|
this.emit('expandStart');
|
|
|
|
var self = this;
|
|
function expandNext(date) {
|
|
self.expand(date, function() {
|
|
if (date === self._next) {
|
|
self._next = null;
|
|
}
|
|
|
|
var next = self._next;
|
|
|
|
// when the queue is empty emit expandComplete
|
|
if (!next) {
|
|
self.pending = false;
|
|
self.emit('expandComplete');
|
|
return;
|
|
}
|
|
|
|
expandNext(next);
|
|
});
|
|
}
|
|
|
|
expandNext(expandTo);
|
|
},
|
|
|
|
/**
|
|
* Ensures we have time converage until the given date.
|
|
* Additional time will be added to the date see .paddingInDays.
|
|
*
|
|
* @param {Date} expandTo date to expand to.
|
|
*/
|
|
expand: function(expandTo, callback) {
|
|
debug('expand', expandTo);
|
|
|
|
this.accounts.all((err, accounts) => {
|
|
if (err) {
|
|
return callback(err);
|
|
}
|
|
|
|
// add minimum padding...
|
|
var expandDate = new Date(expandTo.valueOf());
|
|
expandDate.setDate(expandDate.getDate() + this.paddingInDays);
|
|
|
|
var providers = this._getExpandableProviders(accounts);
|
|
var pending = providers.length;
|
|
|
|
if (!pending) {
|
|
return nextTick(callback);
|
|
}
|
|
|
|
providers.forEach(provider => {
|
|
this._expandProvider(expandDate, provider, () => {
|
|
if (--pending <= 0) {
|
|
callback();
|
|
}
|
|
});
|
|
});
|
|
});
|
|
},
|
|
|
|
_getExpandableProviders: function(accounts) {
|
|
var providers = [];
|
|
Object.keys(accounts).forEach(key => {
|
|
var account = accounts[key];
|
|
var provider = core.providerFactory.get(account.providerType);
|
|
if (provider &&
|
|
provider.canExpandRecurringEvents &&
|
|
providers.indexOf(provider) === -1) {
|
|
providers.push(provider);
|
|
}
|
|
});
|
|
|
|
return providers;
|
|
}
|
|
};
|
|
|
|
});
|