@@ -1,378 +1,365 @@
-/*
-*  AngularJs Fullcalendar Wrapper for the JQuery FullCalendar
-*  API @ http://arshaw.com/fullcalendar/
-*
-*  Angular Calendar Directive that takes in the [eventSources] nested array object as the ng-model and watches it deeply changes.
-*       Can also take in multiple event urls as a source object(s) and feed the events per view.
-*       The calendar will watch any eventSource array and update itself when a change is made.
-*
-*/
-
-angular.module('ui.calendar', [])
-  
-    .constant('uiCalendarConfig', {
-        calendars : {}
-    })
-
-    .controller('uiCalendarCtrl', ['$scope', '$locale',
-        function ($scope, $locale) {
-
-            var sources = $scope.eventSources;
-            var extraEventSignature = $scope.calendarWatchEvent
-                ? $scope.calendarWatchEvent
-                : angular.noop;
-
-            var wrapFunctionWithScopeApply = function (functionToWrap) {
-                return function () {
-                    // This may happen outside of angular context, so create one if outside.
-                    if ($scope.$root.$$phase) {
-                        return functionToWrap.apply(this, arguments);
-                    }
-                    
-                    var args = arguments;
-                    return $scope.$root.$apply(
-                        function () {
-                            return functionToWrap.apply(this, args);
-                        }
-                    ).bind(this);
-                };
-            };
-            
-            var eventSerialId = 1;
-            // @return {String} fingerprint of the event object and its properties
-            this.eventFingerprint = function (e) {
-                if (!e._id) {
-                    e._id = eventSerialId++;
-                }
-        
-                var extraSignature = extraEventSignature({
-                    event : e
-                }) || '';
-                var start = moment.isMoment(e.start) ? e.start.unix() : (e.start ? moment(e.start).unix() : '');
-                var end = moment.isMoment(e.end) ? e.end.unix() : (e.end   ? moment(e.end).unix()   : '');
-        
-                // This extracts all the information we need from the event. http://jsperf.com/angular-calendar-events-fingerprint/3
-                return [e._id, e.id || '', e.title || '', e.url || '', start, end, e.allDay || '', e.className || '', extraSignature].join('');
-            };
-
-            var sourceSerialId = 1;
-            var sourceEventsSerialId = 1;
-            // @return {String} fingerprint of the source object and its events array
-            this.sourceFingerprint = function (source) {
-                var fp = '' + (source.__id || (source.__id = sourceSerialId++));
-                var events = angular.isObject(source) && source.events;
-                
-                if (events) {
-                    fp = fp + '-' + (events.__id || (events.__id = sourceEventsSerialId++));
-                }
-                return fp;
-            };
-
-            // @return {Array} all events from all sources
-            this.allEvents = function () {
-                // do sources.map(&:events).flatten(), but we don't have flatten
-                return (sources || []).reduce(
-                    function (previous, source) {
-                        if (angular.isArray(source)) {
-                            previous.push(source);
-                        } else if (angular.isObject(source) && angular.isArray(source.events)) {
-                            var extEvent = Object.keys(source).filter(
-                                function (key) {
-                                    return (key !== '_id' && key !== 'events');
-                                }
-                            );
-                            
-                            source.events.forEach(
-                                function (event) {
-                                    angular.extend(event, evtEvent);
-                                }
-                            );
-                        
-                            previous.push(source.events);
-                        }
-                        return previous;
-                    },
-                    []
-                );
-            };
-
-            // Track changes in array of objects by assigning id tokens to each element and watching the scope for changes in the tokens
-            // @param {Array|Function} arraySource array of objects to watch
-            // @param tokenFn {Function} that returns the token for a given object
-            // @return {Object}
-            //  subscribe: function(scope, function(newTokens, oldTokens))
-            //    called when source has changed. return false to prevent individual callbacks from firing
-            //  onAdded/Removed/Changed:
-            //    when set to a callback, called each item where a respective change is detected
-            this.changeWatcher = function (arraySource, tokenFn) {
-                var self;
-                var getTokens = function () {
-                    return (angular.isFunction(arraySource)
-                        ? arraySource()
-                        : arraySource
-                    ).reduce(
-                        function (rslt, source) {
-                            source.forEach(
-                                function (el) {
-                                    var token = tokenFn(el);
-                                    map[token] = el;
-                                    rslt.push(token);
-                                }
-                            );
-                            return rslt;
-                        },
-                        []
-                    );
-                };
-
-                // @param {Array} a
-                // @param {Array} b
-                // @return {Array} elements in that are in a but not in b
-                // @example
-                //  subtractAsSets([6, 100, 4, 5], [4, 5, 7]) // [6, 100]
-                var subtractAsSets = function (a, b) {
-                    var obj = (b || []).reduce(
-                        function (rslt, val) {
-                            rslt[val] = true;
-                            return rslt;
-                        },
-                        Object.create(null)
-                    );
-                    return (a || []).filter(
-                        function (val) {
-                            return !obj[val];
-                        }
-                    );
-                };
-
-                // Map objects to tokens and vice-versa
-                var map = {};
-
-                // Compare newTokens to oldTokens and call onAdded, onRemoved, and onChanged handlers for each affected event respectively.
-                var applyChanges = function (newTokens, oldTokens) {
-                    var i, n, el, token;
-                    var replacedTokens = {};
-                    var removedTokens = subtractAsSets(oldTokens, newTokens);
-                    for (i = 0, n = removedTokens.length; i < n; i++) {
-                        var removedToken = removedTokens[i];
-                        el = map[removedToken];
-                        delete map[removedToken];
-                        var newToken = tokenFn(el);
-                        // if the element wasn't removed but simply got a new token, its old token will be different from the current one
-                        if (newToken === removedToken) {
-                            self.onRemoved(el);
-                        } else {
-                            replacedTokens[newToken] = removedToken;
-                            self.onChanged(el);
-                        }
-                    }
-
-                    var addedTokens = subtractAsSets(newTokens, oldTokens);
-                    for (i = 0, n = addedTokens.length; i < n; i++) {
-                        token = addedTokens[i];
-                        el = map[token];
-                        if (!replacedTokens[token]) {
-                            self.onAdded(el);
-                        }
-                    }
-                };
-            
-                return self = {
-                    subscribe : function (scope, onArrayChanged) {
-                        scope.$watch(getTokens, function (newTokens, oldTokens) {
-                            var notify = !(onArrayChanged && onArrayChanged(newTokens, oldTokens) === false);
-                            if (notify) {
-                                applyChanges(newTokens, oldTokens);
-                            }
-                        }, true);
-                    },
-                    onAdded : angular.noop,
-                    onChanged : angular.noop,
-                    onRemoved : angular.noop
-                };
-            };
-
-            this.getFullCalendarConfig = function (calendarSettings, uiCalendarConfig) {
-                var config = {};
-
-                angular.extend(config, uiCalendarConfig);
-                angular.extend(config, calendarSettings);
-
-                angular.forEach(config, function (value, key) {
-                    if (typeof value === 'function') {
-                        config[key] = wrapFunctionWithScopeApply(config[key]);
-                    }
-                });
-
-                return config;
-            };
-
-            this.getLocaleConfig = function (fullCalendarConfig) {
-                if (!fullCalendarConfig.lang || fullCalendarConfig.useNgLocale) {
-                    // Configure to use locale names by default
-                    var tValues = function (data) {
-                        // convert {0: "Jan", 1: "Feb", ...} to ["Jan", "Feb", ...]
-                        return Object.keys(data).reduce(
-                            function (rslt, el) {
-                                rslt[el] = data[el];
-                                return rslt;
-                            },
-                            {}
-                        );
-                    };
-            
-                    var dtf = $locale.DATETIME_FORMATS;
-                    return {
-                        monthNames : tValues(dtf.MONTH),
-                        monthNamesShort : tValues(dtf.SHORTMONTH),
-                        dayNames : tValues(dtf.DAY),
-                        dayNamesShort : tValues(dtf.SHORTDAY)
-                    };
-                }
-                
-                return {};
-            };
-        }
-    ])
-
-    .directive('uiCalendar', ['uiCalendarConfig',
-        function (uiCalendarConfig) {
-
-            return {
-                restrict : 'A',
-                scope : {
-                    eventSources : '=ngModel',
-                    calendarWatchEvent : '&'
-                },
-                controller : 'uiCalendarCtrl',
-                link : function (scope, elm, attrs, controller) {
-                    var sources = scope.eventSources;
-                    var sourcesChanged = false;
-                    var calendar;
-                    var eventSourcesWatcher = controller.changeWatcher(sources, controller.sourceFingerprint);
-                    var eventsWatcher = controller.changeWatcher(controller.allEvents, controller.eventFingerprint);
-                    var options = null;
-
-                    function getOptions () {
-                        var calendarSettings = attrs.uiCalendar
-                            ? scope.$parent.$eval(attrs.uiCalendar)
-                            : {};
-
-                        var options = angular.extend(
-                            {
-                                eventSources : sources
-                            },
-                            controller.getFullCalendarConfig(calendarSettings, uiCalendarConfig),
-                            controller.getLocaleConfig(fullCalendarConfig)
-                        );
-                        
-                        //remove calendars from options
-                        options.calendars = null;
-
-                        var options2 = {};
-                        for (var o in options) {
-                            if (o !== 'eventSources') {
-                                options2[o] = options[o];
-                            }
-                        }
-                        return JSON.stringify(options2);
-                    }
-
-                    scope.destroyCalendar = function () {
-                        if (calendar && calendar.fullCalendar) {
-                            calendar.fullCalendar('destroy');
-                        }
-                        if (attrs.calendar) {
-                            calendar = uiCalendarConfig.calendars[attrs.calendar] = $(elm).html('');
-                        } else {
-                            calendar = $(elm).html('');
-                        }
-                    };
-
-                    scope.initCalendar = function () {
-                        if (!calendar) {
-                            calendar = angular.element(elm).html('');
-                        }
-                        calendar.fullCalendar(options);
-                        if (attrs.calendar) {
-                            uiCalendarConfig.calendars[attrs.calendar] = calendar;
-                        }
-                    };
-                    
-                    scope.$on('$destroy', function () {
-                        scope.destroyCalendar();
-                    });
-
-                    eventSourcesWatcher.onAdded = function (source) {
-                        if (calendar && calendar.fullCalendar) {
-                            calendar.fullCalendar(options);
-                            if (attrs.calendar) {
-                                uiCalendarConfig.calendars[attrs.calendar] = calendar;
-                            }
-                            calendar.fullCalendar('addEventSource', source);
-                            sourcesChanged = true;
-                        }
-                    };
-
-                    eventSourcesWatcher.onRemoved = function (source) {
-                        if (calendar && calendar.fullCalendar) {
-                            calendar.fullCalendar('removeEventSource', source);
-                            sourcesChanged = true;
-                        }
-                    };
-
-                    eventSourcesWatcher.onChanged = function () {
-                        if (calendar && calendar.fullCalendar) {
-                            calendar.fullCalendar('refetchEvents');
-                            sourcesChanged = true;
-                        }
-                    };
-
-                    eventsWatcher.onAdded = function (event) {
-                        if (calendar && calendar.fullCalendar) {
-                            calendar.fullCalendar('renderEvent', event, (event.stick
-                                ? true
-                                : false
-                            ));
-                        }
-                    };
-
-                    eventsWatcher.onRemoved = function (event) {
-                        if (calendar && calendar.fullCalendar) {
-                            calendar.fullCalendar('removeEvents', event._id);
-                        }
-                    };
-
-                    eventsWatcher.onChanged = function (event) {
-                        if (calendar && calendar.fullCalendar) {
-                            var clientEvents = calendar.fullCalendar('clientEvents', event._id);
-                            for (var i = 0; i < clientEvents.length; i++) {
-                                var clientEvent = clientEvents[i];
-                                clientEvent = angular.extend(clientEvent, event);
-                                calendar.fullCalendar('updateEvent', clientEvent);
-                            }
-                        }
-                    };
-
-                    eventSourcesWatcher.subscribe(scope);
-                    eventsWatcher.subscribe(scope, function () {
-                        if (sourcesChanged === true) {
-                            sourcesChanged = false;
-                            // return false to prevent onAdded/Removed/Changed handlers from firing in this case
-                            return false;
-                        }
-                    });
-
-                    scope.$watch(getOptions, function (newValue, oldValue) {
-                        if (newValue !== oldValue) {
-                            scope.destroyCalendar();
-                            scope.initCalendar();
-                        } else if ((newValue && angular.isUndefined(calendar))) {
-                            scope.initCalendar();
-                        }
-                    });
-                }
-            };
-        }
-    ]
-);
+/*
+*  AngularJs Fullcalendar Wrapper for the JQuery FullCalendar
+*  API @ http://arshaw.com/fullcalendar/
+*
+*  Angular Calendar Directive that takes in the [eventSources] nested array object as the ng-model and watches it deeply changes.
+*       Can also take in multiple event urls as a source object(s) and feed the events per view.
+*       The calendar will watch any eventSource array and update itself when a change is made.
+*
+*/
+
+angular.module('ui.calendar', [])
+
+    .constant('uiCalendarConfig', {
+        calendars : {}
+    })
+    .controller('uiCalendarCtrl', ['$scope', '$locale',
+        function ($scope, $locale) {
+
+            var sources = $scope.eventSources;
+            var extraEventSignature = $scope.calendarWatchEvent ? $scope.calendarWatchEvent : angular.noop;
+
+            var wrapFunctionWithScopeApply = function (functionToWrap) {
+                return function () {
+                    // This may happen outside of angular context, so create one if outside.
+                    if ($scope.$root.$$phase) {
+                        return functionToWrap.apply(this, arguments);
+                    }
+
+                    var args = arguments;
+                    var that = this;
+                    return $scope.$root.$apply(
+                        function () {
+                            return functionToWrap.apply(that, args);
+                        }
+                    );
+                };
+            };
+
+            var eventSerialId = 1;
+            // @return {String} fingerprint of the event object and its properties
+            this.eventFingerprint = function (e) {
+                if (!e._id) {
+                    e._id = eventSerialId++;
+                }
+
+                var extraSignature = extraEventSignature({
+                    event : e
+                }) || '';
+                var start = moment.isMoment(e.start) ? e.start.unix() : (e.start ? moment(e.start).unix() : '');
+                var end = moment.isMoment(e.end) ? e.end.unix() : (e.end ? moment(e.end).unix() : '');
+
+                // This extracts all the information we need from the event. http://jsperf.com/angular-calendar-events-fingerprint/3
+                return [e._id, e.id || '', e.title || '', e.url || '', start, end, e.allDay || '', e.className || '', extraSignature].join('');
+            };
+
+            var sourceSerialId = 1;
+            var sourceEventsSerialId = 1;
+            // @return {String} fingerprint of the source object and its events array
+            this.sourceFingerprint = function (source) {
+                var fp = '' + (source.__id || (source.__id = sourceSerialId++));
+                var events = angular.isObject(source) && source.events;
+
+                if (events) {
+                    fp = fp + '-' + (events.__id || (events.__id = sourceEventsSerialId++));
+                }
+                return fp;
+            };
+
+            // @return {Array} all events from all sources
+            this.allEvents = function () {
+                return Array.prototype.concat.apply(
+                    [],
+                    (sources || []).reduce(
+                        function (previous, source) {
+                            if (angular.isArray(source)) {
+                                previous.push(source);
+                            } else if (angular.isObject(source) && angular.isArray(source.events)) {
+                                var extEvent = Object.keys(source).filter(
+                                    function (key) {
+                                        return (key !== '_id' && key !== 'events');
+                                    }
+                                );
+
+                                source.events.forEach(
+                                    function (event) {
+                                        angular.extend(event, extEvent);
+                                    }
+                                );
+
+                                previous.push(source.events);
+                            }
+                            return previous;
+                        },
+                        []
+                    )
+                );
+            };
+
+            // Track changes in array of objects by assigning id tokens to each element and watching the scope for changes in the tokens
+            // @param {Array|Function} arraySource array of objects to watch
+            // @param tokenFn {Function} that returns the token for a given object
+            // @return {Object}
+            //  subscribe: function(scope, function(newTokens, oldTokens))
+            //    called when source has changed. return false to prevent individual callbacks from firing
+            //  onAdded/Removed/Changed:
+            //    when set to a callback, called each item where a respective change is detected
+            this.changeWatcher = function (arraySource, tokenFn) {
+                var self;
+
+                var getTokens = function () {
+                    return (angular.isFunction(arraySource) ? arraySource() : arraySource).reduce(
+                        function (rslt, el) {
+                            var token = tokenFn(el);
+                            map[token] = el;
+                            rslt.push(token);
+                            return rslt;
+                        },
+                        []
+                    );
+                };
+
+                // @param {Array} a
+                // @param {Array} b
+                // @return {Array} elements in that are in a but not in b
+                // @example
+                //  subtractAsSets([6, 100, 4, 5], [4, 5, 7]) // [6, 100]
+                var subtractAsSets = function (a, b) {
+                    var obj = (b || []).reduce(
+                        function (rslt, val) {
+                            rslt[val] = true;
+                            return rslt;
+                        },
+                        Object.create(null)
+                    );
+                    return (a || []).filter(
+                        function (val) {
+                            return !obj[val];
+                        }
+                    );
+                };
+
+                // Map objects to tokens and vice-versa
+                var map = {};
+
+                // Compare newTokens to oldTokens and call onAdded, onRemoved, and onChanged handlers for each affected event respectively.
+                var applyChanges = function (newTokens, oldTokens) {
+                    var i;
+                    var token;
+                    var replacedTokens = {};
+                    var removedTokens = subtractAsSets(oldTokens, newTokens);
+                    for (i = 0; i < removedTokens.length; i++) {
+                        var removedToken = removedTokens[i];
+                        var el = map[removedToken];
+                        delete map[removedToken];
+                        var newToken = tokenFn(el);
+                        // if the element wasn't removed but simply got a new token, its old token will be different from the current one
+                        if (newToken === removedToken) {
+                            self.onRemoved(el);
+                        } else {
+                            replacedTokens[newToken] = removedToken;
+                            self.onChanged(el);
+                        }
+                    }
+
+                    var addedTokens = subtractAsSets(newTokens, oldTokens);
+                    for (i = 0; i < addedTokens.length; i++) {
+                        token = addedTokens[i];
+                        if (!replacedTokens[token]) {
+                            self.onAdded(map[token]);
+                        }
+                    }
+                };
+
+                self = {
+                    subscribe : function (scope, onArrayChanged) {
+                        scope.$watch(getTokens, function (newTokens, oldTokens) {
+                            var notify = !(onArrayChanged && onArrayChanged(newTokens, oldTokens) === false);
+                            if (notify) {
+                                applyChanges(newTokens, oldTokens);
+                            }
+                        }, true);
+                    },
+                    onAdded : angular.noop,
+                    onChanged : angular.noop,
+                    onRemoved : angular.noop
+                };
+                return self;
+            };
+
+            this.getFullCalendarConfig = function (calendarSettings, uiCalendarConfig) {
+                var config = {};
+
+                angular.extend(config, uiCalendarConfig);
+                angular.extend(config, calendarSettings);
+
+                angular.forEach(config, function (value, key) {
+                    if (typeof value === 'function') {
+                        config[key] = wrapFunctionWithScopeApply(config[key]);
+                    }
+                });
+
+                return config;
+            };
+
+            this.getLocaleConfig = function (fullCalendarConfig) {
+                if (!fullCalendarConfig.lang || fullCalendarConfig.useNgLocale) {
+                    // Configure to use locale names by default
+                    var tValues = function (data) {
+                        // convert {0: "Jan", 1: "Feb", ...} to ["Jan", "Feb", ...]
+                        return Object.keys(data).reduce(
+                            function (rslt, el) {
+                                rslt[el] = data[el];
+                                return rslt;
+                            },
+                            {}
+                        );
+                    };
+
+                    var dtf = $locale.DATETIME_FORMATS;
+                    return {
+                        monthNames : tValues(dtf.MONTH),
+                        monthNamesShort : tValues(dtf.SHORTMONTH),
+                        dayNames : tValues(dtf.DAY),
+                        dayNamesShort : tValues(dtf.SHORTDAY)
+                    };
+                }
+
+                return {};
+            };
+        }
+    ])
+    .directive('uiCalendar', ['uiCalendarConfig',
+        function (uiCalendarConfig) {
+
+            return {
+                restrict : 'A',
+                scope : {
+                    eventSources : '=ngModel',
+                    calendarWatchEvent : '&'
+                },
+                controller : 'uiCalendarCtrl',
+                link : function (scope, elm, attrs, controller) {
+                    var sources = scope.eventSources;
+                    var sourcesChanged = false;
+                    var calendar;
+                    var eventSourcesWatcher = controller.changeWatcher(sources, controller.sourceFingerprint);
+                    var eventsWatcher = controller.changeWatcher(controller.allEvents, controller.eventFingerprint);
+                    var options = null;
+
+                    function getOptions () {
+                        var calendarSettings = attrs.uiCalendar ? scope.$parent.$eval(attrs.uiCalendar) : {};
+                        var fullCalendarConfig = controller.getFullCalendarConfig(calendarSettings, uiCalendarConfig);
+                        var localeFullCalendarConfig = controller.getLocaleConfig(fullCalendarConfig);
+                        angular.extend(localeFullCalendarConfig, fullCalendarConfig);
+                        options = {
+                            eventSources : sources
+                        };
+                        angular.extend(options, localeFullCalendarConfig);
+                        //remove calendars from options
+                        options.calendars = null;
+
+                        var options2 = {};
+                        for (var o in options) {
+                            if (o !== 'eventSources') {
+                                options2[o] = options[o];
+                            }
+                        }
+                        return JSON.stringify(options2);
+                    }
+
+                    scope.destroyCalendar = function () {
+                        if (calendar && calendar.fullCalendar) {
+                            calendar.fullCalendar('destroy');
+                        }
+                        if (attrs.calendar) {
+                            calendar = uiCalendarConfig.calendars[attrs.calendar] = $(elm).html('');
+                        } else {
+                            calendar = $(elm).html('');
+                        }
+                    };
+
+                    scope.initCalendar = function () {
+                        if (!calendar) {
+                            calendar = angular.element(elm).html('');
+                        }
+                        calendar.fullCalendar(options);
+                        if (attrs.calendar) {
+                            uiCalendarConfig.calendars[attrs.calendar] = calendar;
+                        }
+                    };
+
+                    scope.$on('$destroy', function () {
+                        scope.destroyCalendar();
+                    });
+
+                    eventSourcesWatcher.onAdded = function (source) {
+                        if (calendar && calendar.fullCalendar) {
+                            calendar.fullCalendar(options);
+                            if (attrs.calendar) {
+                                uiCalendarConfig.calendars[attrs.calendar] = calendar;
+                            }
+                            calendar.fullCalendar('addEventSource', source);
+                            sourcesChanged = true;
+                        }
+                    };
+
+                    eventSourcesWatcher.onRemoved = function (source) {
+                        if (calendar && calendar.fullCalendar) {
+                            calendar.fullCalendar('removeEventSource', source);
+                            sourcesChanged = true;
+                        }
+                    };
+
+                    eventSourcesWatcher.onChanged = function () {
+                        if (calendar && calendar.fullCalendar) {
+                            calendar.fullCalendar('refetchEvents');
+                            sourcesChanged = true;
+                        }
+                    };
+
+                    eventsWatcher.onAdded = function (event) {
+                        if (calendar && calendar.fullCalendar) {
+                            calendar.fullCalendar('renderEvent', event, !!event.stick);
+                        }
+                    };
+
+                    eventsWatcher.onRemoved = function (event) {
+                        if (calendar && calendar.fullCalendar) {
+                            calendar.fullCalendar('removeEvents', event._id);
+                        }
+                    };
+
+                    eventsWatcher.onChanged = function (event) {
+                        if (calendar && calendar.fullCalendar) {
+                            var clientEvents = calendar.fullCalendar('clientEvents', event._id);
+                            for (var i = 0; i < clientEvents.length; i++) {
+                                var clientEvent = clientEvents[i];
+                                clientEvent = angular.extend(clientEvent, event);
+                                calendar.fullCalendar('updateEvent', clientEvent);
+                            }
+                        }
+                    };
+
+                    eventSourcesWatcher.subscribe(scope);
+                    eventsWatcher.subscribe(scope, function () {
+                        if (sourcesChanged === true) {
+                            sourcesChanged = false;
+                            // return false to prevent onAdded/Removed/Changed handlers from firing in this case
+                            return false;
+                        }
+                    });
+
+                    scope.$watch(getOptions, function (newValue, oldValue) {
+                        if (newValue !== oldValue) {
+                            scope.destroyCalendar();
+                            scope.initCalendar();
+                        } else if ((newValue && angular.isUndefined(calendar))) {
+                            scope.initCalendar();
+                        }
+                    });
+                }
+            };
+        }
+    ]
+);