diff options
author | kurrik@chromium.org <kurrik@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-15 06:16:51 +0000 |
---|---|---|
committer | kurrik@chromium.org <kurrik@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-11-15 06:16:51 +0000 |
commit | 46648d3a6cac794efa4129f84c56562f32accef7 (patch) | |
tree | f4f227dbe485c35f3ddc7a73c843a4ccc9fee32c /chrome | |
parent | 17654a99e28ee744f10cb5c794bbaa78819f200b (diff) | |
download | chromium_src-46648d3a6cac794efa4129f84c56562f32accef7.zip chromium_src-46648d3a6cac794efa4129f84c56562f32accef7.tar.gz chromium_src-46648d3a6cac794efa4129f84c56562f32accef7.tar.bz2 |
Adding anujb's calendar extension.
BUG=None
TEST=Extension is hosted in source control
TBR=kathyw
Review URL: http://codereview.chromium.org/4996001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@66099 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
11 files changed, 974 insertions, 0 deletions
diff --git a/chrome/common/extensions/docs/examples/extensions/calendar/_locales/en/messages.json b/chrome/common/extensions/docs/examples/extensions/calendar/_locales/en/messages.json new file mode 100644 index 0000000..bb7517f --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/calendar/_locales/en/messages.json @@ -0,0 +1,38 @@ +{
+ "name": {"message": "Google Calendar Checker (by Google)"},
+ "description": {"message": "Quickly see the time until your next meeting from any of your calendars. Click on the button to be taken to your calendar."},
+ "title": {"message": "Google Calendar Checker"},
+ "direction": {"message" : "ltr"},
+ "noTitle": {"message" : "(No Title)"},
+ "optionsTitle": {"message" : "Google Calendar Checker"},
+ "minutes": {
+ "message": "$minutes$m",
+ "placeholders": {
+ "minutes": {
+ "content": "$1"
+ }
+ }
+ },
+ "hours": {
+ "message": "$hours$h",
+ "placeholders": {
+ "hours": {
+ "content": "$1"
+ }
+ }
+ },
+ "days": {
+ "message": "$days$d",
+ "placeholders": {
+ "days": {
+ "content": "$1"
+ }
+ }
+ },
+ "multiCalendarText": {"message": "Multi Calendar Support"},
+ "extensionName": {"message": "Google Calendar Checker"},
+ "status_saved": {"message": "Settings Saved."},
+ "status_saving": {"message": "Saving...."},
+ "multiCalendarToolTip": {"message": "Please check the box to enable multiple calendar support"},
+ "imageTooltip": {"message": "Google Calendar"}
+}
diff --git a/chrome/common/extensions/docs/examples/extensions/calendar/images/calendar_logo.gif b/chrome/common/extensions/docs/examples/extensions/calendar/images/calendar_logo.gif Binary files differnew file mode 100644 index 0000000..e082dc9 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/calendar/images/calendar_logo.gif diff --git a/chrome/common/extensions/docs/examples/extensions/calendar/images/icon-128.gif b/chrome/common/extensions/docs/examples/extensions/calendar/images/icon-128.gif Binary files differnew file mode 100644 index 0000000..78d34bc --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/calendar/images/icon-128.gif diff --git a/chrome/common/extensions/docs/examples/extensions/calendar/images/icon-16.gif b/chrome/common/extensions/docs/examples/extensions/calendar/images/icon-16.gif Binary files differnew file mode 100644 index 0000000..054e628 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/calendar/images/icon-16.gif diff --git a/chrome/common/extensions/docs/examples/extensions/calendar/images/icon-16_bw.gif b/chrome/common/extensions/docs/examples/extensions/calendar/images/icon-16_bw.gif Binary files differnew file mode 100644 index 0000000..c20d7b8 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/calendar/images/icon-16_bw.gif 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 0000000..6abedb0 --- /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); diff --git a/chrome/common/extensions/docs/examples/extensions/calendar/javascript/options.js b/chrome/common/extensions/docs/examples/extensions/calendar/javascript/options.js new file mode 100644 index 0000000..1a6ce65 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/calendar/javascript/options.js @@ -0,0 +1,73 @@ +/** + * 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. + */ + +//Contains true if multiple calendar option is checked, false otherwise. +var isMultiCalendar; + +//adding listener when body is loaded to call init function. +window.addEventListener('load', init, false); + +/** + * Sets the value of multiple calendar checkbox based on value from + * local storage. + */ + function init() { + isMultiCalendar = JSON.parse(localStorage.multiCalendar || false); + $('multiCalendar').checked = isMultiCalendar; + $('multiCalendarText').innerHTML = + chrome.i18n.getMessage('multiCalendarText'); + $('optionsTitle').innerHTML = chrome.i18n.getMessage('optionsTitle'); + $('imageTooltip').title = chrome.i18n.getMessage('imageTooltip'); + $('imageTooltip').alt = chrome.i18n.getMessage('imageTooltip'); + $('multiCalendarText').title = chrome.i18n.getMessage('multiCalendarToolTip'); + $('multiCalendar').title = chrome.i18n.getMessage('multiCalendarToolTip'); + $('extensionName').innerHTML = chrome.i18n.getMessage('extensionName'); + if (chrome.i18n.getMessage('direction') == 'rtl') { + document.querySelector('body').style.direction = 'rtl'; + } +}; + +/** + * Saves the value of the checkbox into local storage. + */ +function save() { + var multiCalendarId = $('multiCalendar'); + localStorage.multiCalendar = multiCalendarId.checked; + if (multiCalendarId) { + multiCalendar.disabled = true; + } + $('status').innerHTML = chrome.i18n.getMessage('status_saving'); + $('status').style.display = 'block'; + chrome.extension.getBackgroundPage().onSettingsChange(); +}; + +/** + * Fired when a request is sent from either an extension process or a content + * script. Add Listener to enable the save checkbox button on server response. + * @param {String} request Request sent by the calling script. + * @param {Object} sender Information about the script that sent a message or + * request. + * @param {Function} sendResponse Function to call when there is a response. + * The argument should be any JSON-ifiable object, or undefined if there + * is no response. + */ +chrome.extension.onRequest.addListener(function(request, sender, sendResponse) { + if (!request.message) + return; + switch (request.message) { + case 'enableSave': + if ($('multiCalendar')) { + if ($('multiCalendar').disabled) { + $('status').innerHTML = chrome.i18n.getMessage('status_saved'); + $('status').style.display = 'block'; + setTimeout("$('status').style.display = 'none'", 1500); + } + $('multiCalendar').disabled = false; + } + sendResponse(); + break; + } +}); diff --git a/chrome/common/extensions/docs/examples/extensions/calendar/javascript/util.js b/chrome/common/extensions/docs/examples/extensions/calendar/javascript/util.js new file mode 100644 index 0000000..1ad5ac69 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/calendar/javascript/util.js @@ -0,0 +1,14 @@ +/** + * 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. + */ + +/** + * Alias for document.getElementById. + * @param {string} id The id of the element. + * @return {HTMLElement} The html element for the given element id. + */ +function $(id) { + return document.getElementById(id); +}; diff --git a/chrome/common/extensions/docs/examples/extensions/calendar/manifest.json b/chrome/common/extensions/docs/examples/extensions/calendar/manifest.json new file mode 100644 index 0000000..e5eb37b --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/calendar/manifest.json @@ -0,0 +1,18 @@ +{ + "name": "__MSG_name__", + "description": "__MSG_description__", + "default_locale":"en", + "options_page": "views/options.html", + "version": "1.1.0", + "background_page": "views/background.html", + "permissions": [ + "tabs", "http://*.google.com/", "https://*.google.com/" + ], + "browser_action": { + "default_title": "__MSG_title__" + }, + "icons": { + "128": "images/icon-128.gif", + "16":"images/icon-16.gif" + } +} diff --git a/chrome/common/extensions/docs/examples/extensions/calendar/views/background.html b/chrome/common/extensions/docs/examples/extensions/calendar/views/background.html new file mode 100644 index 0000000..b90a41b --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/calendar/views/background.html @@ -0,0 +1,16 @@ +<!DOCTYPE html> +<!-- + * 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. +--> +<html> + <head> + <script src="../javascript/util.js"></script> + <script src="../javascript/background.js"></script> + </head> + <body> + <img id="logged_in" src="../images/icon-16.gif"> + <canvas id="canvas" width="19" height="19"></canvas> + </body> +</html> diff --git a/chrome/common/extensions/docs/examples/extensions/calendar/views/options.html b/chrome/common/extensions/docs/examples/extensions/calendar/views/options.html new file mode 100644 index 0000000..1cc70c2 --- /dev/null +++ b/chrome/common/extensions/docs/examples/extensions/calendar/views/options.html @@ -0,0 +1,87 @@ +<!DOCTYPE html> +<!-- + * 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. +--> +<html> +<head> + <title id=optionsTitle></title> + <script src="../javascript/options.js"></script> + <script src="../javascript/util.js"></script> + <style> + #content { + background-color: white; + border: 4px solid #B5C7DE; + border-radius: 12px; + margin: 40px auto 20px; + padding: 8px; + width: 600px; + } + .option_row { + clear: left; + padding: 2.5em 1em 0; + text-align:center + } + #status { + background-color: rgb(255, 241, 168); + display: none; + margin-left: 3px; + padding: 1px 2px; + text-align: center; + font-size: 15px; + color: #000; + } + #multiCalendarText { + font-size: 15px; + color: #000; + } + body { + background-color: "#ebeff9"; + } + #logo { + font-size: 20px; + text-align: center; + } + #extensionName { + color: #444; + } + </style> + +</head> +<body> + <div id="content"> + <div id = "logo"> + <img src="../images/icon-128.gif" width="48"> + <img src="../images/calendar_logo.gif" id="imageTooltip" title="" alt=""> + <br> + <label id="extensionName" ></label> + </div> + + <div class="option_row"> + <div> + <table cellpadding=1 width=100%> + <tr> + <td width="15%"> + + </td> + <td width="5%"> + <input type="checkbox" id="multiCalendar" name="multiCalendar" onclick="save()" title=""> + </td> + <td width="40%" align="left"> + <span id="multiCalendarText" title=""></span> + </td> + <td width="30%"> + <label id="status"></label> + </td> + <td width="10%"> + + </td> + </tr> + </table> + <br> + </div> + </div> + </div> +</body> +</html> |