Commit 8a9227c8 authored by kalman's avatar kalman Committed by Commit bot

Replace the sample Calendar extension with a deprecation notice linking to a

working (also By Google) Calendar extension.

BUG=435290
R=aboxhall@chromium.org
NOTRY=true

Review URL: https://codereview.chromium.org/890173003

Cr-Commit-Position: refs/heads/master@{#314075}
parent f5231cd4
......@@ -4,749 +4,39 @@
* 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 5 min.
var DRAW_INTERVAL_MINUTES = 5.0;
// Object for BadgeAnimation
var badgeAnimation_;
//Object for CanvasAnimation
var canvasAnimation_;
var pollUnderProgress = false;
var defaultAuthor = '';
var isMultiCalendar = false;
// The Calendar API identifies requests from this extension by the 'extid' get
// parameter, set to this extension's ID.
var API_KEY = 'extid=' + chrome.runtime.id;
var warningId = 'notification.warning';
//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&' + API_KEY;
//URL for getting feed of multiple calendar support.
var MULTIPLE_CALENDAR_SUPPORT_URL = 'https://www.google.com/calendar/feeds' +
'/default/allcalendars/full?' + API_KEY;
//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;
/**
* Sets |key| as |value| in localStorage. |value| may be any JavaScript object;
* this method will automatically stringify to JSON if needed.
*/
function localStorageSet(key, value) {
if (typeof value == 'undefined') {
// Don't try to stringify undefined, or bad things may happen (particularly
// in localStorageGet, so let's be consistent).
delete localStorage[key];
} else {
localStorage[key] = JSON.stringify(value);
}
function hideWarning(done) {
chrome.notifications.clear(warningId, function() {
if (done) done();
});
}
/**
* Gets the JavaScript object at |key| from localStorage, defaulting to |deflt|
* if it hasn't been set. Assumes that the value was written by localStorageSet
* (i.e. stored as JSON).
*/
function localStorageGet(key, deflt) {
var value = localStorage[key];
return (typeof value == 'undefined') ? deflt : JSON.parse(value);
function showWarning() {
hideWarning(function() {
chrome.notifications.create(warningId, {
iconUrl: chrome.runtime.getURL('images/icon-48.png'),
title: 'Removal required',
type: 'basic',
message: chrome.i18n.getMessage('name') + ' is obsolete ' +
'and must be removed. A replacement Extension ' +
'is available.',
buttons: [{ title: 'Learn More' }],
isClickable: true,
priority: 2,
}, function() {});
});
}
/**
* 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();
var nextEvent = localStorageGet('nextEvent');
if (!nextEvent) {
this.showLoggedOut();
} else {
this.drawIconAtRotation();
this.rotation_ = 0;
var ms = (new Date(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_});
var nextEvents = localStorageGet('nextEvents');
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;
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 = (new Date(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.
* @param {Object} mailId email of the owner of calendar in multiple calendar
* support.
* @return {Object} out An object containing the event properties.
*/
CalendarManager.extractEvent = function(elem, mailId) {
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) {
if ((!isMultiCalendar) || (isMultiCalendar && mailId &&
node.getAttribute('email') == mailId)) {
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) {
localStorageSet('eventList', []);
pollUnderProgress = true;
pendingLoadId_ = null;
localStorageSet('calendars', []);
localStorageSet('lastPollTime', getCurrentTime());
var url;
var xhr = new XMLHttpRequest();
try {
xhr.onreadystatechange = CalendarManager.genResponseChangeFunc(xhr);
xhr.onerror = function(error) {
console.error('error:', error);
localStorageSet('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.error('ex:', e);
localStorageSet('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');
localStorageSet('nextEvent', null);
canvasAnimation_.drawFinal();
return;
}
if (isMultiCalendar) {
var entry_ = xhr.responseXML.getElementsByTagName('entry');
if (entry_ && entry_.length > 0) {
var 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);
}
}
}
localStorageSet('calendars', calendars);
CalendarManager.getCalendarFeed(0);
return;
}
} else {
var calendars = [];
calendars.push(SINGLE_CALENDAR_SUPPORT_URL);
localStorageSet('calendars', calendars);
CalendarManager.parseCalendarEntry(xhr.responseXML, 0);
return;
}
console.error('Error: feed retrieved, but no event found');
localStorageSet('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.error('error:', error);
localStorageSet('nextEvent', null);
canvasAnimation_.drawFinal();
};
// Augment the calendar's URL with the API key.
var calendarUrl = localStorageGet('calendars')[calendarId] + '&' + API_KEY;
xmlhttp.open('GET', calendarUrl);
xmlhttp.send(null);
}
catch (e) {
console.error('ex:', e);
localStorageSet('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');
localStorageSet('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 mailId = null;
var author = null;
if (responseXML.querySelector('author name')) {
author = responseXML.querySelector('author name').textContent;
}
if (responseXML.querySelector('author email')) {
mailId = responseXML.querySelector('author email').textContent;
}
if (entry_ && entry_.length > 0) {
var eventList = localStorageGet('eventList', []);
for (var i = 0, entry; entry = entry_[i]; ++i) {
var event_ = CalendarManager.extractEvent(entry, mailId);
// 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 && author) {
event_.author = author;
}
eventList.push(event_);
}
}
}
localStorageSet('eventList', eventList);
}
calendarId++;
//get the next calendar
if (calendarId < localStorageGet('calendars', []).length) {
CalendarManager.getCalendarFeed(calendarId);
} else {
CalendarManager.populateLatestEvent(localStorageGet('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) {
var nextEvents = [];
if (isMultiCalendar) {
eventList.sort(sortByDate);
}
//populating next events array.
if (eventList.length > 0) {
nextEvent = localStorageGet('nextEvent');
nextEvent = eventList[0];
nextEvents.push(nextEvent);
var startTime = (new Date(nextEvent.startTime)).setSeconds(0, 0);
for (var i = 1, event; event = eventList[i]; i++) {
var time = (new Date(event.startTime)).setSeconds(0, 0);
if (time == startTime) {
nextEvents.push(event);
} else {
break;
}
}
if (nextEvents.length > 1 && isMultiCalendar) {
nextEvents.sort(sortByAuthor);
}
localStorageSet('nextEvents', nextEvents);
localStorageSet('nextEvent', nextEvent);
canvasAnimation_.animate();
return;
} else {
console.error('Error: feed retrieved, but no event found');
localStorageSet('nextEvent', null);
localStorageSet('nextEvents', nextEvents);
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 ((new Date(event_1.startTime)).getTime() -
(new Date(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_1.author && event_2.author && 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.
var nextEvent = localStorageGet('nextEvent');
if (nextEvent) {
var t = (new Date(nextEvent.startTime)).getTime() - getCurrentTime();
if (t <= 0) {
CalendarManager.pollServer();
return;
}
}
canvasAnimation_.drawFinal();
// if 30 minutes have passed re-poll
if (getCurrentTime() - localStorageGet('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(/&lt;/g, '<');
data = data.replace(/&gt;/g, '>');
data = data.replace(/&amp;/g, '&');
data = data.replace(/%7B/g, '{');
data = data.replace(/%7D/g, '}');
data = data.replace(/&quot;/g, '"');
data = data.replace(/&#39;/g, '\'');
}
return data;
};
/**
* Called from options.js page on saving the settings
*/
function onSettingsChange() {
isMultiCalendar = localStorageGet('multiCalendar', false);
badgeAnimation_.start();
CalendarManager.pollServer();
};
/**
* Function runs on completed navigation with a url of google applications.
* @param {details} details of the completed web navigation.
*/
function onCompleted(details) {
var url = details.url;
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)) {
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();
function openWarningPage() {
chrome.tabs.create({
url: 'chrome://extensions?options=' + chrome.runtime.id
});
};
/**
* 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;
};
function onInstalled() {
badgeAnimation_.start();
CalendarManager.pollServer();
localStorageSet('lastPollTime', 0);
localStorageSet('nextEvent', null);
}
/*
* Load animation contexts and add listeners.
*/
badgeAnimation_ = new BadgeAnimation();
canvasAnimation_ = new CanvasAnimation();
isMultiCalendar = localStorageGet('multiCalendar', false);
chrome.browserAction.setIcon({path: '../images/icon-16.gif'});
chrome.runtime.onInstalled.addListener(onInstalled);
chrome.alarms.onAlarm.addListener(redraw)
chrome.alarms.create('redraw', {periodInMinutes: DRAW_INTERVAL_MINUTES});
chrome.webNavigation.onCompleted.addListener(onCompleted,
{url: [{hostSuffix: 'www.google.com', pathPrefix: '/calendar'},
{hostSuffix: 'www.google.com', pathPrefix: '/accounts'},
{hostSuffix: 'www.google.com', pathPrefix: '/a'}]});
chrome.browserAction.onClicked.addListener(onClickAction);
redraw();
chrome.browserAction.setBadgeBackgroundColor({ color: '#FF0000' });
chrome.browserAction.setBadgeText({ text: '!' });
chrome.browserAction.onClicked.addListener(openWarningPage);
chrome.notifications.onClicked.addListener(openWarningPage);
chrome.notifications.onButtonClicked.addListener(openWarningPage);
chrome.runtime.onInstalled.addListener(showWarning);
......@@ -4,62 +4,14 @@
* LICENSE file.
*/
//Contains true if multiple calendar option is checked, false otherwise.
var isMultiCalendar;
var $ = document.getElementById.bind(document);
//adding listener when body is loaded to call init function.
window.addEventListener('load', init, false);
var url = 'https://chrome.google.com/webstore/detail/' +
'google-calendar-by-google/gmbgaklkmjakoegficnlkhebmhkjfich';
/**
* 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');
$('multiCalendar').addEventListener('click', save);
$('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') {
$('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';
// Sends message to the background page notifying it that the settings
// have updated.
chrome.runtime.getBackgroundPage(function(bg) {
bg.onSettingsChange();
statusSaved();
});
};
function statusSaved() {
if ($('multiCalendar')) {
if ($('multiCalendar').disabled) {
$('status').innerHTML = chrome.i18n.getMessage('status_saved');
$('status').style.display = 'block';
setTimeout(
function() { $('status').style.display = 'none'; }
, 1500);
}
$('multiCalendar').disabled = false;
}
$('name').textContent = chrome.i18n.getMessage('name');
$('link').href = url;
$('remove').onclick = function() {
chrome.management.uninstallSelf({showConfirmDialog: true});
window.close();
};
/**
* 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);
};
......@@ -4,19 +4,28 @@
"manifest_version": 2,
"default_locale":"en",
"options_page": "views/options.html",
"version": "1.5.0",
"options_ui": {
"page": "views/options.html",
"chrome_style": true
},
"version": "2.0.0",
"background": {
"page": "views/background.html",
"scripts": ["javascript/background.js"],
"persistent": false
},
},
"permissions": [
"alarms", "tabs", "webNavigation", "http://*.google.com/", "https://*.google.com/"
],
"notifications"
],
"browser_action": {
"default_icon": {
"19": "images/icon-19.png",
"38": "images/icon-38.png"
},
"default_title": "__MSG_title__"
},
"icons": {
"128": "images/icon-128.gif",
"16":"images/icon-16.gif"
"128": "images/icon-128.png",
"48": "images/icon-48.png",
"16":"images/icon-16.png"
}
}
<!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>
</head>
<body>
<img id="logged_in" src="../images/icon-16.gif">
<canvas id="canvas" width="19" height="19"></canvas>
<script src="../javascript/util.js"></script>
<script src="../javascript/background.js"></script>
</body>
</html>
......@@ -5,83 +5,23 @@
* 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">&nbsp;&nbsp;&nbsp;
<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%">
&nbsp;
</td>
<td width="5%">
<input type="checkbox" id="multiCalendar" name="multiCalendar" title="">
</td>
<td width="40%" align="left">
<span id="multiCalendarText" title=""></span>
</td>
<td width="30%">
<label id="status"></label>
</td>
<td width="10%">
&nbsp;
</td>
</tr>
</table>
<br>
</div>
</div>
</div>
<p>
<strong><span id="name"></span> is now obsolete and must be removed, as it
relies on an API which will be turned off imminently</strong>.
<a href="https://developers.google.com/google-apps/calendar/v2/developers_guide_protocol">Learn more</a>.
</p>
<p>
Instead, we recommend the more powerful <a id="link">Google Calendar (by
Google)</a> Extension.
</p>
<p>
<button id="remove">Remove from Chrome...</button>
</p>
<script src="../javascript/options.js"></script>
</body>
</html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment