PageSourceSearch

https://itouch.cycu.edu.tw/active_project/cycu2100h_06/acpm3/js/lib/ui-calendar-master/calendar.js

js cycu.edu.tw collected 2026-09-24 09:00:04 UTC 16,249 bytes, 365 lines download raw bytes

1/*
2*  AngularJs Fullcalendar Wrapper for the JQuery FullCalendar
3*  API @ http://arshaw.com/fullcalendar/
4*
5*  Angular Calendar Directive that takes in the [eventSources] nested array object as the ng-model and watches it deeply changes.
6*       Can also take in multiple event urls as a source object(s) and feed the events per view.
7*       The calendar will watch any eventSource array and update itself when a change is made.
8*
9*/
10
11angular.module('ui.calendar', [])
12
13    .constant('uiCalendarConfig', {
14        calendars : {}
15    })
16    .controller('uiCalendarCtrl', ['$scope', '$locale',
17        function ($scope, $locale) {
18
19            var sources = $scope.eventSources;
20            var extraEventSignature = $scope.calendarWatchEvent ? $scope.calendarWatchEvent : angular.noop;
21
22            var wrapFunctionWithScopeApply = function (functionToWrap) {
23                return function () {
24                    // This may happen outside of angular context, so create one if outside.
25                    if ($scope.$root.$$phase) {
26                        return functionToWrap.apply(this, arguments);
27                    }
28
29                    var args = arguments;
30                    var that = this;
31                    return $scope.$root.$apply(
32                        function () {
33                            return functionToWrap.apply(that, args);
34                        }
35                    );
36                };
37            };
38
39            var eventSerialId = 1;
40            // @return {String} fingerprint of the event object and its properties
41            this.eventFingerprint = function (e) {
42                if (!e._id) {
43                    e._id = eventSerialId++;
44                }
45
46                var extraSignature = extraEventSignature({
47                    event : e
48                }) || '';
49                var start = moment.isMoment(e.start) ? e.start.unix() : (e.start ? moment(e.start).unix() : '');
50                var end = moment.isMoment(e.end) ? e.end.unix() : (e.end ? moment(e.end).unix() : '');
51
52                // This extracts all the information we need from the event. http://jsperf.com/angular-calendar-events-fingerprint/3
53                return [e._id, e.id || '', e.title || '', e.url || '', start, end, e.allDay || '', e.className || '', extraSignature].join('');
54            };
55
56            var sourceSerialId = 1;
57            var sourceEventsSerialId = 1;
58            // @return {String} fingerprint of the source object and its events array
59            this.sourceFingerprint = function (source) {
60                var fp = '' + (source.__id || (source.__id = sourceSerialId++));
61                var events = angular.isObject(source) && source.events;
62
63                if (events) {
64                    fp = fp + '-' + (events.__id || (events.__id = sourceEventsSerialId++));
65                }
66                return fp;
67            };
68
69            // @return {Array} all events from all sources
70            this.allEvents = function () {
71                return Array.prototype.concat.apply(
72                    [],
73                    (sources || []).reduce(
74                        function (previous, source) {
75                            if (angular.isArray(source)) {
76                                previous.push(source);
77                            } else if (angular.isObject(source) && angular.isArray(source.events)) {
78                                var extEvent = Object.keys(source).filter(
79                                    function (key) {
80                                        return (key !== '_id' && key !== 'events');
81                                    }
82                                );
83
84                                source.events.forEach(
85                                    function (event) {
86                                        angular.extend(event, extEvent);
87                                    }
88                                );
89
90                                previous.push(source.events);
91                            }
92                            return previous;
93                        },
94                        []
95                    )
96                );
97            };
98
99            // Track changes in array of objects by assigning id tokens to each element and watching the scope for changes in the tokens
100            // @param {Array|Function}
100 arraySource array of objects to watch
101            // @param tokenFn {Function} that returns the token for a given object
102            // @return {Object}
103            //  subscribe: function(scope, function(newTokens, oldTokens))
104            //    called when source has changed. return false to prevent individual callbacks from firing
105            //  onAdded/Removed/Changed:
106            //    when set to a callback, called each item where a respective change is detected
107            this.changeWatcher = function (arraySource, tokenFn) {
108                var self;
109
110                var getTokens = function () {
111                    return ((angular.isFunction(arraySource) ? arraySource() : arraySource) || []).reduce(
112                        function (rslt, el) {
113                            var token = tokenFn(el);
114                            map[token] = el;
115                            rslt.push(token);
116                            return rslt;
117                        },
118                        []
119                    );
120                };
121
122                // @param {Array} a
123                // @param {Array} b
124                // @return {Array} elements in that are in a but not in b
125                // @example
126                //  subtractAsSets([6, 100, 4, 5], [4, 5, 7]) // [6, 100]
127                var subtractAsSets = function (a, b) {
128                    var obj = (b || []).reduce(
129                        function (rslt, val) {
130                            rslt[val] = true;
131                            return rslt;
132                        },
133                        Object.create(null)
134                    );
135                    return (a || []).filter(
136                        function (val) {
137                            return !obj[val];
138                        }
139                    );
140                };
141
142                // Map objects to tokens and vice-versa
143                var map = {};
144
145                // Compare newTokens to oldTokens and call onAdded, onRemoved, and onChanged handlers for each affected event respectively.
146                var applyChanges = function (newTokens, oldTokens) {
147                    var i;
148                    var token;
149                    var replacedTokens = {};
150                    var removedTokens = subtractAsSets(oldTokens, newTokens);
151                    for (i = 0; i < removedTokens.length; i++) {
152                        var removedToken = removedTokens[i];
153                        var el = map[removedToken];
154                        delete map[removedToken];
155                        var newToken = tokenFn(el);
156                        // if the element wasn't removed but simply got a new token, its old token will be different from the current one
157                        if (newToken === removedToken) {
158                            self.onRemoved(el);
159                        } else {
160                            replacedTokens[newToken] = removedToken;
161                            self.onChanged(el);
162                        }
163                    }
164
165                    var addedTokens = subtractAsSets(newTokens, oldTokens);
166                    for (i = 0; i < addedTokens.length; i++) {
167                        token = addedTokens[i];
168                        if (!replacedTokens[token]) {
169                            self.onAdded(map[token]);
170                        }
171                    }
172                };
173
174                self = {
175                    subscribe : function (scope, onArrayChanged) {
176                        scope.$watch(getTokens, function (newTokens, oldTokens) {
177                            var notify = !(onArrayChanged && onArrayChanged(newTokens, oldTokens) === false);
178                            if (notify) {
179                                applyChanges(newTokens, oldTokens);
180                            }
181                        }, true);
182                    },
183                    onAdded : angular.noop,
184                    onChanged : angular.noop,
185                    onRemoved : angular.noop
186                };
187                return self;
188            };
189
190            this.getFullCalendarConfig = function (calendarSettings, uiCalendarConfig) {
191                var config = {};
192
193                angular.extend(config, uiCalendarConfig);
194                angular.extend(config, calendarSettings);
195
196                angular.forEach(config, function (value, key) {
197                    if (typeof value === 'function') {
198                        config[key] = wrapFunctionWithScopeApply(config[key]);
199                    }
200                });
201
202                return config;
203            };
204
205            this.getLocaleConfig = function (fullCalendarConfig) {
206                if (!fullCalendarConfig.lang && !fullCalendarConfig.locale || fullCalendarConfig.useNgLocale) {
207                    // Configure to use locale names by default
208                    var tValues = function (data) {
209                        // convert {0: "Jan", 1: "Feb", ...} to ["Jan", "Feb", ...]
210                        return (Object.keys(data) || []).reduce(
211                            function (rslt, el) {
212                                rslt.push(data[el]);
213                                return rslt;
214                            },
215                            []
216                        );
217                    };
218
219                    var dtf = $locale.DATETIME_FORMATS;
220                    return {
221                        monthNames : tValues(dtf.MONTH),
222                        monthNamesShort : tValues(dtf.SHORTMONTH),
223                        dayNames : tValues(dtf.DAY),
224                        dayNamesShort : tValues(dtf.SHORTDAY)
225                    };
226                }
227
228                return {};
229            };
230        }
231    ])
232    .directive('uiCalendar', ['uiCalendarConfig',
233        function (uiCalendarConfig) {
234
235            return {
236                restrict : 'A',
237                scope : {
238                    eventSources : '=ngModel',
239                    calendarWatchEvent : '&'
240                },
241                controller : 'uiCalendarCtrl',
242                link : function (scope, elm, attrs, controller) {
243                    var sources = scope.eventSources;
244                    var sourcesChanged = false;
245                    var calendar;
246                    var eventSourcesWatcher = controller.changeWatcher(sources, controller.sourceFingerprint);
247                    var eventsWatcher = controller.changeWatcher(controller.allEvents, controller.eventFingerprint);
248                    var options = null;
249
250                    function getOptions () {
251                        var calendarSettings = attrs.uiCalendar ? scope.$parent.$eval(attrs.uiCalendar) : {};
252                        var fullCalendarConfig = controller.getFullCalendarConfig(calendarSettings, uiCalendarConfig);
253                        var localeFullCalendarConfig = controller.getLocaleConfig(fullCalendarConfig);
254                        angular.extend(localeFullCalendarConfig, fullCalendarConfig);
255                        options = {
256                            eventSources : sources
257                        };
258                        angular.extend(options, localeFullCalendarConfig);
259                        //remove calendars from options
260                        options.calendars = null;
261
262                        var options2 = {};
263                        for (var o in options) {
264                            if (o !== 'eventSources') {
265                                options2[o] = options[o];
266                            }
267                        }
268                        return JSON.stringify(options2);
269                    }
270
271                    scope.destroyCalendar = function () {
272                        if (calendar && calendar.fullCalendar) {
273                            calendar.fullCalendar('destroy');
274                        }
275                        if (attrs.calendar) {
276                            calendar = uiCalendarConfig.calendars[attrs.calendar] = angular.element(elm).html('');
277                        } else {
278                            calendar = angular.element(elm).html('');
279                        }
280                    };
281
282                    scope.initCalendar = function () {
283                        if (!calendar) {
284                            calendar = $(elm).html('');
285                        }
286                        calendar.fullCalendar(options);
287                        if (attrs.calendar) {
288                            uiCalendarConfig.calendars[attrs.calendar] = calendar;
289                        }
290                    };
291
292                    scope.$on('$destroy', function () {
293                        scope.destroyCalendar();
294                    });
295
296                    eventSourcesWatcher.onAdded = function (source) {
297                        if (calendar && calendar.fullCalendar) {
298                            calendar.fullCalendar(options);
299                            if (attrs.calendar) {
300                                uiCalendarConfig.calendars[attrs.calendar] = calendar;
301                            }
302                            calendar.fullCalendar('addEventSource', source);
303                            sourcesChanged = true;
304                        }
305                    };
306
307                    eventSourcesWatcher.onRemoved = function (source) {
308                        if (calendar && calendar.fullCalendar) {
309                            calendar.fullCalendar('removeEventSource', source);
310                            sourcesChanged = true;
311                        }
312                    };
313
314                    eventSourcesWatcher.onChanged = function () {
315                        if (calendar && calendar.fullCalendar) {
316                            calendar.fullCalendar('refetchEvents');
317                            sourcesChanged = true;
318                        }
319                    };
320
321                    eventsWatcher.onAdded = function (event) {
322                        if (calendar && calendar.fullCalendar) {
323                            calendar.fullCalendar('renderEvent', event, !!event.stick);
324                        }
325                    };
326
327                    eventsWatcher.onRemoved = function (event) {
328                        if (calendar && calendar.fullCalendar) {
329                            calendar.fullCalendar('removeEvents', event._id);
330                        }
331                    };
332
333                    eventsWatcher.onChanged = function (event) {
334                        if (calendar && calendar.fullCalendar) {
335                            var clientEvents = calendar.fullCalendar('clientEvents', event._id);
336                            for (var i = 0; i < clientEvents.length; i++) {
337                                var clientEvent = clientEvents[i];
338                                clientEvent = angular.extend(clientEvent, event);
339                                calendar.fullCalendar('updateEvent', clientEvent);
340                            }
341                        }
342                    };
343
344                    eventSourcesWatcher.subscribe(scope);
345                    eventsWatcher.subscribe(scope, function () {
346                        if (sourcesChanged === true) {
347                            sourcesChanged = false;
348                            // return false to prevent onAdded/Removed/Changed handlers from firing in this case
349                            return false;
350                        }
351                    });
352
353                    scope.$watch(getOptions, function (newValue, oldValue) {
354                        if (newValue !== oldValue) {
355                            scope.destroyCalendar();
356                            scope.initCalendar();
357                        } else if ((newValue && angular.isUndefined(calendar))) {
358                            scope.initCalendar();
359                        }
360                    });
361                }
362            };
363        }
364    ]
365);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.