/**
* The Scheduler Component
*
* @module aui-scheduler
* @submodule aui-scheduler-base
*/
var CSS_SCHEDULER_VIEW_ = A.getClassName('scheduler-base', 'view', ''),
CSS_SCHEDULER_VIEW_SELECTED = A.getClassName('active'),
DateMath = A.DataType.DateMath,
Lang = A.Lang,
isArray = Lang.isArray,
isBoolean = Lang.isBoolean,
isDate = Lang.isDate,
isFunction = Lang.isFunction,
isNumber = Lang.isNumber,
WidgetStdMod = A.WidgetStdMod,
isModelList = function(val) {
return val instanceof A.ModelList;
},
isSchedulerView = function(val) {
return val instanceof A.SchedulerView;
},
getCN = A.getClassName,
CSS_SCHEDULER_NAV = getCN('scheduler-base', 'nav'),
CSS_SCHEDULER_NAV_DATE = getCN('scheduler-base', 'nav', 'date'),
CSS_SCHEDULER_CONTROLS = getCN('scheduler-base', 'controls'),
CSS_SCHEDULER_HD = getCN('scheduler-base', 'hd'),
CSS_SCHEDULER_ICON_NEXT = getCN('scheduler-base', 'icon', 'next'),
CSS_SCHEDULER_ICON_PREV = getCN('scheduler-base', 'icon', 'prev'),
CSS_SCHEDULER_TODAY = getCN('scheduler-base', 'today'),
CSS_SCHEDULER_VIEW = getCN('scheduler-base', 'view'),
CSS_SCHEDULER_VIEW_ = getCN('scheduler-base', 'view', ''),
CSS_SCHEDULER_VIEW_DATE = getCN('scheduler-base', 'view', 'date'),
CSS_BTN = getCN('btn'),
CSS_BTN_DEFAULT = getCN('btn', 'default'),
CSS_ICON = getCN('glyphicon'),
CSS_ICON_CHEVRON_RIGHT = getCN('glyphicon', 'chevron', 'right'),
CSS_ICON_CHEVRON_LEFT = getCN('glyphicon', 'chevron', 'left'),
CSS_SCHEDULER_VIEWS = getCN('scheduler-base', 'views'),
TPL_SCHEDULER_CONTROLS = '<div class="col-xs-7 ' + CSS_SCHEDULER_CONTROLS + '"></div>',
TPL_SCHEDULER_HD = '<div class="row ' + CSS_SCHEDULER_HD + '"></div>',
TPL_SCHEDULER_ICON_NEXT = '<button aria-label="{ariaLabel}"" role="button" type="button" class="' + [CSS_SCHEDULER_ICON_NEXT, CSS_BTN,
CSS_BTN_DEFAULT].join(' ') + '"><span class="' + [CSS_ICON, CSS_ICON_CHEVRON_RIGHT].join(' ') + '"></span></button>',
TPL_SCHEDULER_ICON_PREV = '<button aria-label="{ariaLabel}"" role="button" type="button" class="' + [CSS_SCHEDULER_ICON_PREV, CSS_BTN,
CSS_BTN_DEFAULT].join(' ') + '"><span class="' + [CSS_ICON, CSS_ICON_CHEVRON_LEFT].join(' ') + '"></span></button>',
TPL_SCHEDULER_NAV = '<div class="btn-group"></div>',
TPL_SCHEDULER_NAV_DATE = '<div class="' + CSS_SCHEDULER_NAV_DATE + ' hidden-xs"></div>',
TPL_SCHEDULER_TODAY = '<button aria-label="{ariaLabel}" role="button" type="button" class="' +
[CSS_SCHEDULER_TODAY, CSS_BTN, CSS_BTN_DEFAULT].join(' ') + '">{today}</button>',
TPL_SCHEDULER_VIEW_BUTTON = '<button aria-label="{ariaLabel}" aria-pressed="false" type="button" class="hidden-xs ' +
[CSS_SCHEDULER_VIEW, CSS_SCHEDULER_VIEW_].join(' ') + '{name}" data-view-name="{name}">{label}</button>',
TPL_SCHEDULER_VIEW_LIST = '<option aria-label="{ariaLabel}" aria-pressed="false" class="' +
[CSS_SCHEDULER_VIEW, CSS_SCHEDULER_VIEW_].join(' ') + '{name}" data-view-name="{name}">{label}</option>',
TPL_SCHEDULER_VIEW_DATE = '<div class="' + CSS_SCHEDULER_VIEW_DATE + ' visible-xs"></div>',
TPL_SCHEDULER_VIEWS = '<div class="col-xs-5 form-inline ' + CSS_SCHEDULER_VIEWS + '"></div>',
TPL_SCHEDULER_VIEWS_SELECT = '<select class="form-control visible-xs"></select>';
/**
* A base class for `SchedulerEvents`.
*
* @class A.SchedulerEvents
* @extends ModelList
* @param {Object} config Object literal specifying widget configuration
* properties.
* @constructor
*/
A.SchedulerEvents = A.Base.create('scheduler-events', A.ModelList, [], {
/**
* Constructor for the `A.SchedulerEvents`. Lifecycle.
*
* @method initializer
* @protected
*/
initializer: function() {
this._remainingItems = this.get('originalItems');
this.after('originalItemsChange', this._afterOriginalItemsChange);
this.get('scheduler').on('plotViewEvents', A.bind(this._onPlotViewEvents, this));
},
/**
* Compares the inputs of a start and end date to see if adding `1` to the
* start date time is larger than the difference between start and end date
* times.
*
* @method comparator
* @param {Object} model
* @return {Number}
*/
comparator: function(model) {
var startDateTime = model.get('startDate'),
endDateTime = model.get('endDate');
return startDateTime + 1 / (endDateTime - startDateTime);
},
/**
* Fired after the `originalItems` attribute changes.
*
* @method _afterOriginalItemsChange
* @protected
*/
_afterOriginalItemsChange: function() {
this._remainingItems = this.get('originalItems');
this.remove(this.toArray());
this._updateEventsForView();
},
/**
* Fired when the `plotViewEvents` event is triggered.
*
* @method _onPlotViewEvents
* @protected
*/
_onPlotViewEvents: function() {
this._updateEventsForView();
},
/**
* Sets the `originalItems` attribute.
*
* @method _setOriginalItems
* @param {Array} originalItems
* @protected
*/
_setOriginalItems: function(val) {
var originalItems = [];
for (var i = 0; i < val.length; i++) {
if (A.instanceOf(val[i], this.model)) {
this.add(val[i]);
}
else {
val[i].startDate = val[i].startDate || new Date();
if (!val[i].endDate) {
val[i].endDate = DateMath.clone(val[i].startDate);
val[i].endDate.setHours(val[i].endDate.getHours() + 1);
}
originalItems.push(val[i]);
}
}
return originalItems;
},
/**
* Adds the necessary events for the view to the model list.
*
* @method _updateEventsForView
* @protected
*/
_updateEventsForView: function() {
var dateInterval,
eventEndDate,
eventStartDate,
i,
remainingItems = [],
view = this.get('scheduler').get('activeView');
if (!view) {
return;
}
dateInterval = view.getDateInterval();
for (i = 0; i < this._remainingItems.length; i++) {
eventStartDate = this._remainingItems[i].startDate;
eventEndDate = this._remainingItems[i].endDate;
if ((!dateInterval.startDate || eventEndDate >= dateInterval.startDate) &&
(!dateInterval.endDate || eventStartDate <= dateInterval.endDate)) {
this.add(this._remainingItems[i]);
}
else {
remainingItems.push(this._remainingItems[i]);
}
}
this._remainingItems = remainingItems;
},
model: A.SchedulerEvent
}, {
/**
* Static property used to define the default attribute
* configuration for the `SchedulerEvents`.
*
* @property ATTRS
* @type {Object}
* @static
*/
ATTRS: {
/**
* The original list of items.
*
* @attribute originalItems
* @default []
* @type {Array}
*/
originalItems: {
setter: '_setOriginalItems',
validator: A.Lang.isArray,
value: []
},
scheduler: {}
}
});
/**
* A base class for `SchedulerEventSupport`.
*
* @class A.SchedulerEventSupport
* @param {Object} config Object literal specifying widget configuration
* properties.
* @constructor
*/
var SchedulerEventSupport = function() {};
/**
* Static property used to define the default attribute
* configuration for the `SchedulerEventSupport`.
*
* @property ATTRS
* @type {Object}
* @static
*/
SchedulerEventSupport.ATTRS = {};
A.mix(SchedulerEventSupport.prototype, {
calendarModel: A.SchedulerCalendar,
eventModel: A.SchedulerEvent,
eventsModel: A.SchedulerEvents,
/**
* Construction logic executed during `SchedulerEventSupport` instantiation.
* Lifecycle.
*
* @method initializer
* @param config
* @protected
*/
initializer: function(config) {
var instance = this,
events = instance._toSchedulerEvents(config.items || config.events);
instance._events = new instance.eventsModel({
after: {
add: A.bind(instance._afterAddEvent, instance)
},
bubbleTargets: instance,
originalItems: this.get('pagination') ? events : [],
scheduler: instance
});
if (!this.get('pagination')) {
this._events.add(events);
}
},
/**
* Adds and returns the collection of events for this `Scheduler`.
*
* @method addEvents
* @param {Array | ModelList | Model | A.SchedulerEvent} models
* @return {A.SchedulerEvents}
*/
addEvents: function(models) {
var instance = this,
events = instance._toSchedulerEvents(models);
return instance._events.add(events);
},
/**
* Applies a `function` to the collection of `Scheduler` events.
*
* @method eachEvent
* @param {Function} fn
* @return {A.SchedulerEvents}
*/
eachEvent: function(fn) {
var instance = this;
return instance._events.each(fn);
},
/**
* Deletes each event in the collection of `Scheduler` events.
*
* @method flushEvents
*/
flushEvents: function() {
var instance = this;
instance._events.each(function(evt) {
delete evt._filtered;
});
},
/**
* Returns the event by matching it's `clientId`.
*
* @method getEventByClientId
* @param {String} clientId
* @return {Object}
*/
getEventByClientId: function(clientId) {
var instance = this;
return instance._events.getByClientId(clientId);
},
/**
* Gets a collection of events.
*
* @method getEvents
* @param {Function} filterFn (optional) Filters `events` and returns a list
* of events.
* @param {Boolean} skipSort
* @return {Array}
*/
getEvents: function(filterFn, skipSort) {
var instance = this,
events = instance._events;
// TODO: Check why the items are not being sorted on add
if (!skipSort) {
events.sort({
silent: true
});
}
if (filterFn) {
events = events.filter(filterFn);
}
else {
events = events.toArray();
}
return events;
},
/**
* Gets a collection of events within a given day. It will filter
* overlapping events by default unless `includeOverlap` is true.
*
* @method getEventsByDay
* @param {Date} date
* @param {Boolean} includeOverlap
* @return {Array}
*/
getEventsByDay: function(date, includeOverlap) {
var instance = this;
date = DateMath.safeClearTime(date);
return instance.getEvents(function(evt) {
return DateMath.compare(evt.getClearStartDate(), date) ||
(includeOverlap && DateMath.compare(evt.getClearEndDate(), date));
});
},
/**
* Returns the list of all events that intersect with a given date. Events
* that are not visible are not included in this list.
*
* @method getIntersectEvents
* @param {Date} date
* @return {Array}
*/
getIntersectEvents: function(date) {
var instance = this;
date = DateMath.safeClearTime(date);
return instance.getEvents(function(evt) {
var startDate = evt.getClearStartDate();
var endDate = evt.getClearEndDate();
return (evt.get('visible') &&
(DateMath.compare(date, startDate) ||
DateMath.compare(date, endDate) ||
DateMath.between(date, startDate, endDate)));
});
},
/**
* Removes given `SchedulerEvents` from the scheduler.
*
* @method removeEvents
* @param {Array | ModelList | Model | A.SchedulerEvent} models
* @return {A.SchedulerEvents} Removed SchedulerEvents.
*/
removeEvents: function(models) {
var instance = this,
events = instance._toSchedulerEvents(models);
return instance._events.remove(events);
},
/**
* Completely replaces all `SchedulerEvents` in the list with the given
* `SchedulerEvents`.
*
* @method resetEvents
* @param {Array | ModelList | Model | A.SchedulerEvent} models
* @return {A.SchedulerEvents} Reset SchedulerEvents.
*/
resetEvents: function(models) {
var instance = this,
events = instance._toSchedulerEvents(models);
return instance._events.reset(events);
},
/**
* Handles `add` events.
*
* @method _afterAddEvent
* @param {EventFacade} event
* @protected
*/
_afterAddEvent: function(event) {
var instance = this;
event.model.set('scheduler', instance);
},
/**
* Converts given values to `SchedulerEvents`.
*
* @method _toSchedulerEvents
* @param {Array | ModelList | Model | A.SchedulerEvent} values Values to be
* used or converted to `SchedulerEvent` instances.
* @return {A.SchedulerEvents} The values converted to `SchedulerEvents`.
* @protected
*/
_toSchedulerEvents: function(values) {
var instance = this,
events = [];
if (isModelList(values)) {
events = values.toArray();
values.set('scheduler', instance);
}
else if (isArray(values)) {
A.Array.each(values, function(value) {
if (isModelList(value)) {
events = events.concat(value.toArray());
value.set('scheduler', instance);
}
else {
events.push(value);
}
});
}
else {
events = values;
}
return events;
}
});
A.SchedulerEventSupport = SchedulerEventSupport;
/**
* Fired when the current image will be animated in.
*
* @event plotViewEvents
* @preventable _defPlotViewEventsFn
*/
/**
* A base class for `SchedulerBase`.
*
* @class A.SchedulerBase
* @extends A.Component
* @uses A.SchedulerEventSupport, WidgetStdMod
* @param {Object} config Object literal specifying widget configuration
* properties.
* @constructor
* @example
```
<div id="myScheduler"></div>
```
* @example
```
YUI().use(
'aui-scheduler',
function(Y) {
var events = [
{
content: 'Event1',
endDate: new Date(2013, 1, 4, 5),
startDate: new Date(2013, 1, 4, 1)
}
];
var eventRecorder = new Y.SchedulerEventRecorder();
var weekView = new Y.SchedulerWeekView();
new Y.Scheduler(
{
boundingBox: '#myScheduler',
date: new Date(2013, 1, 4),
eventRecorder: eventRecorder,
items: events,
render: true,
views: [weekView]
}
);
}
);
```
*/
var SchedulerBase = A.Component.create({
/**
* Static property provides a string to identify the class.
*
* @property NAME
* @type {String}
* @static
*/
NAME: 'scheduler-base',
/**
* Static property used to define the default attribute
* configuration for the `SchedulerBase`.
*
* @property ATTRS
* @type {Object}
* @static
*/
ATTRS: {
/**
* Contains the active view.
*
* @attribute activeView
* @type {A.SchedulerView}
*/
activeView: {
validator: isSchedulerView
},
/**
* Contains the aria labels.
*
* @attribute ariaLabels
* @default { agenda: 'View Agenda', day: 'View by Day', month: 'View by
* Month', next: 'Go to Next', previous: 'Go to Previous',
* today: 'Go to Today', week: 'View by Week' }
* @type {Object}
*/
ariaLabels: {
value: {
agenda: 'View Agenda',
day: 'View by Day',
month: 'View by Month',
next: 'Go to Next',
previous: 'Go to Previous',
today: 'Go to Today',
week: 'View by Week'
}
},
/**
* Contains a function to call a callback with a date object. The date
* should represent the current time.
*
* @attribute currentTimeFn
* @type {Function}
*/
currentTimeFn: {
value: function(callback) {
callback(new Date());
}
},
/**
* Contains the date corresponding to the current date which is the
* value of the date set on the user's computer.
*
* @attribute date
* @type {Date}
*/
date: {
value: new Date(),
validator: isDate
},
/**
* Defines the keyboard configuration object for
* `Plugin.NodeFocusManager`.
*
* @attribute focusmanager
* @default { descendants: 'button', keys: { next: 'down:39', previous:
* 'down:37' }, circular: false }
* @type {Object}
*/
focusmanager: {
value: {
descendants: 'button',
keys: {
next: 'down:39',
previous: 'down:37'
},
circular: false
},
writeOnce: 'initOnly'
},
/**
* Contains the `Scheduler`'s `SchedulerEventRecorder` instance.
*
* @attribute eventRecorder
* @type {A.SchedulerEventRecorder}
*/
eventRecorder: {
setter: '_setEventRecorder'
},
/**
* Contains the collection of strings used to label elements of the UI.
*
* @attribute strings
* @type {Object}
*/
strings: {
value: {
agenda: 'Agenda',
day: 'Day',
month: 'Month',
table: 'Table',
today: 'Today',
week: 'Week',
year: 'Year'
}
},
/**
* Contains the function that formats the navigation date.
*
* @attribute navigationDateFormatter
* @default %A - %d %b %Y
* @type {Function}
*/
navigationDateFormatter: {
value: function(date) {
var instance = this;
return A.DataType.Date.format(
date, {
format: '%B %d, %Y',
locale: instance.get('locale')
}
);
},
validator: isFunction
},
/**
* If the model items should be lazily loaded through pagination or not.
*
* @attribute pagination
* @default true
* @type {Boolean}
*/
pagination: {
validator: A.Lang.isBoolean,
value: true,
writeOnce: 'initOnly'
},
/**
* Contains the list of views belonging to this `Scheduler`.
*
* @attribute views
* @default []
* @type {Array}
*/
views: {
setter: '_setViews',
value: []
},
/**
* Contains the `Scheduler`'s current date. If there is an `activeView`,
* this attribute will contain the `activeView`'s current date.
*
* @attribute viewDate
* @type {Date}
* @readOnly
*/
viewDate: {
getter: '_getViewDate',
readOnly: true
},
/**
* First day of the week: Sunday is 0, Monday is 1.
*
* @attribute firstDayOfWeek
* @default 0
* @type {Number}
*/
firstDayOfWeek: {
value: 0,
validator: isNumber
},
/*
* HTML_PARSER attributes
*/
controlsNode: {
valueFn: function() {
return A.Node.create(TPL_SCHEDULER_CONTROLS);
}
},
/**
* Contains the node that displays `Scheduler`'s current date in the `SchedulerView`.
* This node is only visible on mobile.
*
* @attribute viewDateNode
* @type {Node}
*/
viewDateNode: {
valueFn: function() {
return A.Node.create(TPL_SCHEDULER_VIEW_DATE);
}
},
/**
* Contains `Scheduler`'s header node.
*
* @attribute headerNode
* @type {Node}
*/
headerNode: {
valueFn: function() {
return A.Node.create(TPL_SCHEDULER_HD);
}
},
/**
* Contains the node that goes to the next date in the `activeView`.
*
* @attribute iconNextNode
* @type {Node}
*/
iconNextNode: {
valueFn: function() {
var instance = this;
return A.Node.create(
A.Lang.sub(TPL_SCHEDULER_ICON_NEXT, {
ariaLabel: instance.getAriaLabel('next')
})
);
}
},
/**
* Contains the node that goes to the previous date in the `activeView`.
*
* @attribute iconPrevNode
* @type {Node}
*/
iconPrevNode: {
valueFn: function() {
var instance = this;
return A.Node.create(
A.Lang.sub(TPL_SCHEDULER_ICON_PREV, {
ariaLabel: instance.getAriaLabel('previous')
})
);
}
},
/**
* Contains `Scheduler`'s header navigation node.
*
* @attribute navNode
* @type {Node}
*/
navNode: {
valueFn: function() {
return A.Node.create(TPL_SCHEDULER_NAV);
}
},
/**
* Contains the node that displays `Scheduler`'s current date in `Scheduler`'s header.
* This node is hidden on mobile.
*
* @attribute navDateNode
* @type {Node}
*/
navDateNode: {
valueFn: function() {
return A.Node.create(TPL_SCHEDULER_NAV_DATE);
}
},
/**
* Define whether the scheduler header will be displayed.
*
* @attribute showHeader
* @default true
* @type {Boolean}
*/
showHeader: {
validator: isBoolean,
value: true
},
/**
* Contains the node for the select dropdown for `Scheduler`'s views.
* This node is only visible on mobile.
*
* @attribute viewsSelectNode
* @type {Node}
*/
viewsSelectNode: {
valueFn: function() {
return A.Node.create(TPL_SCHEDULER_VIEWS_SELECT);
}
},
/**
* Today date representation. This option allows the developer to
* specify the date he wants to be used as the today date.
*
* @attribute todayDate
* @default new Date()
* @type {Date}
*/
todayDate: {
value: new Date(),
validator: isDate
},
/**
* Contains the node that goes to today date in the `activeView`.
*
* @attribute todayNode
* @type {Node}
*/
todayNode: {
valueFn: function() {
var instance = this;
return A.Node.create(
A.Lang.sub(this._processTemplate(TPL_SCHEDULER_TODAY), {
ariaLabel: instance.getAriaLabel('today')
})
);
}
},
/**
* Contains the node container that holds the nodes to change `Scheduler`'s
* `activeView`.
*
* @attribute viewsNode
* @type {Node}
*/
viewsNode: {
valueFn: function() {
return A.Node.create(TPL_SCHEDULER_VIEWS);
}
}
},
/**
* Contains an object hash, defining how attribute values are to be parsed
* from markup contained in the widget's bounding box.
*
* @property HTML_PARSER
* @type {Object}
* @static
*/
HTML_PARSER: {
controlsNode: '.' + CSS_SCHEDULER_CONTROLS,
viewDateNode: '.' + CSS_SCHEDULER_VIEW_DATE,
headerNode: '.' + CSS_SCHEDULER_HD,
iconNextNode: '.' + CSS_SCHEDULER_ICON_NEXT,
iconPrevNode: '.' + CSS_SCHEDULER_ICON_PREV,
navNode: '.' + CSS_SCHEDULER_NAV,
navDateNode: '.' + CSS_SCHEDULER_NAV_DATE,
todayNode: '.' + CSS_SCHEDULER_TODAY,
viewsNode: '.' + CSS_SCHEDULER_VIEWS
},
/**
* Static property used to define the UI attributes.
*
* @property UI_ATTRS
* @type {Array}
* @static
*/
UI_ATTRS: ['date', 'activeView', 'showHeader'],
/**
* Static property used to define the augmented classes.
*
* @property AUGMENTS
* @type {Array}
* @static
*/
AUGMENTS: [A.SchedulerEventSupport, A.WidgetStdMod],
prototype: {
viewStack: null,
/**
* Construction logic executed during `SchedulerBase` instantiation.
* Lifecycle.
*
* @method initializer
* @protected
*/
initializer: function() {
var instance = this;
instance.viewStack = {};
instance.controlsNode = instance.get('controlsNode');
instance.viewDateNode = instance.get('viewDateNode');
instance.header = instance.get('headerNode');
instance.iconNextNode = instance.get('iconNextNode');
instance.iconPrevNode = instance.get('iconPrevNode');
instance.navNode = instance.get('navNode');
instance.navDateNode = instance.get('navDateNode');
instance.viewsSelectNode = instance.get('viewsSelectNode');
instance.todayNode = instance.get('todayNode');
instance.viewsNode = instance.get('viewsNode');
instance._populateViewNodes();
instance.after({
activeViewChange: instance._afterActiveViewChange,
render: instance._afterRender
});
this.publish({
plotViewEvents: {
defaultFn: this._defPlotViewEventsFn
}
});
},
/**
* Binds the events on the `SchedulerBase` UI. Lifecycle.
*
* @method bindUI
* @protected
*/
bindUI: function() {
var instance = this;
instance._bindDelegate();
},
/**
* Syncs the `SchedulerBase` UI. Lifecycle.
*
* @method syncUI
* @protected
*/
syncUI: function() {
var instance = this;
instance.syncStdContent();
},
/**
* Returns the `SchedulerView` that belongs to a given name.
*
* @method getViewByName
* @param {String} name
* @return {A.SchedulerView}
*/
getViewByName: function(name) {
var instance = this;
return instance.viewStack[name];
},
/**
* Returns the trigger node for the given `SchedulerView`.
*
* @method getViewTriggerNode
* @param {A.SchedulerView} view
* @return {Node} The `SchedulerView`'s trigger `Node`.
*/
getViewTriggerNode: function(view) {
var instance = this,
name = view.get('name'),
viewportWidth = A.DOM.winWidth();
if (viewportWidth >= 768) {
return instance.viewsNode.one('.' + CSS_SCHEDULER_VIEW_ + name);
}
return instance.viewsSelectNode.one('.' + CSS_SCHEDULER_VIEW_ + name);
},
/**
* Returns this `Scheduler`'s `strings` attribute value.
*
* @method getStrings
* @return {String}
*/
getStrings: function() {
var instance = this;
return instance.get('strings');
},
/**
* Returns the string that matches the `key` type.
*
* @method getString
* @param {String} key
* @return {String}
*/
getString: function(key) {
var instance = this;
return instance.getStrings()[key];
},
/**
* Returns the aria label that matches the `key` type.
*
* @method getAriaLabel
* @param {String} key
* @return {String}
*/
getAriaLabel: function(key) {
var instance = this,
ariaLabels = instance.get('ariaLabels');
return ariaLabels[key];
},
/**
* Renders the `SchedulerView` based on the given `view` parameter
* under `instance.bodyNode`.
*
* @method renderView
* @param {A.SchedulerView} view
*/
renderView: function(view) {
var instance = this;
if (view) {
view.show();
if (!view.get('rendered')) {
if (!instance.bodyNode) {
instance.setStdModContent(WidgetStdMod.BODY, '');
}
instance.bodyNode.prepend(instance.viewDateNode);
view.render(instance.bodyNode);
}
}
},
/**
* Plots all events for the current view.
*
* @method plotViewEvents
* @param view
*/
plotViewEvents: function(view) {
var instance = this;
view.plotEvents(
instance.getEvents()
);
},
/**
* Plots the `activeView` events value.
*
* @method syncEventsUI
*/
syncEventsUI: function() {
var instance = this,
activeView = instance.get('activeView');
if (activeView) {
this.fire('plotViewEvents');
}
},
/**
* Renders a new `ButtonGroup` and attaches it to the `Scheduler`
* instances as a property `instance.buttonGroup`. It is rendered under
* the `Scheduler` instance's `viewsNode`.
*
* @method renderButtonGroup
*/
renderButtonGroup: function() {
var instance = this;
if (!instance.buttonGroup) {
instance.buttonGroup = new A.ButtonGroup({
boundingBox: instance.viewsNode,
on: {
selectionChange: A.bind(instance._onButtonGroupSelectionChange, instance)
}
}).render();
}
},
/**
* Renders a dropdown list under the `Scheduler` instance's `viewsNode`.
*
* @method renderDropdownList
*/
renderDropdownList: function() {
var instance = this;
instance.viewsSelectNode.on('change', A.bind(instance._onSelectionChange, instance));
},
/**
* Sync `SchedulerBase` StdContent.
*
* @method syncStdContent
*/
syncStdContent: function() {
var instance = this;
if (instance.get('showHeader')) {
instance.renderButtonGroup();
instance.navNode.append(instance.iconPrevNode);
instance.navNode.append(instance.todayNode);
instance.navNode.append(instance.iconNextNode);
instance.controlsNode.append(instance.navNode);
instance.controlsNode.append(instance.navDateNode);
instance.viewsNode.append(instance.viewsSelectNode);
instance.header.append(instance.controlsNode);
instance.header.append(instance.viewsNode);
instance.setStdModContent(WidgetStdMod.HEADER, instance.header.getDOM());
instance.header.show();
}
else {
instance.header.hide();
}
},
/**
* Handles `activeView` events.
*
* @method _afterActiveViewChange
* @param {EventFacade} event
* @protected
*/
_afterActiveViewChange: function(event) {
var instance = this;
if (instance.get('rendered')) {
var activeView = event.newVal;
var lastActiveView = event.prevVal;
if (lastActiveView) {
lastActiveView.hide();
}
instance.renderView(activeView);
var eventRecorder = instance.get('eventRecorder');
if (eventRecorder) {
eventRecorder.hidePopover();
}
instance._uiSetDate(instance.get('date'));
}
},
/**
* Handles `render` events.
*
* @method _afterRender
* @param {EventFacade} event
* @protected
*/
_afterRender: function() {
var instance = this,
activeView = instance.get('activeView');
instance.renderView(activeView);
instance.renderDropdownList();
instance._uiSetDate(instance.get('date'));
instance._uiSetActiveView(activeView);
instance._plugFocusManager();
},
/**
* Binds click events to an event delegate.
*
* @method _bindDelegate
* @protected
*/
_bindDelegate: function() {
var instance = this;
instance.controlsNode.delegate('click', instance._onClickPrevIcon, '.' + CSS_SCHEDULER_ICON_PREV,
instance);
instance.controlsNode.delegate('click', instance._onClickNextIcon, '.' + CSS_SCHEDULER_ICON_NEXT,
instance);
instance.controlsNode.delegate('click', instance._onClickToday, '.' + CSS_SCHEDULER_TODAY, instance);
},
/**
* Creates the given `SchedulerView`'s trigger `Node`.
*
* @method _createViewTriggerNode
* @param {A.SchedulerView} view
* @param {String} tpl
* @protected
* @return {Node} The `SchedulerView`'s trigger `Node`.
*/
_createViewTriggerNode: function(view, tpl) {
var instance = this;
var name = view.get('name');
return A.Node.create(
A.Lang.sub(tpl, {
name: name,
label: (instance.getString(name) || name),
ariaLabel: instance.getAriaLabel(name)
}));
},
/**
* Default behavior for the `plotViewEvents` event.
*
* @method _defPlotViewEventsFn
* @protected
*/
_defPlotViewEventsFn: function() {
this.plotViewEvents(this.get('activeView'));
},
/**
* Returns the `SchedulerView`'s `date`.
*
* @method _getViewDate
* @protected
* @return {Date} The `SchedulerView`'s `date`.
*/
_getViewDate: function() {
var instance = this,
date = instance.get('date'),
activeView = instance.get('activeView');
if (activeView) {
date = activeView.getAdjustedViewDate(date);
}
return date;
},
/**
* Handles `buttonGroupSelectionChange` events.
*
* @method _onButtonGroupSelectionChange
* @param {EventFacade} event
* @protected
*/
_onButtonGroupSelectionChange: function(event) {
var instance = this,
viewName = event.originEvent.target.attr('data-view-name');
instance.set('activeView', instance.getViewByName(viewName));
instance.viewsSelectNode.one('[data-view-name=' + viewName + ']').set('selected', true);
event.preventDefault();
},
/**
* Handles `clickToday` events.
*
* @method _onClickToday
* @param {EventFacade} event
* @protected
*/
_onClickToday: function(event) {
var instance = this,
activeView = instance.get('activeView');
if (activeView) {
instance.set('date', instance.get('todayDate'));
}
event.preventDefault();
},
/**
* Handles `clickNextIcon` events.
*
* @method _onClickNextIcon
* @param {EventFacade} event
* @protected
*/
_onClickNextIcon: function(event) {
var instance = this,
activeView = instance.get('activeView');
if (activeView) {
instance.set('date', activeView.get('nextDate'));
}
event.preventDefault();
},
/**
* Handles `clickPrevIcon` events.
*
* @method _onClickPrevIcon
* @param {EventFacade} event
* @protected
*/
_onClickPrevIcon: function(event) {
var instance = this,
activeView = instance.get('activeView');
if (activeView) {
instance.set('date', activeView.get('prevDate'));
}
event.preventDefault();
},
/**
* Handles select tag's change events.
*
* @method _onSelectionChange
* @param {EventFacade} event
* @protected
*/
_onSelectionChange: function(event) {
var instance = this,
target = event.target,
index = target.get('selectedIndex'),
viewName = target.get('options').item(index).attr('data-view-name');
instance.set('activeView', instance.getViewByName(viewName));
},
/**
* Add the content to represent each view to the corresponding view
* nodes.
*
* The scheduler can display its events in many views - day view, week
* view etc. These views are selected through a set of buttons at the
* top left of the scheduler (if it is being viewed in desktop mode)
* or from a select (if it is being viewed from a mobile device). This
* method checks which views the scheduler is expected to render, and
* adds elements both options (i.e. adds buttons to the button group and
* options to the select).
*
* @method _populateViewNodes
* @protected
*/
_populateViewNodes: function() {
var instance = this;
var views = instance.get('views');
A.Array.each(views, function(view) {
instance.viewsSelectNode.append(instance._createViewTriggerNode(view, TPL_SCHEDULER_VIEW_LIST));
instance.viewsNode.append(instance._createViewTriggerNode(view, TPL_SCHEDULER_VIEW_BUTTON));
});
},
/**
* Plugs 'NodeFocusManager' into the viewsNode and navNode.
*
* @method _plugFocusManager
* @protected
*/
_plugFocusManager: function() {
var instance = this;
instance.viewsNode.plug(A.Plugin.NodeFocusManager, this.get('focusmanager'));
instance.navNode.plug(A.Plugin.NodeFocusManager, this.get('focusmanager'));
},
/**
* Applies substitution to a given template.
*
* @method _processTemplate
* @param {String} tpl
* @protected
*/
_processTemplate: function(tpl) {
var instance = this;
return A.Lang.sub(tpl, instance.getStrings());
},
/**
* Replaces this `SchedulerBase`'s `eventRecorder` with the given
* `eventRecorder` value.
*
* @method _setEventRecorder
* @param {A.SchedulerEventRecorder} val A `SchedulerEventRecorder`
* instance.
* @protected
*/
_setEventRecorder: function(val) {
var instance = this;
if (val) {
val.setAttrs({
scheduler: instance
}, {
silent: true
});
val.addTarget(instance);
}
},
/**
* Replaces this `SchedulerBase`'s `views` with the given `views` value.
*
* @method _setViews
* @param {Array} val Array of `SchedulerView` instances.
* @protected
* @return {Array} The replaced `SchedulerBase`'s `views`.
*/
_setViews: function(val) {
var instance = this;
var views = [];
A.Array.each(val, function(view) {
if (isSchedulerView(view) && !view.get('rendered')) {
view.setAttrs({
scheduler: instance
});
views.push(view);
instance.viewStack[view.get('name')] = view;
}
});
if (!instance.get('activeView')) {
instance.set('activeView', val[0]);
}
return views;
},
/**
* Sets `activeView` on the UI.
*
* @method _uiSetActiveView
* @param {SchedulerView} val A `SchedulerView` instance.
* @protected
*/
_uiSetActiveView: function(val) {
var instance = this;
if (val) {
var activeView = val.get('name'),
activeNav = instance.viewsNode.one('.' + CSS_SCHEDULER_VIEW_ + activeView);
if (activeNav) {
instance.viewsNode.all('button').removeClass(CSS_SCHEDULER_VIEW_SELECTED).setAttribute('aria-pressed', false);
instance.viewsSelectNode.one('[data-view-name=' + activeView + ']').set('selected', true);
activeNav.addClass(CSS_SCHEDULER_VIEW_SELECTED).setAttribute('aria-pressed', true);
}
}
},
/**
* Sets `date` on the UI.
*
* @method _uiSetDate
* @param {Date} date
* @protected
*/
_uiSetDate: function(date) {
var instance = this;
var formatter = instance.get('navigationDateFormatter');
var navigationTitle = formatter.call(instance, date);
if (instance.get('rendered')) {
var activeView = instance.get('activeView');
if (activeView) {
activeView._uiSetDate(date);
formatter = activeView.get('navigationDateFormatter');
navigationTitle = formatter.call(activeView, date);
}
instance.navDateNode.html(navigationTitle);
instance.viewDateNode.html(navigationTitle);
instance.syncEventsUI();
}
},
/**
* Set the `showHeader` property - i.e., defines if the scheduler
* header will be displayed.
*
* @method _uiSetShowHeader
* @protected
*/
_uiSetShowHeader: function() {
var instance = this;
instance.syncStdContent();
}
}
});
A.Scheduler = SchedulerBase;