Index: chrome/common/extensions/docs/examples/extensions/calendar/javascript/background.js |
diff --git a/chrome/common/extensions/docs/examples/extensions/calendar/javascript/background.js b/chrome/common/extensions/docs/examples/extensions/calendar/javascript/background.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..6abedb03467b39fdc09070718c16e610142342ff |
--- /dev/null |
+++ b/chrome/common/extensions/docs/examples/extensions/calendar/javascript/background.js |
@@ -0,0 +1,728 @@ |
+/** |
+ * Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this |
+ * source code is governed by a BSD-style license that can be found in the |
+ * LICENSE file. |
+ */ |
+ |
+/** |
+ * PHASES |
+ * 1) Load next event from server refresh every 30 minutes or every time |
+ * you go to calendar or every time you logout drop in a data object. |
+ * 2) Display on screen periodically once per minute or on demand. |
+ */ |
+ |
+// Message shown in badge title when no title is given to an event. |
+var MSG_NO_TITLE = chrome.i18n.getMessage('noTitle'); |
+ |
+// Time between server polls = 30 minutes. |
+var POLL_INTERVAL = 30 * 60 * 1000; |
+ |
+// Redraw interval is 1 min. |
+var DRAW_INTERVAL = 60 * 1000; |
+ |
+// The time when we last polled. |
+var lastPollTime_ = 0; |
+ |
+// Object for BadgeAnimation |
+var badgeAnimation_; |
+ |
+//Object for CanvasAnimation |
+var canvasAnimation_; |
+ |
+// Object containing the event. |
+var nextEvent_ = null; |
+ |
+// Storing events. |
+var eventList = []; |
+var nextEvents = []; |
+ |
+// Storing calendars. |
+var calendars = []; |
+ |
+var pollUnderProgress = false; |
+var defaultAuthor = ''; |
+var isMultiCalendar = false; |
+ |
+//URL for getting feed of individual calendar support. |
+var SINGLE_CALENDAR_SUPPORT_URL = 'https://www.google.com/calendar/feeds' + |
+ '/default/private/embed?toolbar=true&max-results=10'; |
+ |
+//URL for getting feed of multiple calendar support. |
+var MULTIPLE_CALENDAR_SUPPORT_URL = 'https://www.google.com/calendar/feeds' + |
+ '/default/allcalendars/full'; |
+ |
+//URL for opening Google Calendar in new tab. |
+var GOOGLE_CALENDAR_URL = 'http://www.google.com/calendar/render'; |
+ |
+//URL for declining invitation of the event. |
+var DECLINED_URL = 'http://schemas.google.com/g/2005#event.declined'; |
+ |
+//This is used to poll only once per second at most, and delay that if |
+//we keep hitting pages that would otherwise force a load. |
+var pendingLoadId_ = null; |
+ |
+/** |
+ * A "loading" animation displayed while we wait for the first response from |
+ * Calendar. This animates the badge text with a dot that cycles from left to |
+ * right. |
+ * @constructor |
+ */ |
+function BadgeAnimation() { |
+ this.timerId_ = 0; |
+ this.maxCount_ = 8; // Total number of states in animation |
+ this.current_ = 0; // Current state |
+ this.maxDot_ = 4; // Max number of dots in animation |
+}; |
+ |
+/** |
+ * Paints the badge text area while loading the data. |
+ */ |
+BadgeAnimation.prototype.paintFrame = function() { |
+ var text = ''; |
+ for (var i = 0; i < this.maxDot_; i++) { |
+ text += (i == this.current_) ? '.' : ' '; |
+ } |
+ |
+ chrome.browserAction.setBadgeText({text: text}); |
+ this.current_++; |
+ if (this.current_ == this.maxCount_) { |
+ this.current_ = 0; |
+ } |
+}; |
+ |
+/** |
+ * Starts the animation process. |
+ */ |
+BadgeAnimation.prototype.start = function() { |
+ if (this.timerId_) { |
+ return; |
+ } |
+ |
+ var self = this; |
+ this.timerId_ = window.setInterval(function() { |
+ self.paintFrame(); |
+ }, 100); |
+}; |
+ |
+/** |
+ * Stops the animation process. |
+ */ |
+BadgeAnimation.prototype.stop = function() { |
+ if (!this.timerId_) { |
+ return; |
+ } |
+ |
+ window.clearInterval(this.timerId_); |
+ this.timerId_ = 0; |
+}; |
+ |
+/** |
+ * Animates the canvas after loading the data from all the calendars. It |
+ * rotates the icon and defines the badge text and title. |
+ * @constructor |
+ */ |
+function CanvasAnimation() { |
+ this.animationFrames_ = 36; // The number of animation frames |
+ this.animationSpeed_ = 10; // Time between each frame(in ms). |
+ this.canvas_ = $('canvas'); // The canvas width + height. |
+ this.canvasContext_ = this.canvas_.getContext('2d'); // Canvas context. |
+ this.loggedInImage_ = $('logged_in'); |
+ this.rotation_ = 0; //Keeps count of rotation angle of extension icon. |
+ this.w = this.canvas_.width; // Setting canvas width. |
+ this.h = this.canvas_.height; // Setting canvas height. |
+ this.RED = [208, 0, 24, 255]; //Badge color of extension icon in RGB format. |
+ this.BLUE = [0, 24, 208, 255]; |
+ this.currentBadge_ = null; // The text in the current badge. |
+}; |
+ |
+/** |
+ * Flips the icon around and draws it. |
+ */ |
+CanvasAnimation.prototype.animate = function() { |
+ this.rotation_ += (1 / this.animationFrames_); |
+ this.drawIconAtRotation(); |
+ var self = this; |
+ if (this.rotation_ <= 1) { |
+ setTimeout(function() { |
+ self.animate(); |
+ }, self.animationSpeed_); |
+ } else { |
+ this.drawFinal(); |
+ } |
+}; |
+ |
+/** |
+ * Renders the icon. |
+ */ |
+CanvasAnimation.prototype.drawIconAtRotation = function() { |
+ this.canvasContext_.save(); |
+ this.canvasContext_.clearRect(0, 0, this.w, this.h); |
+ this.canvasContext_.translate(Math.ceil(this.w / 2), Math.ceil(this.h / 2)); |
+ this.canvasContext_.rotate(2 * Math.PI * this.getSector(this.rotation_)); |
+ this.canvasContext_.drawImage(this.loggedInImage_, -Math.ceil(this.w / 2), |
+ -Math.ceil(this.h / 2)); |
+ this.canvasContext_.restore(); |
+ chrome.browserAction.setIcon( |
+ {imageData: this.canvasContext_.getImageData(0, 0, this.w, this.h)}); |
+}; |
+ |
+/** |
+ * Calculates the sector which has to be traversed in a single call of animate |
+ * function(360/animationFrames_ = 360/36 = 10 radians). |
+ * @param {integer} sector angle to be rotated(in radians). |
+ * @return {integer} value in radian of the sector which it has to cover. |
+ */ |
+CanvasAnimation.prototype.getSector = function(sector) { |
+ return (1 - Math.sin(Math.PI / 2 + sector * Math.PI)) / 2; |
+}; |
+ |
+/** |
+ * Draws the event icon and determines the badge title and icon title. |
+ */ |
+CanvasAnimation.prototype.drawFinal = function() { |
+ badgeAnimation_.stop(); |
+ |
+ if (!nextEvent_) { |
+ this.showLoggedOut(); |
+ } else { |
+ this.drawIconAtRotation(); |
+ this.rotation_ = 0; |
+ |
+ var ms = nextEvent_.startTime.getTime() - getCurrentTime(); |
+ var nextEventMin = ms / (1000 * 60); |
+ var bgColor = (nextEventMin < 60) ? this.RED : this.BLUE; |
+ |
+ chrome.browserAction.setBadgeBackgroundColor({color: bgColor}); |
+ currentBadge_ = this.getBadgeText(nextEvent_); |
+ chrome.browserAction.setBadgeText({text: currentBadge_}); |
+ |
+ if (nextEvents.length > 0) { |
+ var text = ''; |
+ for (var i = 0, event; event = nextEvents[i]; i++) { |
+ text += event.title; |
+ if (event.author || event.location) { |
+ text += '\n'; |
+ } |
+ if (event.location) { |
+ text += event.location + ' '; |
+ } |
+ if (event.author) { |
+ text += event.author; |
+ } |
+ if (i < (nextEvents.length - 1)) { |
+ text += '\n----------\n'; |
+ } |
+ } |
+ text = filterSpecialChar(text); |
+ chrome.browserAction.setTitle({'title' : text}); |
+ } |
+ } |
+ pollUnderProgress = false; |
+ |
+ chrome.extension.sendRequest({ |
+ message: 'enableSave' |
+ }, function() { |
+ }); |
+ |
+ return; |
+}; |
+ |
+/** |
+ * Shows the user logged out. |
+ */ |
+CanvasAnimation.prototype.showLoggedOut = function() { |
+ currentBadge_ = '?'; |
+ chrome.browserAction.setIcon({path: '../images/icon-16_bw.gif'}); |
+ chrome.browserAction.setBadgeBackgroundColor({color: [190, 190, 190, 230]}); |
+ chrome.browserAction.setBadgeText({text: '?'}); |
+ chrome.browserAction.setTitle({ 'title' : ''}); |
+}; |
+ |
+/** |
+ * Gets the badge text. |
+ * @param {Object} nextEvent_ next event in the calendar. |
+ * @return {String} text Badge text to be shown in extension icon. |
+ */ |
+CanvasAnimation.prototype.getBadgeText = function(nextEvent_) { |
+ if (!nextEvent_) { |
+ return ''; |
+ } |
+ |
+ var ms = nextEvent_.startTime.getTime() - getCurrentTime(); |
+ var nextEventMin = Math.ceil(ms / (1000 * 60)); |
+ |
+ var text = ''; |
+ if (nextEventMin < 60) { |
+ text = chrome.i18n.getMessage('minutes', nextEventMin.toString()); |
+ } else if (nextEventMin < 1440) { |
+ text = chrome.i18n.getMessage('hours', |
+ Math.round(nextEventMin / 60).toString()); |
+ } else if (nextEventMin < (1440 * 10)) { |
+ text = chrome.i18n.getMessage('days', |
+ Math.round(nextEventMin / 60 / 24).toString()); |
+ } |
+ return text; |
+}; |
+ |
+/** |
+ * Provides all the calendar related utils. |
+ */ |
+CalendarManager = {}; |
+ |
+/** |
+ * Extracts event from the each entry of the calendar. |
+ * @param {Object} elem The XML node to extract the event from. |
+ * @return {Object} out An object containing the event properties. |
+ */ |
+CalendarManager.extractEvent = function(elem) { |
+ var out = {}; |
+ |
+ for (var node = elem.firstChild; node != null; node = node.nextSibling) { |
+ if (node.nodeName == 'title') { |
+ out.title = node.firstChild ? node.firstChild.nodeValue : MSG_NO_TITLE; |
+ } else if (node.nodeName == 'link' && |
+ node.getAttribute('rel') == 'alternate') { |
+ out.url = node.getAttribute('href'); |
+ } else if (node.nodeName == 'gd:where') { |
+ out.location = node.getAttribute('valueString'); |
+ } else if (node.nodeName == 'gd:who') { |
+ if (node.firstChild) { |
+ out.attendeeStatus = node.firstChild.getAttribute('value'); |
+ } |
+ } else if (node.nodeName == 'gd:eventStatus') { |
+ out.status = node.getAttribute('value'); |
+ } else if (node.nodeName == 'gd:when') { |
+ var startTimeStr = node.getAttribute('startTime'); |
+ var endTimeStr = node.getAttribute('endTime'); |
+ |
+ startTime = rfc3339StringToDate(startTimeStr); |
+ endTime = rfc3339StringToDate(endTimeStr); |
+ |
+ if (startTime == null || endTime == null) { |
+ continue; |
+ } |
+ |
+ out.isAllDay = (startTimeStr.length <= 11); |
+ out.startTime = startTime; |
+ out.endTime = endTime; |
+ } |
+ } |
+ return out; |
+}; |
+ |
+/** |
+ * Polls the server to get the feed of the user. |
+ */ |
+CalendarManager.pollServer = function() { |
+ if (! pollUnderProgress) { |
+ eventList = []; |
+ pollUnderProgress = true; |
+ pendingLoadId_ = null; |
+ calendars = []; |
+ lastPollTime_ = getCurrentTime(); |
+ var url; |
+ var xhr = new XMLHttpRequest(); |
+ try { |
+ xhr.onreadystatechange = CalendarManager.genResponseChangeFunc(xhr); |
+ xhr.onerror = function(error) { |
+ console.log('error: ' + error); |
+ nextEvent_ = null; |
+ canvasAnimation_.drawFinal(); |
+ }; |
+ if (isMultiCalendar) { |
+ url = MULTIPLE_CALENDAR_SUPPORT_URL; |
+ } else { |
+ url = SINGLE_CALENDAR_SUPPORT_URL; |
+ } |
+ |
+ xhr.open('GET', url); |
+ xhr.send(null); |
+ } catch (e) { |
+ console.log('ex: ' + e); |
+ nextEvent_ = null; |
+ canvasAnimation_.drawFinal(); |
+ } |
+ } |
+}; |
+ |
+/** |
+ * Gathers the list of all calendars of a specific user for multiple calendar |
+ * support and event entries in single calendar. |
+ * @param {xmlHttpRequest} xhr xmlHttpRequest object containing server response. |
+ * @return {Object} anonymous function which returns to onReadyStateChange. |
+ */ |
+CalendarManager.genResponseChangeFunc = function(xhr) { |
+ return function() { |
+ if (xhr.readyState != 4) { |
+ return; |
+ } |
+ if (!xhr.responseXML) { |
+ console.log('No responseXML'); |
+ nextEvent_ = null; |
+ canvasAnimation_.drawFinal(); |
+ return; |
+ } |
+ if (isMultiCalendar) { |
+ var entry_ = xhr.responseXML.getElementsByTagName('entry'); |
+ if (entry_ && entry_.length > 0) { |
+ calendars = []; |
+ for (var i = 0, entry; entry = entry_[i]; ++i) { |
+ if (!i) { |
+ defaultAuthor = entry.querySelector('title').textContent; |
+ } |
+ // Include only those calendars which are not hidden and selected |
+ var isHidden = entry.querySelector('hidden'); |
+ var isSelected = entry.querySelector('selected'); |
+ if (isHidden && isHidden.getAttribute('value') == 'false') { |
+ if (isSelected && isSelected.getAttribute('value') == 'true') { |
+ var calendar_content = entry.querySelector('content'); |
+ var cal_src = calendar_content.getAttribute('src'); |
+ cal_src += '?toolbar=true&max-results=10'; |
+ calendars.push(cal_src); |
+ } |
+ } |
+ } |
+ CalendarManager.getCalendarFeed(0); |
+ return; |
+ } |
+ } else { |
+ calendars = []; |
+ calendars.push(SINGLE_CALENDAR_SUPPORT_URL); |
+ CalendarManager.parseCalendarEntry(xhr.responseXML, 0); |
+ return; |
+ } |
+ |
+ console.error('Error: feed retrieved, but no event found'); |
+ nextEvent_ = null; |
+ canvasAnimation_.drawFinal(); |
+ }; |
+}; |
+ |
+/** |
+ * Retrieves feed for a calendar |
+ * @param {integer} calendarId Id of the calendar in array of calendars. |
+ */ |
+CalendarManager.getCalendarFeed = function(calendarId) { |
+ var xmlhttp = new XMLHttpRequest(); |
+ try { |
+ xmlhttp.onreadystatechange = CalendarManager.onCalendarResponse(xmlhttp, |
+ calendarId); |
+ xmlhttp.onerror = function(error) { |
+ console.log('error: ' + error); |
+ nextEvent_ = null; |
+ canvasAnimation_.drawFinal(); |
+ }; |
+ |
+ xmlhttp.open('GET', calendars[calendarId]); |
+ xmlhttp.send(null); |
+ } |
+ catch (e) { |
+ console.log('ex: ' + e); |
+ nextEvent_ = null; |
+ canvasAnimation_.drawFinal(); |
+ } |
+}; |
+ |
+/** |
+ * Gets the event entries of every calendar subscribed in default user calendar. |
+ * @param {xmlHttpRequest} xmlhttp xmlHttpRequest containing server response |
+ * for the feed of a specific calendar. |
+ * @param {integer} calendarId Variable for storing the no of calendars |
+ * processed. |
+ * @return {Object} anonymous function which returns to onReadyStateChange. |
+ */ |
+CalendarManager.onCalendarResponse = function(xmlhttp, calendarId) { |
+ return function() { |
+ if (xmlhttp.readyState != 4) { |
+ return; |
+ } |
+ if (!xmlhttp.responseXML) { |
+ console.log('No responseXML'); |
+ nextEvent_ = null; |
+ canvasAnimation_.drawFinal(); |
+ return; |
+ } |
+ CalendarManager.parseCalendarEntry(xmlhttp.responseXML, calendarId); |
+ }; |
+}; |
+ |
+/** |
+ * Parses events from calendar response XML |
+ * @param {string} responseXML Response XML for calendar. |
+ * @param {integer} calendarId Id of the calendar in array of calendars. |
+ */ |
+CalendarManager.parseCalendarEntry = function(responseXML, calendarId) { |
+ var entry_ = responseXML.getElementsByTagName('entry'); |
+ var author = responseXML.querySelector('author name').textContent; |
+ |
+ if (entry_ && entry_.length > 0) { |
+ for (var i = 0, entry; entry = entry_[i]; ++i) { |
+ var event_ = CalendarManager.extractEvent(entry); |
+ |
+ // Get the time from then to now |
+ if (event_.startTime) { |
+ var t = event_.startTime.getTime() - getCurrentTime(); |
+ if (t >= 0 && (event_.attendeeStatus != DECLINED_URL)) { |
+ if (isMultiCalendar) { |
+ event_.author = author; |
+ } |
+ eventList.push(event_); |
+ } |
+ } |
+ } |
+ } |
+ |
+ calendarId++; |
+ //get the next calendar |
+ if (calendarId < calendars.length) { |
+ CalendarManager.getCalendarFeed(calendarId); |
+ } else { |
+ CalendarManager.populateLatestEvent(eventList); |
+ } |
+}; |
+ |
+/** |
+ * Fills the event list with the events acquired from the calendar(s). |
+ * Parses entire event list and prepares an array of upcoming events. |
+ * @param {Array} eventList List of all events. |
+ */ |
+CalendarManager.populateLatestEvent = function(eventList) { |
+ nextEvents = []; |
+ if (isMultiCalendar) { |
+ eventList.sort(sortByDate); |
+ } |
+ |
+ //populating next events array. |
+ if (eventList.length > 0) { |
+ nextEvent_ = eventList[0]; |
+ nextEvent_.startTime.setSeconds(0, 0); |
+ nextEvents.push(nextEvent_); |
+ var startTime = nextEvent_.startTime; |
+ for (var i = 1, event; event = eventList[i]; i++) { |
+ var time = event.startTime.setSeconds(0, 0); |
+ if (time == startTime) { |
+ nextEvents.push(event); |
+ } else { |
+ break; |
+ } |
+ } |
+ if (nextEvents.length > 1) { |
+ nextEvents.sort(sortByAuthor); |
+ } |
+ canvasAnimation_.animate(); |
+ return; |
+ } else { |
+ console.error('Error: feed retrieved, but no event found'); |
+ nextEvent_ = null; |
+ canvasAnimation_.drawFinal(); |
+ } |
+}; |
+ |
+var DATE_TIME_REGEX = |
+ /^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)\.\d+(\+|-)(\d\d):(\d\d)$/; |
+var DATE_TIME_REGEX_Z = /^(\d\d\d\d)-(\d\d)-(\d\d)T(\d\d):(\d\d):(\d\d)\.\d+Z$/; |
+var DATE_REGEX = /^(\d\d\d\d)-(\d\d)-(\d\d)$/; |
+ |
+/** |
+* Convert the incoming date into a javascript date. |
+* @param {String} rfc3339 The rfc date in string format as following |
+* 2006-04-28T09:00:00.000-07:00 |
+* 2006-04-28T09:00:00.000Z |
+* 2006-04-19. |
+* @return {Date} The javascript date format of the incoming date. |
+*/ |
+function rfc3339StringToDate(rfc3339) { |
+ var parts = DATE_TIME_REGEX.exec(rfc3339); |
+ |
+ // Try out the Z version |
+ if (!parts) { |
+ parts = DATE_TIME_REGEX_Z.exec(rfc3339); |
+ } |
+ |
+ if (parts && parts.length > 0) { |
+ var d = new Date(); |
+ d.setUTCFullYear(parts[1], parseInt(parts[2], 10) - 1, parts[3]); |
+ d.setUTCHours(parts[4]); |
+ d.setUTCMinutes(parts[5]); |
+ d.setUTCSeconds(parts[6]); |
+ |
+ var tzOffsetFeedMin = 0; |
+ if (parts.length > 7) { |
+ tzOffsetFeedMin = parseInt(parts[8], 10) * 60 + parseInt(parts[9], 10); |
+ if (parts[7] != '-') { // This is supposed to be backwards. |
+ tzOffsetFeedMin = -tzOffsetFeedMin; |
+ } |
+ } |
+ return new Date(d.getTime() + tzOffsetFeedMin * 60 * 1000); |
+ } |
+ |
+ parts = DATE_REGEX.exec(rfc3339); |
+ if (parts && parts.length > 0) { |
+ return new Date(parts[1], parseInt(parts[2], 10) - 1, parts[3]); |
+ } |
+ return null; |
+}; |
+ |
+/** |
+ * Sorts all the events by date and time. |
+ * @param {object} event_1 Event object. |
+ * @param {object} event_2 Event object. |
+ * @return {integer} timeDiff Difference in time. |
+ */ |
+function sortByDate(event_1, event_2) { |
+ return (event_1.startTime.getTime() - event_2.startTime.getTime()); |
+}; |
+ |
+/** |
+ * Sorts all the events by author name. |
+ * @param {object} event_1 Event object. |
+ * @param {object} event_2 Event object. |
+ * @return {integer} nameDiff Difference in default author and others. |
+ */ |
+function sortByAuthor(event_1, event_2) { |
+ var nameDiff; |
+ if (event_2.author == defaultAuthor) { |
+ nameDiff = 1; |
+ } else { |
+ return 0; |
+ } |
+ return nameDiff; |
+}; |
+ |
+/** |
+ * Fires once per minute to redraw extension icon. |
+ */ |
+function redraw() { |
+ // If the next event just passed, re-poll. |
+ if (nextEvent_) { |
+ var t = nextEvent_.startTime.getTime() - getCurrentTime(); |
+ if (t <= 0) { |
+ CalendarManager.pollServer(); |
+ return; |
+ } |
+ } |
+ canvasAnimation_.animate(); |
+ |
+ // if ((we are logged in) && (30 minutes have passed)) re-poll |
+ if (nextEvent_ && (getCurrentTime() - lastPollTime_ >= POLL_INTERVAL)) { |
+ CalendarManager.pollServer(); |
+ } |
+}; |
+ |
+/** |
+ * Returns the current time in milliseconds. |
+ * @return {Number} Current time in milliseconds. |
+ */ |
+function getCurrentTime() { |
+ return (new Date()).getTime(); |
+}; |
+ |
+/** |
+* Replaces ASCII characters from the title. |
+* @param {String} data String containing ASCII code for special characters. |
+* @return {String} data ASCII characters replaced with actual characters. |
+*/ |
+function filterSpecialChar(data) { |
+ if (data) { |
+ data = data.replace(/</g, '<'); |
+ data = data.replace(/>/g, '>'); |
+ data = data.replace(/&/g, '&'); |
+ data = data.replace(/%7B/g, '{'); |
+ data = data.replace(/%7D/g, '}'); |
+ data = data.replace(/"/g, '"'); |
+ data = data.replace(/'/g, '\''); |
+ } |
+ return data; |
+}; |
+ |
+/** |
+ * Called from options.js page on saving the settings |
+ */ |
+function onSettingsChange() { |
+ isMultiCalendar = JSON.parse(localStorage.multiCalendar); |
+ badgeAnimation_.start(); |
+ CalendarManager.pollServer(); |
+}; |
+ |
+/** |
+ * Function runs on updating a tab having url of google applications. |
+ * @param {integer} tabId Id of the tab which is updated. |
+ * @param {String} changeInfo Gives the information of change in url. |
+ * @param {String} tab Gives the url of the tab updated. |
+ */ |
+function onTabUpdated(tabId, changeInfo, tab) { |
+ var url = tab.url; |
+ if (!url) { |
+ return; |
+ } |
+ |
+ if ((url.indexOf('www.google.com/calendar/') != -1) || |
+ ((url.indexOf('www.google.com/a/') != -1) && |
+ (url.lastIndexOf('/acs') == url.length - 4)) || |
+ (url.indexOf('www.google.com/accounts/') != -1)) { |
+ |
+ // The login screen isn't helpful |
+ if (url.indexOf('https://www.google.com/accounts/ServiceLogin?') == 0) { |
+ return; |
+ } |
+ |
+ if (pendingLoadId_) { |
+ clearTimeout(pendingLoadId_); |
+ pendingLoadId_ = null; |
+ } |
+ |
+ // try to poll in 2 second [which makes the redirects settle down] |
+ pendingLoadId_ = setTimeout(CalendarManager.pollServer, 2000); |
+ } |
+}; |
+ |
+/** |
+ * Called when the user clicks on extension icon and opens calendar page. |
+ */ |
+function onClickAction() { |
+ chrome.tabs.getAllInWindow(null, function(tabs) { |
+ for (var i = 0, tab; tab = tabs[i]; i++) { |
+ if (tab.url && isCalendarUrl(tab.url)) { |
+ chrome.tabs.update(tab.id, {selected: true}); |
+ CalendarManager.pollServer(); |
+ return; |
+ } |
+ } |
+ chrome.tabs.create({url: GOOGLE_CALENDAR_URL}); |
+ CalendarManager.pollServer(); |
+ }); |
+}; |
+ |
+/** |
+ * Checks whether an instance of Google calendar is already open. |
+ * @param {String} url Url of the tab visited. |
+ * @return {boolean} true if the url is a Google calendar relative url, false |
+ * otherwise. |
+ */ |
+function isCalendarUrl(url) { |
+ return url.indexOf('www.google.com/calendar') != -1 ? true : false; |
+}; |
+ |
+/** |
+ * Initializes everything. |
+ */ |
+function init() { |
+ badgeAnimation_ = new BadgeAnimation(); |
+ canvasAnimation_ = new CanvasAnimation(); |
+ |
+ isMultiCalendar = JSON.parse(localStorage.multiCalendar || false); |
+ |
+ chrome.browserAction.setIcon({path: '../images/icon-16.gif'}); |
+ badgeAnimation_.start(); |
+ CalendarManager.pollServer(); |
+ window.setInterval(redraw, DRAW_INTERVAL); |
+ |
+ chrome.tabs.onUpdated.addListener(onTabUpdated); |
+ |
+ chrome.browserAction.onClicked.addListener(function(tab) { |
+ onClickAction(); |
+ }); |
+}; |
+ |
+//Adding listener when body is loaded to call init function. |
+window.addEventListener('load', init, false); |