From 38b8a2a8c5689dc1fd48892e610b610fdd857e97 Mon Sep 17 00:00:00 2001 From: Pedro Pereira Date: Wed, 18 Jan 2017 19:08:48 -0200 Subject: [PATCH 1/4] Adding AMD support --- src/calendar.js | 633 ++++++++++++++++++++++++------------------------ 1 file changed, 320 insertions(+), 313 deletions(-) diff --git a/src/calendar.js b/src/calendar.js index 7ac3056..ce20756 100644 --- a/src/calendar.js +++ b/src/calendar.js @@ -7,359 +7,366 @@ * 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); +if (typeof define === 'function' && define.amd) { + define(['angular', 'moment', 'fullcalendar'], factory); +} else { + factory(); +} + +function factory(angular, moment, fullcalendar) { + return 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 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]; - } - ); + var args = arguments; + var that = this; + return $scope.$root.$apply( + function () { + return functionToWrap.apply(that, args); + } + ); + }; }; - // 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 eventSerialId = 1; + // @return {String} fingerprint of the event object and its properties + this.eventFingerprint = function (e) { + if (!e._id) { + e._id = eventSerialId++; } - var addedTokens = subtractAsSets(newTokens, oldTokens); - for (i = 0; i < addedTokens.length; i++) { - token = addedTokens[i]; - if (!replacedTokens[token]) { - self.onAdded(map[token]); - } - } - }; + 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() : ''); - 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 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(''); }; - return self; - }; - this.getFullCalendarConfig = function (calendarSettings, uiCalendarConfig) { - var config = {}; + 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; - angular.extend(config, uiCalendarConfig); - angular.extend(config, calendarSettings); - - angular.forEach(config, function (value, key) { - if (typeof value === 'function') { - config[key] = wrapFunctionWithScopeApply(config[key]); + if (events) { + fp = fp + '-' + (events.__id || (events.__id = sourceEventsSerialId++)); } - }); + return fp; + }; - return config; - }; + // @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; + }, + [] + ) + ); + }; - this.getLocaleConfig = function (fullCalendarConfig) { - if (!fullCalendarConfig.lang && !fullCalendarConfig.locale || 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( + // 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) { - rslt.push(data[el]); + var token = tokenFn(el); + map[token] = el; + rslt.push(token); return rslt; }, [] ); }; - var dtf = $locale.DATETIME_FORMATS; - return { - monthNames : tValues(dtf.MONTH), - monthNamesShort : tValues(dtf.SHORTMONTH), - dayNames : tValues(dtf.DAY), - dayNamesShort : tValues(dtf.SHORTDAY) + // @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]; + } + ); }; - } - - 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]; + + // 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); } } - return JSON.stringify(options2); - } - scope.destroyCalendar = function () { - if (calendar && calendar.fullCalendar) { - calendar.fullCalendar('destroy'); - } - if (attrs.calendar) { - calendar = uiCalendarConfig.calendars[attrs.calendar] = angular.element(elm).html(''); - } else { - calendar = angular.element(elm).html(''); + var addedTokens = subtractAsSets(newTokens, oldTokens); + for (i = 0; i < addedTokens.length; i++) { + token = addedTokens[i]; + if (!replacedTokens[token]) { + self.onAdded(map[token]); + } } }; - scope.initCalendar = function () { - if (!calendar) { - calendar = $(elm).html(''); - } - calendar.fullCalendar(options); - if (attrs.calendar) { - uiCalendarConfig.calendars[attrs.calendar] = calendar; - } + 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); - scope.$on('$destroy', function () { - scope.destroyCalendar(); + angular.forEach(config, function (value, key) { + if (typeof value === 'function') { + config[key] = wrapFunctionWithScopeApply(config[key]); + } }); - eventSourcesWatcher.onAdded = function (source) { - if (calendar && calendar.fullCalendar) { + return config; + }; + + this.getLocaleConfig = function (fullCalendarConfig) { + if (!fullCalendarConfig.lang && !fullCalendarConfig.locale || 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.push(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] = angular.element(elm).html(''); + } else { + calendar = angular.element(elm).html(''); + } + }; + + scope.initCalendar = function () { + if (!calendar) { + calendar = $(elm).html(''); + } 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; - } - }; + 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.onChanged = function () { - if (calendar && calendar.fullCalendar) { - calendar.fullCalendar('refetchEvents'); - sourcesChanged = true; - } - }; + eventSourcesWatcher.onRemoved = function (source) { + if (calendar && calendar.fullCalendar) { + calendar.fullCalendar('removeEventSource', source); + sourcesChanged = true; + } + }; - eventsWatcher.onAdded = function (event) { - if (calendar && calendar.fullCalendar) { - calendar.fullCalendar('renderEvent', event, !!event.stick); - } - }; + eventSourcesWatcher.onChanged = function () { + if (calendar && calendar.fullCalendar) { + calendar.fullCalendar('refetchEvents'); + sourcesChanged = true; + } + }; - eventsWatcher.onRemoved = function (event) { - if (calendar && calendar.fullCalendar) { - calendar.fullCalendar('removeEvents', event._id); - } - }; + eventsWatcher.onAdded = function (event) { + if (calendar && calendar.fullCalendar) { + calendar.fullCalendar('renderEvent', event, !!event.stick); + } + }; - 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); + eventsWatcher.onRemoved = function (event) { + if (calendar && calendar.fullCalendar) { + calendar.fullCalendar('removeEvents', event._id); } - } - }; + }; - 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; - } - }); + 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); + } + } + }; - scope.$watch(getOptions, function (newValue, oldValue) { - if (newValue !== oldValue) { - scope.destroyCalendar(); - scope.initCalendar(); - } else if ((newValue && angular.isUndefined(calendar))) { - scope.initCalendar(); - } - }); - } - }; - } - ] -); + 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(); + } + }); + } + }; + } + ] + ); +} \ No newline at end of file From cdb9de1fc4cda05427d6c8b19c9f98ca74f308f1 Mon Sep 17 00:00:00 2001 From: Pedro Pereira Date: Mon, 6 Mar 2017 02:26:31 -0300 Subject: [PATCH 2/4] Adding global --- src/calendar.js | 622 ++++++++++++++++++++++++------------------------ 1 file changed, 312 insertions(+), 310 deletions(-) diff --git a/src/calendar.js b/src/calendar.js index ce20756..1d56b49 100644 --- a/src/calendar.js +++ b/src/calendar.js @@ -7,366 +7,368 @@ * The calendar will watch any eventSource array and update itself when a change is made. * */ -if (typeof define === 'function' && define.amd) { - define(['angular', 'moment', 'fullcalendar'], factory); -} else { - factory(); -} -function factory(angular, moment, fullcalendar) { - return angular.module('ui.calendar', []) +(function(global, factory) { + if (typeof define === "function" && define.amd) { + define(['angular', 'moment', 'fullcalendar'], factory); + } else if (typeof exports === "object") { + module.exports = factory(require('angular'), require('moment'), require('fullcalendar')); + } else { + global.uiCalendarDirective = factory(global.angular, global.moment, global.fullcalendar); + } +})(this, function(angular) { + 'use strict'; + angular.module('ui.calendar', []) .constant('uiCalendarConfig', { calendars : {} }) - .controller('uiCalendarCtrl', ['$scope', '$locale', - function ($scope, $locale) { + .controller('uiCalendarCtrl', ['$scope', '$locale', function ($scope, $locale) { - var sources = $scope.eventSources; - var extraEventSignature = $scope.calendarWatchEvent ? $scope.calendarWatchEvent : angular.noop; + 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 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 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 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({ + 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() : ''); + 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; + }, + [] + ); + }; - // 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(''); + // @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]; + } + ); }; - 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; + // 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); + } + } - if (events) { - fp = fp + '-' + (events.__id || (events.__id = sourceEventsSerialId++)); + var addedTokens = subtractAsSets(newTokens, oldTokens); + for (i = 0; i < addedTokens.length; i++) { + token = addedTokens[i]; + if (!replacedTokens[token]) { + self.onAdded(map[token]); + } } - 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; - }, - [] - ) - ); + 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; + }; - // 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( + 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.locale || 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) { - var token = tokenFn(el); - map[token] = el; - rslt.push(token); + rslt.push(data[el]); 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]; - } - ); + var dtf = $locale.DATETIME_FORMATS; + return { + monthNames : tValues(dtf.MONTH), + monthNamesShort : tValues(dtf.SHORTMONTH), + dayNames : tValues(dtf.DAY), + dayNamesShort : tValues(dtf.SHORTDAY) }; - - // 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); + } + + 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); + } - var addedTokens = subtractAsSets(newTokens, oldTokens); - for (i = 0; i < addedTokens.length; i++) { - token = addedTokens[i]; - if (!replacedTokens[token]) { - self.onAdded(map[token]); - } + scope.destroyCalendar = function () { + if (calendar && calendar.fullCalendar) { + calendar.fullCalendar('destroy'); + } + if (attrs.calendar) { + calendar = uiCalendarConfig.calendars[attrs.calendar] = angular.element(elm).html(''); + } else { + calendar = angular.element(elm).html(''); } }; - 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]); + scope.initCalendar = function () { + if (!calendar) { + calendar = $(elm).html(''); } - }); - - return config; - }; - - this.getLocaleConfig = function (fullCalendarConfig) { - if (!fullCalendarConfig.lang && !fullCalendarConfig.locale || 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.push(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); + calendar.fullCalendar(options); + if (attrs.calendar) { + uiCalendarConfig.calendars[attrs.calendar] = calendar; } + }; - scope.destroyCalendar = function () { - if (calendar && calendar.fullCalendar) { - calendar.fullCalendar('destroy'); - } - if (attrs.calendar) { - calendar = uiCalendarConfig.calendars[attrs.calendar] = angular.element(elm).html(''); - } else { - calendar = angular.element(elm).html(''); - } - }; + scope.$on('$destroy', function () { + scope.destroyCalendar(); + }); - scope.initCalendar = function () { - if (!calendar) { - calendar = $(elm).html(''); - } + 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; + } + }; - 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.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; + } + }; - 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.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.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); } - }; + } + }; - 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(); + } + }); + } + }; + }]); +}); +function factory(angular, moment, fullcalendar) { - 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(); - } - }); - } - }; - } - ] - ); } \ No newline at end of file From d5d6a360583399562f3bdc58cf82f83d8b041e40 Mon Sep 17 00:00:00 2001 From: Pedro Pereira Date: Sun, 12 Mar 2017 16:28:13 -0300 Subject: [PATCH 3/4] Bugfix jquery dependencies --- src/calendar.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/calendar.js b/src/calendar.js index 1d56b49..e94fe9e 100644 --- a/src/calendar.js +++ b/src/calendar.js @@ -10,13 +10,13 @@ (function(global, factory) { if (typeof define === "function" && define.amd) { - define(['angular', 'moment', 'fullcalendar'], factory); + define(['angular', 'jquery', 'moment', 'fullcalendar'], factory); } else if (typeof exports === "object") { - module.exports = factory(require('angular'), require('moment'), require('fullcalendar')); + module.exports = factory(require('angular'), require('jquery'), require('moment'), require('fullcalendar')); } else { - global.uiCalendarDirective = factory(global.angular, global.moment, global.fullcalendar); + global.uiCalendarDirective = factory(global.angular, global.jQuery, global.moment, global.fullcalendar); } -})(this, function(angular) { +})(this, function(angular, $) { 'use strict'; angular.module('ui.calendar', []) From ac40e05eabaeed41efbcd50fa357ed22964376b4 Mon Sep 17 00:00:00 2001 From: Pedro Pereira Date: Sun, 12 Mar 2017 22:38:56 -0300 Subject: [PATCH 4/4] Release --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index feaad90..cb4f0f5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "angular-ui-calendar", - "version": "1.0.2", + "version": "1.0.4", "description": "A complete AngularJS directive for the Arshaw FullCalendar.", "author": "https://github.com/angular-ui/ui-calendar/graphs/contributors", "license": "MIT",