Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(618)

Side by Side Diff: chrome/common/extensions/docs/examples/extensions/calendar/javascript/background.js

Issue 890173003: Replace the sample Calendar extension with a deprecation notice (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: . Created 5 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /** 1 /**
2 * Copyright (c) 2013 The Chromium Authors. All rights reserved. Use of this 2 * Copyright (c) 2013 The Chromium Authors. All rights reserved. Use of this
3 * source code is governed by a BSD-style license that can be found in the 3 * source code is governed by a BSD-style license that can be found in the
4 * LICENSE file. 4 * LICENSE file.
5 */ 5 */
6 6
7 /** 7 var warningId = 'notification.warning';
8 * PHASES
9 * 1) Load next event from server refresh every 30 minutes or every time
10 * you go to calendar or every time you logout drop in a data object.
11 * 2) Display on screen periodically once per minute or on demand.
12 */
13 8
14 // Message shown in badge title when no title is given to an event. 9 function hideWarning(done) {
15 var MSG_NO_TITLE = chrome.i18n.getMessage('noTitle'); 10 chrome.notifications.clear(warningId, function() {
16 11 if (done) done();
17 // Time between server polls = 30 minutes. 12 });
18 var POLL_INTERVAL = 30 * 60 * 1000;
19
20 // Redraw interval is 5 min.
21 var DRAW_INTERVAL_MINUTES = 5.0;
22
23 // Object for BadgeAnimation
24 var badgeAnimation_;
25
26 //Object for CanvasAnimation
27 var canvasAnimation_;
28
29 var pollUnderProgress = false;
30 var defaultAuthor = '';
31 var isMultiCalendar = false;
32
33 // The Calendar API identifies requests from this extension by the 'extid' get
34 // parameter, set to this extension's ID.
35 var API_KEY = 'extid=' + chrome.runtime.id;
36
37 //URL for getting feed of individual calendar support.
38 var SINGLE_CALENDAR_SUPPORT_URL = 'https://www.google.com/calendar/feeds' +
39 '/default/private/embed?toolbar=true&max-results=10&' + API_KEY;
40
41 //URL for getting feed of multiple calendar support.
42 var MULTIPLE_CALENDAR_SUPPORT_URL = 'https://www.google.com/calendar/feeds' +
43 '/default/allcalendars/full?' + API_KEY;
44
45 //URL for opening Google Calendar in new tab.
46 var GOOGLE_CALENDAR_URL = 'http://www.google.com/calendar/render';
47
48 //URL for declining invitation of the event.
49 var DECLINED_URL = 'http://schemas.google.com/g/2005#event.declined';
50
51 //This is used to poll only once per second at most, and delay that if
52 //we keep hitting pages that would otherwise force a load.
53 var pendingLoadId_ = null;
54
55 /**
56 * Sets |key| as |value| in localStorage. |value| may be any JavaScript object;
57 * this method will automatically stringify to JSON if needed.
58 */
59 function localStorageSet(key, value) {
60 if (typeof value == 'undefined') {
61 // Don't try to stringify undefined, or bad things may happen (particularly
62 // in localStorageGet, so let's be consistent).
63 delete localStorage[key];
64 } else {
65 localStorage[key] = JSON.stringify(value);
66 }
67 } 13 }
68 14
69 /** 15 function showWarning() {
70 * Gets the JavaScript object at |key| from localStorage, defaulting to |deflt| 16 hideWarning(function() {
71 * if it hasn't been set. Assumes that the value was written by localStorageSet 17 chrome.notifications.create(warningId, {
72 * (i.e. stored as JSON). 18 iconUrl: chrome.runtime.getURL('images/icon-48.png'),
73 */ 19 title: 'Removal required',
74 function localStorageGet(key, deflt) { 20 type: 'basic',
75 var value = localStorage[key]; 21 message: chrome.i18n.getMessage('name') + ' is obsolete ' +
76 return (typeof value == 'undefined') ? deflt : JSON.parse(value); 22 'and must be removed. A replacement Extension ' +
23 'is available.',
24 buttons: [{ title: 'Learn More' }],
25 isClickable: true,
26 priority: 2,
27 }, function() {});
28 });
77 } 29 }
78 30
79 /** 31 function openWarningPage() {
80 * A "loading" animation displayed while we wait for the first response from 32 chrome.tabs.create({
81 * Calendar. This animates the badge text with a dot that cycles from left to 33 url: 'chrome://extensions?options=' + chrome.runtime.id
82 * right.
83 * @constructor
84 */
85 function BadgeAnimation() {
86 this.timerId_ = 0;
87 this.maxCount_ = 8; // Total number of states in animation
88 this.current_ = 0; // Current state
89 this.maxDot_ = 4; // Max number of dots in animation
90 };
91
92 /**
93 * Paints the badge text area while loading the data.
94 */
95 BadgeAnimation.prototype.paintFrame = function() {
96 var text = '';
97 for (var i = 0; i < this.maxDot_; i++) {
98 text += (i == this.current_) ? '.' : ' ';
99 }
100
101 chrome.browserAction.setBadgeText({text: text});
102 this.current_++;
103 if (this.current_ == this.maxCount_) {
104 this.current_ = 0;
105 }
106 };
107
108 /**
109 * Starts the animation process.
110 */
111 BadgeAnimation.prototype.start = function() {
112 if (this.timerId_) {
113 return;
114 }
115
116 var self = this;
117 this.timerId_ = window.setInterval(function() {
118 self.paintFrame();
119 }, 100);
120 };
121
122 /**
123 * Stops the animation process.
124 */
125 BadgeAnimation.prototype.stop = function() {
126 if (!this.timerId_) {
127 return;
128 }
129
130 window.clearInterval(this.timerId_);
131 this.timerId_ = 0;
132 };
133
134 /**
135 * Animates the canvas after loading the data from all the calendars. It
136 * rotates the icon and defines the badge text and title.
137 * @constructor
138 */
139 function CanvasAnimation() {
140 this.animationFrames_ = 36; // The number of animation frames
141 this.animationSpeed_ = 10; // Time between each frame(in ms).
142 this.canvas_ = $('canvas'); // The canvas width + height.
143 this.canvasContext_ = this.canvas_.getContext('2d'); // Canvas context.
144 this.loggedInImage_ = $('logged_in');
145 this.rotation_ = 0; //Keeps count of rotation angle of extension icon.
146 this.w = this.canvas_.width; // Setting canvas width.
147 this.h = this.canvas_.height; // Setting canvas height.
148 this.RED = [208, 0, 24, 255]; //Badge color of extension icon in RGB format.
149 this.BLUE = [0, 24, 208, 255];
150 this.currentBadge_ = null; // The text in the current badge.
151 };
152
153 /**
154 * Flips the icon around and draws it.
155 */
156 CanvasAnimation.prototype.animate = function() {
157 this.rotation_ += (1 / this.animationFrames_);
158 this.drawIconAtRotation();
159 var self = this;
160 if (this.rotation_ <= 1) {
161 setTimeout(function() {
162 self.animate();
163 }, self.animationSpeed_);
164 } else {
165 this.drawFinal();
166 }
167 };
168
169 /**
170 * Renders the icon.
171 */
172 CanvasAnimation.prototype.drawIconAtRotation = function() {
173 this.canvasContext_.save();
174 this.canvasContext_.clearRect(0, 0, this.w, this.h);
175 this.canvasContext_.translate(Math.ceil(this.w / 2), Math.ceil(this.h / 2));
176 this.canvasContext_.rotate(2 * Math.PI * this.getSector(this.rotation_));
177 this.canvasContext_.drawImage(this.loggedInImage_, -Math.ceil(this.w / 2),
178 -Math.ceil(this.h / 2));
179 this.canvasContext_.restore();
180 chrome.browserAction.setIcon(
181 {imageData: this.canvasContext_.getImageData(0, 0, this.w, this.h)});
182 };
183
184 /**
185 * Calculates the sector which has to be traversed in a single call of animate
186 * function(360/animationFrames_ = 360/36 = 10 radians).
187 * @param {integer} sector angle to be rotated(in radians).
188 * @return {integer} value in radian of the sector which it has to cover.
189 */
190 CanvasAnimation.prototype.getSector = function(sector) {
191 return (1 - Math.sin(Math.PI / 2 + sector * Math.PI)) / 2;
192 };
193
194 /**
195 * Draws the event icon and determines the badge title and icon title.
196 */
197 CanvasAnimation.prototype.drawFinal = function() {
198 badgeAnimation_.stop();
199 var nextEvent = localStorageGet('nextEvent');
200 if (!nextEvent) {
201 this.showLoggedOut();
202 } else {
203 this.drawIconAtRotation();
204 this.rotation_ = 0;
205
206 var ms = (new Date(nextEvent.startTime)).getTime() - getCurrentTime();
207 var nextEventMin = ms / (1000 * 60);
208 var bgColor = (nextEventMin < 60) ? this.RED : this.BLUE;
209
210 chrome.browserAction.setBadgeBackgroundColor({color: bgColor});
211 currentBadge_ = this.getBadgeText(nextEvent);
212 chrome.browserAction.setBadgeText({text: currentBadge_});
213 var nextEvents = localStorageGet('nextEvents');
214 if (nextEvents.length > 0) {
215 var text = '';
216 for (var i = 0, event; event = nextEvents[i]; i++) {
217 text += event.title;
218 if (event.author || event.location) {
219 text += '\n';
220 }
221 if (event.location) {
222 text += event.location + ' ';
223 }
224 if (event.author) {
225 text += event.author;
226 }
227 if (i < (nextEvents.length - 1)) {
228 text += '\n----------\n';
229 }
230 }
231 text = filterSpecialChar(text);
232 chrome.browserAction.setTitle({'title' : text});
233 }
234 }
235 pollUnderProgress = false;
236
237 return;
238 };
239
240 /**
241 * Shows the user logged out.
242 */
243 CanvasAnimation.prototype.showLoggedOut = function() {
244 currentBadge_ = '?';
245 chrome.browserAction.setIcon({path: '../images/icon-16_bw.gif'});
246 chrome.browserAction.setBadgeBackgroundColor({color: [190, 190, 190, 230]});
247 chrome.browserAction.setBadgeText({text: '?'});
248 chrome.browserAction.setTitle({ 'title' : ''});
249 };
250
251 /**
252 * Gets the badge text.
253 * @param {Object} nextEvent next event in the calendar.
254 * @return {String} text Badge text to be shown in extension icon.
255 */
256 CanvasAnimation.prototype.getBadgeText = function(nextEvent) {
257 if (!nextEvent) {
258 return '';
259 }
260
261 var ms = (new Date(nextEvent.startTime)).getTime() - getCurrentTime();
262 var nextEventMin = Math.ceil(ms / (1000 * 60));
263
264 var text = '';
265 if (nextEventMin < 60) {
266 text = chrome.i18n.getMessage('minutes', nextEventMin.toString());
267 } else if (nextEventMin < 1440) {
268 text = chrome.i18n.getMessage('hours',
269 Math.round(nextEventMin / 60).toString());
270 } else if (nextEventMin < (1440 * 10)) {
271 text = chrome.i18n.getMessage('days',
272 Math.round(nextEventMin / 60 / 24).toString());
273 }
274 return text;
275 };
276
277 /**
278 * Provides all the calendar related utils.
279 */
280 CalendarManager = {};
281
282 /**
283 * Extracts event from the each entry of the calendar.
284 * @param {Object} elem The XML node to extract the event from.
285 * @param {Object} mailId email of the owner of calendar in multiple calendar
286 * support.
287 * @return {Object} out An object containing the event properties.
288 */
289 CalendarManager.extractEvent = function(elem, mailId) {
290 var out = {};
291
292 for (var node = elem.firstChild; node != null; node = node.nextSibling) {
293 if (node.nodeName == 'title') {
294 out.title = node.firstChild ? node.firstChild.nodeValue : MSG_NO_TITLE;
295 } else if (node.nodeName == 'link' &&
296 node.getAttribute('rel') == 'alternate') {
297 out.url = node.getAttribute('href');
298 } else if (node.nodeName == 'gd:where') {
299 out.location = node.getAttribute('valueString');
300 } else if (node.nodeName == 'gd:who') {
301 if (node.firstChild) {
302 if ((!isMultiCalendar) || (isMultiCalendar && mailId &&
303 node.getAttribute('email') == mailId)) {
304 out.attendeeStatus = node.firstChild.getAttribute('value');
305 }
306 }
307 } else if (node.nodeName == 'gd:eventStatus') {
308 out.status = node.getAttribute('value');
309 } else if (node.nodeName == 'gd:when') {
310 var startTimeStr = node.getAttribute('startTime');
311 var endTimeStr = node.getAttribute('endTime');
312
313 startTime = rfc3339StringToDate(startTimeStr);
314 endTime = rfc3339StringToDate(endTimeStr);
315
316 if (startTime == null || endTime == null) {
317 continue;
318 }
319
320 out.isAllDay = (startTimeStr.length <= 11);
321 out.startTime = startTime;
322 out.endTime = endTime;
323 }
324 }
325 return out;
326 };
327
328 /**
329 * Polls the server to get the feed of the user.
330 */
331 CalendarManager.pollServer = function() {
332 if (! pollUnderProgress) {
333 localStorageSet('eventList', []);
334 pollUnderProgress = true;
335 pendingLoadId_ = null;
336 localStorageSet('calendars', []);
337 localStorageSet('lastPollTime', getCurrentTime());
338 var url;
339 var xhr = new XMLHttpRequest();
340 try {
341 xhr.onreadystatechange = CalendarManager.genResponseChangeFunc(xhr);
342 xhr.onerror = function(error) {
343 console.error('error:', error);
344 localStorageSet('nextEvent', null);
345 canvasAnimation_.drawFinal();
346 };
347 if (isMultiCalendar) {
348 url = MULTIPLE_CALENDAR_SUPPORT_URL;
349 } else {
350 url = SINGLE_CALENDAR_SUPPORT_URL;
351 }
352
353 xhr.open('GET', url);
354 xhr.send(null);
355 } catch (e) {
356 console.error('ex:', e);
357 localStorageSet('nextEvent', null);
358 canvasAnimation_.drawFinal();
359 }
360 }
361 };
362
363 /**
364 * Gathers the list of all calendars of a specific user for multiple calendar
365 * support and event entries in single calendar.
366 * @param {xmlHttpRequest} xhr xmlHttpRequest object containing server response.
367 * @return {Object} anonymous function which returns to onReadyStateChange.
368 */
369 CalendarManager.genResponseChangeFunc = function(xhr) {
370 return function() {
371 if (xhr.readyState != 4) {
372 return;
373 }
374 if (!xhr.responseXML) {
375 console.log('No responseXML');
376 localStorageSet('nextEvent', null);
377 canvasAnimation_.drawFinal();
378 return;
379 }
380 if (isMultiCalendar) {
381 var entry_ = xhr.responseXML.getElementsByTagName('entry');
382 if (entry_ && entry_.length > 0) {
383 var calendars = [];
384 for (var i = 0, entry; entry = entry_[i]; ++i) {
385 if (!i) {
386 defaultAuthor = entry.querySelector('title').textContent;
387 }
388 // Include only those calendars which are not hidden and selected
389 var isHidden = entry.querySelector('hidden');
390 var isSelected = entry.querySelector('selected');
391 if (isHidden && isHidden.getAttribute('value') == 'false') {
392 if (isSelected && isSelected.getAttribute('value') == 'true') {
393 var calendar_content = entry.querySelector('content');
394 var cal_src = calendar_content.getAttribute('src');
395 cal_src += '?toolbar=true&max-results=10';
396 calendars.push(cal_src);
397 }
398 }
399 }
400 localStorageSet('calendars', calendars);
401 CalendarManager.getCalendarFeed(0);
402 return;
403 }
404 } else {
405 var calendars = [];
406 calendars.push(SINGLE_CALENDAR_SUPPORT_URL);
407 localStorageSet('calendars', calendars);
408 CalendarManager.parseCalendarEntry(xhr.responseXML, 0);
409 return;
410 }
411
412 console.error('Error: feed retrieved, but no event found');
413 localStorageSet('nextEvent', null);
414 canvasAnimation_.drawFinal();
415 };
416 };
417
418 /**
419 * Retrieves feed for a calendar
420 * @param {integer} calendarId Id of the calendar in array of calendars.
421 */
422 CalendarManager.getCalendarFeed = function(calendarId) {
423 var xmlhttp = new XMLHttpRequest();
424 try {
425 xmlhttp.onreadystatechange = CalendarManager.onCalendarResponse(xmlhttp,
426 calendarId);
427 xmlhttp.onerror = function(error) {
428 console.error('error:', error);
429 localStorageSet('nextEvent', null);
430 canvasAnimation_.drawFinal();
431 };
432
433 // Augment the calendar's URL with the API key.
434 var calendarUrl = localStorageGet('calendars')[calendarId] + '&' + API_KEY;
435 xmlhttp.open('GET', calendarUrl);
436 xmlhttp.send(null);
437 }
438 catch (e) {
439 console.error('ex:', e);
440 localStorageSet('nextEvent', null);
441 canvasAnimation_.drawFinal();
442 }
443 };
444
445 /**
446 * Gets the event entries of every calendar subscribed in default user calendar.
447 * @param {xmlHttpRequest} xmlhttp xmlHttpRequest containing server response
448 * for the feed of a specific calendar.
449 * @param {integer} calendarId Variable for storing the no of calendars
450 * processed.
451 * @return {Object} anonymous function which returns to onReadyStateChange.
452 */
453 CalendarManager.onCalendarResponse = function(xmlhttp, calendarId) {
454 return function() {
455 if (xmlhttp.readyState != 4) {
456 return;
457 }
458 if (!xmlhttp.responseXML) {
459 console.log('No responseXML');
460 localStorageSet('nextEvent', null);
461 canvasAnimation_.drawFinal();
462 return;
463 }
464 CalendarManager.parseCalendarEntry(xmlhttp.responseXML, calendarId);
465 };
466 };
467
468 /**
469 * Parses events from calendar response XML
470 * @param {string} responseXML Response XML for calendar.
471 * @param {integer} calendarId Id of the calendar in array of calendars.
472 */
473 CalendarManager.parseCalendarEntry = function(responseXML, calendarId) {
474 var entry_ = responseXML.getElementsByTagName('entry');
475 var mailId = null;
476 var author = null;
477
478 if (responseXML.querySelector('author name')) {
479 author = responseXML.querySelector('author name').textContent;
480 }
481 if (responseXML.querySelector('author email')) {
482 mailId = responseXML.querySelector('author email').textContent;
483 }
484
485 if (entry_ && entry_.length > 0) {
486 var eventList = localStorageGet('eventList', []);
487 for (var i = 0, entry; entry = entry_[i]; ++i) {
488 var event_ = CalendarManager.extractEvent(entry, mailId);
489
490 // Get the time from then to now
491 if (event_.startTime) {
492 var t = event_.startTime.getTime() - getCurrentTime();
493 if (t >= 0 && (event_.attendeeStatus != DECLINED_URL)) {
494 if (isMultiCalendar && author) {
495 event_.author = author;
496 }
497 eventList.push(event_);
498 }
499 }
500 }
501 localStorageSet('eventList', eventList);
502 }
503
504 calendarId++;
505 //get the next calendar
506 if (calendarId < localStorageGet('calendars', []).length) {
507 CalendarManager.getCalendarFeed(calendarId);
508 } else {
509 CalendarManager.populateLatestEvent(localStorageGet('eventList', []));
510 }
511 };
512
513 /**
514 * Fills the event list with the events acquired from the calendar(s).
515 * Parses entire event list and prepares an array of upcoming events.
516 * @param {Array} eventList List of all events.
517 */
518 CalendarManager.populateLatestEvent = function(eventList) {
519 var nextEvents = [];
520 if (isMultiCalendar) {
521 eventList.sort(sortByDate);
522 }
523
524 //populating next events array.
525 if (eventList.length > 0) {
526 nextEvent = localStorageGet('nextEvent');
527 nextEvent = eventList[0];
528 nextEvents.push(nextEvent);
529 var startTime = (new Date(nextEvent.startTime)).setSeconds(0, 0);
530 for (var i = 1, event; event = eventList[i]; i++) {
531 var time = (new Date(event.startTime)).setSeconds(0, 0);
532 if (time == startTime) {
533 nextEvents.push(event);
534 } else {
535 break;
536 }
537 }
538 if (nextEvents.length > 1 && isMultiCalendar) {
539 nextEvents.sort(sortByAuthor);
540 }
541 localStorageSet('nextEvents', nextEvents);
542 localStorageSet('nextEvent', nextEvent);
543 canvasAnimation_.animate();
544 return;
545 } else {
546 console.error('Error: feed retrieved, but no event found');
547 localStorageSet('nextEvent', null);
548 localStorageSet('nextEvents', nextEvents);
549 canvasAnimation_.drawFinal();
550 }
551 };
552
553 var DATE_TIME_REGEX =
554 /^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)\.\d+(\+|-)(\d\d):(\d\d)$/;
555 var DATE_TIME_REGEX_Z = /^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)\.\d+Z$/;
556 var DATE_REGEX = /^(\d\d\d\d)-(\d\d)-(\d\d)$/;
557
558 /**
559 * Convert the incoming date into a javascript date.
560 * @param {String} rfc3339 The rfc date in string format as following
561 * 2006-04-28T09:00:00.000-07:00
562 * 2006-04-28T09:00:00.000Z
563 * 2006-04-19.
564 * @return {Date} The javascript date format of the incoming date.
565 */
566 function rfc3339StringToDate(rfc3339) {
567 var parts = DATE_TIME_REGEX.exec(rfc3339);
568
569 // Try out the Z version
570 if (!parts) {
571 parts = DATE_TIME_REGEX_Z.exec(rfc3339);
572 }
573
574 if (parts && parts.length > 0) {
575 var d = new Date();
576 d.setUTCFullYear(parts[1], parseInt(parts[2], 10) - 1, parts[3]);
577 d.setUTCHours(parts[4]);
578 d.setUTCMinutes(parts[5]);
579 d.setUTCSeconds(parts[6]);
580
581 var tzOffsetFeedMin = 0;
582 if (parts.length > 7) {
583 tzOffsetFeedMin = parseInt(parts[8], 10) * 60 + parseInt(parts[9], 10);
584 if (parts[7] != '-') { // This is supposed to be backwards.
585 tzOffsetFeedMin = -tzOffsetFeedMin;
586 }
587 }
588 return new Date(d.getTime() + tzOffsetFeedMin * 60 * 1000);
589 }
590
591 parts = DATE_REGEX.exec(rfc3339);
592 if (parts && parts.length > 0) {
593 return new Date(parts[1], parseInt(parts[2], 10) - 1, parts[3]);
594 }
595 return null;
596 };
597
598 /**
599 * Sorts all the events by date and time.
600 * @param {object} event_1 Event object.
601 * @param {object} event_2 Event object.
602 * @return {integer} timeDiff Difference in time.
603 */
604 function sortByDate(event_1, event_2) {
605 return ((new Date(event_1.startTime)).getTime() -
606 (new Date(event_2.startTime)).getTime());
607 };
608
609 /**
610 * Sorts all the events by author name.
611 * @param {object} event_1 Event object.
612 * @param {object} event_2 Event object.
613 * @return {integer} nameDiff Difference in default author and others.
614 */
615 function sortByAuthor(event_1, event_2) {
616 var nameDiff;
617 if (event_1.author && event_2.author && event_2.author == defaultAuthor) {
618 nameDiff = 1;
619 } else {
620 return 0;
621 }
622 return nameDiff;
623 };
624
625 /**
626 * Fires once per minute to redraw extension icon.
627 */
628 function redraw() {
629 // If the next event just passed, re-poll.
630 var nextEvent = localStorageGet('nextEvent');
631 if (nextEvent) {
632 var t = (new Date(nextEvent.startTime)).getTime() - getCurrentTime();
633 if (t <= 0) {
634 CalendarManager.pollServer();
635 return;
636 }
637 }
638 canvasAnimation_.drawFinal();
639
640 // if 30 minutes have passed re-poll
641 if (getCurrentTime() - localStorageGet('lastPollTime') >= POLL_INTERVAL) {
642 CalendarManager.pollServer();
643 }
644 };
645
646 /**
647 * Returns the current time in milliseconds.
648 * @return {Number} Current time in milliseconds.
649 */
650 function getCurrentTime() {
651 return (new Date()).getTime();
652 };
653
654 /**
655 * Replaces ASCII characters from the title.
656 * @param {String} data String containing ASCII code for special characters.
657 * @return {String} data ASCII characters replaced with actual characters.
658 */
659 function filterSpecialChar(data) {
660 if (data) {
661 data = data.replace(/&lt;/g, '<');
662 data = data.replace(/&gt;/g, '>');
663 data = data.replace(/&amp;/g, '&');
664 data = data.replace(/%7B/g, '{');
665 data = data.replace(/%7D/g, '}');
666 data = data.replace(/&quot;/g, '"');
667 data = data.replace(/&#39;/g, '\'');
668 }
669 return data;
670 };
671
672 /**
673 * Called from options.js page on saving the settings
674 */
675 function onSettingsChange() {
676 isMultiCalendar = localStorageGet('multiCalendar', false);
677 badgeAnimation_.start();
678 CalendarManager.pollServer();
679 };
680
681 /**
682 * Function runs on completed navigation with a url of google applications.
683 * @param {details} details of the completed web navigation.
684 */
685 function onCompleted(details) {
686 var url = details.url;
687
688 if ((url.indexOf('www.google.com/calendar/') != -1) ||
689 ((url.indexOf('www.google.com/a/') != -1) &&
690 (url.lastIndexOf('/acs') == url.length - 4)) ||
691 (url.indexOf('www.google.com/accounts/') != -1)) {
692
693 if (pendingLoadId_) {
694 clearTimeout(pendingLoadId_);
695 pendingLoadId_ = null;
696 }
697
698 // try to poll in 2 second [which makes the redirects settle down]
699 pendingLoadId_ = setTimeout(CalendarManager.pollServer, 2000);
700 }
701 };
702
703 /**
704 * Called when the user clicks on extension icon and opens calendar page.
705 */
706 function onClickAction() {
707 chrome.tabs.getAllInWindow(null, function(tabs) {
708 for (var i = 0, tab; tab = tabs[i]; i++) {
709 if (tab.url && isCalendarUrl(tab.url)) {
710 chrome.tabs.update(tab.id, {selected: true});
711 CalendarManager.pollServer();
712 return;
713 }
714 }
715 chrome.tabs.create({url: GOOGLE_CALENDAR_URL});
716 CalendarManager.pollServer();
717 }); 34 });
718 };
719
720 /**
721 * Checks whether an instance of Google calendar is already open.
722 * @param {String} url Url of the tab visited.
723 * @return {boolean} true if the url is a Google calendar relative url, false
724 * otherwise.
725 */
726 function isCalendarUrl(url) {
727 return url.indexOf('www.google.com/calendar') != -1 ? true : false;
728 };
729
730 function onInstalled() {
731 badgeAnimation_.start();
732 CalendarManager.pollServer();
733 localStorageSet('lastPollTime', 0);
734 localStorageSet('nextEvent', null);
735 } 35 }
736 36
737 /* 37 chrome.browserAction.setBadgeBackgroundColor({ color: '#FF0000' });
738 * Load animation contexts and add listeners. 38 chrome.browserAction.setBadgeText({ text: '!' });
739 */ 39 chrome.browserAction.onClicked.addListener(openWarningPage);
740 badgeAnimation_ = new BadgeAnimation(); 40 chrome.notifications.onClicked.addListener(openWarningPage);
741 canvasAnimation_ = new CanvasAnimation(); 41 chrome.notifications.onButtonClicked.addListener(openWarningPage);
742 isMultiCalendar = localStorageGet('multiCalendar', false); 42 chrome.runtime.onInstalled.addListener(showWarning);
743 chrome.browserAction.setIcon({path: '../images/icon-16.gif'});
744 chrome.runtime.onInstalled.addListener(onInstalled);
745 chrome.alarms.onAlarm.addListener(redraw)
746 chrome.alarms.create('redraw', {periodInMinutes: DRAW_INTERVAL_MINUTES});
747 chrome.webNavigation.onCompleted.addListener(onCompleted,
748 {url: [{hostSuffix: 'www.google.com', pathPrefix: '/calendar'},
749 {hostSuffix: 'www.google.com', pathPrefix: '/accounts'},
750 {hostSuffix: 'www.google.com', pathPrefix: '/a'}]});
751 chrome.browserAction.onClicked.addListener(onClickAction);
752 redraw();
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698