'
133 | + '
'
134 | + hcalendar.summary
135 | + ' '
136 | + '
'
137 | + ''+ $.fn.strftime($.fn.parseISO(hcalendar.dtstart), '%b %d, %Y at %i:%M%P') +' '
138 | + ' '
139 | + '
'
148 | + '');
149 |
150 | $("#hcalendars").append(hcalHtml);
151 |
152 | // Handle submit to Google link
153 | $('.submitvevent', hcalHtml).click(function(){
154 | bgPage.Export.HCalendar.google(hcalendar, function(txt, response){
155 | if (response.status == 201) {
156 | $('.submitvevent span', hcalHtml).first().replaceWith('
3 ');
157 | } else {
158 | console.log('FAILED');
159 | }
160 | });
161 | return false;
162 | });
163 |
164 | if (hcalendar.dtend)
165 | $('#hcal-'+j+' ul .start').append(' — '+ $.fn.strftime($.fn.parseISO(hcalendar.dtend), '%b %d, %Y at %i:%M%P'));
166 |
167 | if (hcalendar.location) {
168 | var location = hcalendar.location.adr[0];
169 | var street = location["street-address"][0] ? location["street-address"][0] : '';
170 | var locality = location.locality ? location.locality : '';
171 | var region = location.region ? location.region : '';
172 | var country = location["country-name"] ? location["country-name"] : '';
173 | var postalCode = location["postal-code"] ? location["postal-code"] : '';
174 | var mapString = [street, locality, region, country, postalCode];
175 |
176 | $('#hcal-'+j+' ul').append('
'+hcalendar.location.fn+' ');
177 | }
178 | });
179 |
180 | $.each(hreviews, function(k, hreview) {
181 | var hreviewHTML = $('
' + (hreview.item.fn || hreview.item.url || hreview.item.photo_url) + ' ');
182 |
183 | $("#hreviews").append(hreviewHTML);
184 |
185 | if (hreview.summary)
186 | $('#hreview-'+k+' ul').append('
' + hreview.summary + ' ');
187 |
188 | if (hreview.description)
189 | $('#hreview-'+k+' ul').append('
'+ hreview.description +' ');
190 |
191 | if (hreview.dtreviewed)
192 | $('#hreview-'+k+' ul').append('
Reviewed: '+ hreview.dtreviewed +'');
193 |
194 | if (hreview.rating)
195 | $('#hreview-'+k+' ul').append('
Rating: '+ hreview.rating +'');
196 | });
197 |
198 | $.each(hreviewaggs, function(m, hreviewagg) {
199 | var hreviewaggHTML = $('
' + (hreviewagg.count || hreviewagg.item.fn || hreviewagg.item.url || hreviewagg.item.photo_url) + ' ');
200 |
201 | $("#hreviewaggs").append(hreviewaggHTML);
202 |
203 | if (hreviewagg.summary)
204 | $('#hreviewagg-'+m+' ul').append('
'+ hreviewagg.summary +' ');
205 |
206 | if (hreviewagg.rating)
207 | $('#hreviewagg-'+m+' ul').append('
Rating: '+ hreviewagg.rating +'');
208 |
209 | if (hreviewagg.count)
210 | $('#hreviewagg-'+m+' ul').append('
Count: ' + hreviewagg.count + '');
211 |
212 | if (hreviewagg.votes)
213 | $('#hreviewagg-'+m+' ul').append('
Votes: '+ hreviewagg.votes +'');
214 | });
215 |
216 | $.each(hrecipes, function(l, hrecipe) {
217 | var hrecipeHTML = $('
');
218 |
219 | $("#hrecipes").append(hrecipeHTML);
220 |
221 | $.each(hrecipe.ingredient, function(m, ingredient) {
222 | $('#hrecipe-'+l+' .ingredients').append('
'+ ingredient +' ');
223 | });
224 |
225 | if (hrecipe.instructions)
226 | $('#hrecipe-'+l+' .other').append('
Directions '+ hrecipe.instructions +' ');
227 |
228 | if (hrecipe.duration)
229 | $('#hrecipe-'+l+' .other').append('
Total time: '+ hrecipe.duration[0] +'');
230 |
231 | if (hrecipe.yield)
232 | $('#hrecipe-'+l+' .other').append('
Yield: '+ hrecipe.yield +'');
233 |
234 | if (hrecipe.author)
235 | $('#hrecipe-'+l+' .other').append('
From: '+ hrecipe.author +'');
236 |
237 | });
238 |
239 | $.each(unique_geos(geos), function(l, geo) {
240 | var geoHTML = $('
'+ geo.latitude +', '+ geo.longitude +' ');
241 |
242 | $("#geos").append(geoHTML);
243 |
244 | var mapString = [geo.latitude, geo.longitude];
245 | $('#geo-'+l+' ul').append('
');
246 | });
247 | });
--------------------------------------------------------------------------------
/javascripts/utils/PerfectTime.js:
--------------------------------------------------------------------------------
1 | /*
2 | Original implimentation by Why The Lucky Stiff
3 |
, described at:
4 |
5 | http://redhanded.hobix.com/inspect/showingPerfectTime.html
6 |
7 | Modified to fit in a single, unobtrusive javascript
8 | class by Mike West
9 |
10 | I'm not sure what the original license chosen for this
11 | code was. I'm assuming it's liberal enough, and this
12 | class is released under the same license, whatever that
13 | turns out to be.
14 |
15 | Heavily refactored by Daniel Morrison
16 | http://github.com/collectiveidea/perfecttime
17 | */
18 |
19 | /* PerfectTime */
20 | $(function(){
21 | var _defaultFormat = '%d %b %Y at %H:%M';
22 |
23 | $.fn.perfectTime = function(format) {
24 | return this.each( function() {
25 | var fmt = (format)?format:_defaultFormat;
26 | var newDate = $.fn.parseISO($(this).attr('title'));
27 | $(this).html($.fn.strftime(newDate, fmt));
28 | });
29 | }
30 |
31 | var isoRegEx = /(\d{4})(-?(\d{2})(-?(\d{2})((T|\s)(\d{2}):?(\d{2})(:?(\d{2})([.]?(\d+))?)?(Z|(([+-])(\d{2}):?(\d{2}))?)?)?)?)?/;
32 |
33 | $.fn.parseISO = function(isoString) {
34 | // Parse ISO 8601 type times (e.g. hCalendar)
35 | // based on Paul Sowden's method, tweaked to match up
36 | // with 'real world' hCalendar usage:
37 | //
38 | // http://delete.me.uk/2005/03/iso8601.html
39 | //
40 | var d = isoString.match(isoRegEx);
41 |
42 | var theDate = new Date(d[1], 0, 1);
43 |
44 | //
- 1: Because JS months are 0-11
45 | if (d[ 3]) { theDate.setMonth( d[ 3] - 1); }
46 | if (d[ 5]) { theDate.setDate( d[ 5]); }
47 | if (d[ 8]) { theDate.setHours( d[ 8]); }
48 | if (d[ 9]) { theDate.setMinutes(d[ 9]); }
49 | if (d[11]) { theDate.setSeconds(d[11]); }
50 | // Must be between 0 and 999), using Paul Sowden's method: http://delete.me.uk/2005/03/iso8601.html
51 | if (d[13]) { theDate.setMilliseconds(Number("0." + d[13]) * 1000); }
52 | var offset = 0;
53 | if (d[16]) {
54 | var offset = (Number(d[17])*60 + Number(d[18])) * 60;
55 | if (d[16] == "+") { offset *= -1; }
56 | }
57 |
58 | offset -= theDate.getTimezoneOffset() * 60;
59 | theDate.setTime(Number(theDate) + (offset * 1000));
60 | return theDate;
61 | };
62 |
63 | var strftime_funks = {
64 | zeropad:
65 | function( n ){ return n>9 ? n : '0'+n; },
66 | a: function(t) { return ['Sun','Mon','Tue','Wed','Thu','Fri','Sat'][t.getDay()] },
67 | A: function(t) { return ['Sunday','Monday','Tuedsay','Wednesday','Thursday','Friday','Saturday'][t.getDay()] },
68 | b: function(t) { return ['Jan','Feb','Mar','Apr','May','Jun', 'Jul','Aug','Sep','Oct','Nov','Dec'][t.getMonth()] },
69 | B: function(t) { return ['January','February','March','April','May','June', 'July','August', 'September','October','November','December'][t.getMonth()] },
70 | c: function(t) { return t.toString() },
71 | d: function(t) { return this.zeropad(t.getDate()) },
72 | D: function(t) { return t.getDate() },
73 | H: function(t) { return this.zeropad(t.getHours()) },
74 | i: function(t) { return (t.getHours() + 12) % 12 },
75 | I: function(t) { return this.zeropad((t.getHours() + 12) % 12) },
76 | l: function(t) { return (t.getHours() + 12) % 12 },
77 | m: function(t) { return this.zeropad(t.getMonth()+1) }, // month-1
78 | M: function(t) { return this.zeropad(t.getMinutes()) },
79 | p: function(t) { return this.H(t) < 12 ? 'AM' : 'PM'; },
80 | P: function(t) { return this.H(t) < 12 ? 'am' : 'pm'; },
81 | S: function(t) { return this.zeropad(t.getSeconds()) },
82 | w: function(t) { return t.getDay() }, // 0..6 == sun..sat
83 | y: function(t) { return this.zeropad(this.Y(t) % 100); },
84 | Y: function(t) { return t.getFullYear() },
85 | Z: function(t) {
86 | if (t.getTimezoneOffset() > 0) {
87 | return "-" + this.zeropad(t.getTimezoneOffset()/60) + "00";
88 | } else {
89 | return "+" + this.zeropad(Math.abs(t.getTimezoneOffset())/60) + "00";
90 | }
91 | },
92 | '%': function(t) { return '%' }
93 | };
94 | $.fn.strftime = function(theDate, format) {
95 | for (var s in strftime_funks) {
96 | if (s.length == 1) {
97 | format = format.replace('%' + s, strftime_funks[s](theDate));
98 | }
99 | }
100 | return format;
101 | }
102 |
103 | });
--------------------------------------------------------------------------------
/javascripts/utils/chrome_ex_oauth.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Copyright (c) 2010 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
4 | * LICENSE file.
5 | */
6 |
7 | /**
8 | * Constructor - no need to invoke directly, call initBackgroundPage instead.
9 | * @constructor
10 | * @param {String} url_request_token The OAuth request token URL.
11 | * @param {String} url_auth_token The OAuth authorize token URL.
12 | * @param {String} url_access_token The OAuth access token URL.
13 | * @param {String} consumer_key The OAuth consumer key.
14 | * @param {String} consumer_secret The OAuth consumer secret.
15 | * @param {String} oauth_scope The OAuth scope parameter.
16 | * @param {Object} opt_args Optional arguments. Recognized parameters:
17 | * "app_name" {String} Name of the current application
18 | * "callback_page" {String} If you renamed chrome_ex_oauth.html, the name
19 | * this file was renamed to.
20 | */
21 | function ChromeExOAuth(url_request_token, url_auth_token, url_access_token,
22 | consumer_key, consumer_secret, oauth_scope, opt_args) {
23 | this.url_request_token = url_request_token;
24 | this.url_auth_token = url_auth_token;
25 | this.url_access_token = url_access_token;
26 | this.consumer_key = consumer_key;
27 | this.consumer_secret = consumer_secret;
28 | this.oauth_scope = oauth_scope;
29 | this.app_name = opt_args && opt_args['app_name'] ||
30 | "ChromeExOAuth Library";
31 | this.key_token = "oauth_token";
32 | this.key_token_secret = "oauth_token_secret";
33 | this.callback_page = opt_args && opt_args['callback_page'] ||
34 | "chrome_ex_oauth.html";
35 | this.auth_params = {};
36 | if (opt_args && opt_args['auth_params']) {
37 | for (key in opt_args['auth_params']) {
38 | if (opt_args['auth_params'].hasOwnProperty(key)) {
39 | this.auth_params[key] = opt_args['auth_params'][key];
40 | }
41 | }
42 | }
43 | };
44 |
45 | /*******************************************************************************
46 | * PUBLIC API METHODS
47 | * Call these from your background page.
48 | ******************************************************************************/
49 |
50 | /**
51 | * Initializes the OAuth helper from the background page. You must call this
52 | * before attempting to make any OAuth calls.
53 | * @param {Object} oauth_config Configuration parameters in a JavaScript object.
54 | * The following parameters are recognized:
55 | * "request_url" {String} OAuth request token URL.
56 | * "authorize_url" {String} OAuth authorize token URL.
57 | * "access_url" {String} OAuth access token URL.
58 | * "consumer_key" {String} OAuth consumer key.
59 | * "consumer_secret" {String} OAuth consumer secret.
60 | * "scope" {String} OAuth access scope.
61 | * "app_name" {String} Application name.
62 | * "auth_params" {Object} Additional parameters to pass to the
63 | * Authorization token URL. For an example, 'hd', 'hl', 'btmpl':
64 | * http://code.google.com/apis/accounts/docs/OAuth_ref.html#GetAuth
65 | * @return {ChromeExOAuth} An initialized ChromeExOAuth object.
66 | */
67 | ChromeExOAuth.initBackgroundPage = function(oauth_config) {
68 | window.chromeExOAuthConfig = oauth_config;
69 | window.chromeExOAuth = ChromeExOAuth.fromConfig(oauth_config);
70 | window.chromeExOAuthRedirectStarted = false;
71 | window.chromeExOAuthRequestingAccess = false;
72 |
73 | var url_match = chrome.extension.getURL(window.chromeExOAuth.callback_page);
74 | var tabs = {};
75 | chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) {
76 | if (changeInfo.url &&
77 | changeInfo.url.substr(0, url_match.length) === url_match &&
78 | changeInfo.url != tabs[tabId] &&
79 | window.chromeExOAuthRequestingAccess == false) {
80 | chrome.tabs.create({ 'url' : changeInfo.url }, function(tab) {
81 | tabs[tab.id] = tab.url;
82 | chrome.tabs.remove(tabId);
83 | });
84 | }
85 | });
86 |
87 | return window.chromeExOAuth;
88 | };
89 |
90 | /**
91 | * Authorizes the current user with the configued API. You must call this
92 | * before calling sendSignedRequest.
93 | * @param {Function} callback A function to call once an access token has
94 | * been obtained. This callback will be passed the following arguments:
95 | * token {String} The OAuth access token.
96 | * secret {String} The OAuth access token secret.
97 | */
98 | ChromeExOAuth.prototype.authorize = function(callback) {
99 | if (this.hasToken()) {
100 | callback(this.getToken(), this.getTokenSecret());
101 | } else {
102 | window.chromeExOAuthOnAuthorize = function(token, secret) {
103 | callback(token, secret);
104 | };
105 | chrome.tabs.create({ 'url' :chrome.extension.getURL(this.callback_page) });
106 | }
107 | };
108 |
109 | /**
110 | * Clears any OAuth tokens stored for this configuration. Effectively a
111 | * "logout" of the configured OAuth API.
112 | */
113 | ChromeExOAuth.prototype.clearTokens = function() {
114 | delete localStorage[this.key_token + encodeURI(this.oauth_scope)];
115 | delete localStorage[this.key_token_secret + encodeURI(this.oauth_scope)];
116 | };
117 |
118 | /**
119 | * Returns whether a token is currently stored for this configuration.
120 | * Effectively a check to see whether the current user is "logged in" to
121 | * the configured OAuth API.
122 | * @return {Boolean} True if an access token exists.
123 | */
124 | ChromeExOAuth.prototype.hasToken = function() {
125 | return !!this.getToken();
126 | };
127 |
128 | /**
129 | * Makes an OAuth-signed HTTP request with the currently authorized tokens.
130 | * @param {String} url The URL to send the request to. Querystring parameters
131 | * should be omitted.
132 | * @param {Function} callback A function to be called once the request is
133 | * completed. This callback will be passed the following arguments:
134 | * responseText {String} The text response.
135 | * xhr {XMLHttpRequest} The XMLHttpRequest object which was used to
136 | * send the request. Useful if you need to check response status
137 | * code, etc.
138 | * @param {Object} opt_params Additional parameters to configure the request.
139 | * The following parameters are accepted:
140 | * "method" {String} The HTTP method to use. Defaults to "GET".
141 | * "body" {String} A request body to send. Defaults to null.
142 | * "parameters" {Object} Query parameters to include in the request.
143 | * "headers" {Object} Additional headers to include in the request.
144 | */
145 | ChromeExOAuth.prototype.sendSignedRequest = function(url, callback,
146 | opt_params) {
147 | var method = opt_params && opt_params['method'] || 'GET';
148 | var body = opt_params && opt_params['body'] || null;
149 | var params = opt_params && opt_params['parameters'] || {};
150 | var headers = opt_params && opt_params['headers'] || {};
151 |
152 | var signedUrl = this.signURL(url, method, params);
153 |
154 | ChromeExOAuth.sendRequest(method, signedUrl, headers, body, function (xhr) {
155 | if (xhr.readyState == 4) {
156 | callback(xhr.responseText, xhr);
157 | }
158 | });
159 | };
160 |
161 | /**
162 | * Adds the required OAuth parameters to the given url and returns the
163 | * result. Useful if you need a signed url but don't want to make an XHR
164 | * request.
165 | * @param {String} method The http method to use.
166 | * @param {String} url The base url of the resource you are querying.
167 | * @param {Object} opt_params Query parameters to include in the request.
168 | * @return {String} The base url plus any query params plus any OAuth params.
169 | */
170 | ChromeExOAuth.prototype.signURL = function(url, method, opt_params) {
171 | var token = this.getToken();
172 | var secret = this.getTokenSecret();
173 | if (!token || !secret) {
174 | throw new Error("No oauth token or token secret");
175 | }
176 |
177 | var params = opt_params || {};
178 |
179 | var result = OAuthSimple().sign({
180 | action : method,
181 | path : url,
182 | parameters : params,
183 | signatures: {
184 | consumer_key : this.consumer_key,
185 | shared_secret : this.consumer_secret,
186 | oauth_secret : secret,
187 | oauth_token: token
188 | }
189 | });
190 |
191 | return result.signed_url;
192 | };
193 |
194 | /**
195 | * Generates the Authorization header based on the oauth parameters.
196 | * @param {String} url The base url of the resource you are querying.
197 | * @param {Object} opt_params Query parameters to include in the request.
198 | * @return {String} An Authorization header containing the oauth_* params.
199 | */
200 | ChromeExOAuth.prototype.getAuthorizationHeader = function(url, method,
201 | opt_params) {
202 | var token = this.getToken();
203 | var secret = this.getTokenSecret();
204 | if (!token || !secret) {
205 | throw new Error("No oauth token or token secret");
206 | }
207 |
208 | var params = opt_params || {};
209 |
210 | return OAuthSimple().getHeaderString({
211 | action: method,
212 | path : url,
213 | parameters : params,
214 | signatures: {
215 | consumer_key : this.consumer_key,
216 | shared_secret : this.consumer_secret,
217 | oauth_secret : secret,
218 | oauth_token: token
219 | }
220 | });
221 | };
222 |
223 | /*******************************************************************************
224 | * PRIVATE API METHODS
225 | * Used by the library. There should be no need to call these methods directly.
226 | ******************************************************************************/
227 |
228 | /**
229 | * Creates a new ChromeExOAuth object from the supplied configuration object.
230 | * @param {Object} oauth_config Configuration parameters in a JavaScript object.
231 | * The following parameters are recognized:
232 | * "request_url" {String} OAuth request token URL.
233 | * "authorize_url" {String} OAuth authorize token URL.
234 | * "access_url" {String} OAuth access token URL.
235 | * "consumer_key" {String} OAuth consumer key.
236 | * "consumer_secret" {String} OAuth consumer secret.
237 | * "scope" {String} OAuth access scope.
238 | * "app_name" {String} Application name.
239 | * "auth_params" {Object} Additional parameters to pass to the
240 | * Authorization token URL. For an example, 'hd', 'hl', 'btmpl':
241 | * http://code.google.com/apis/accounts/docs/OAuth_ref.html#GetAuth
242 | * @return {ChromeExOAuth} An initialized ChromeExOAuth object.
243 | */
244 | ChromeExOAuth.fromConfig = function(oauth_config) {
245 | return new ChromeExOAuth(
246 | oauth_config['request_url'],
247 | oauth_config['authorize_url'],
248 | oauth_config['access_url'],
249 | oauth_config['consumer_key'],
250 | oauth_config['consumer_secret'],
251 | oauth_config['scope'],
252 | {
253 | 'app_name' : oauth_config['app_name'],
254 | 'auth_params' : oauth_config['auth_params']
255 | }
256 | );
257 | };
258 |
259 | /**
260 | * Initializes chrome_ex_oauth.html and redirects the page if needed to start
261 | * the OAuth flow. Once an access token is obtained, this function closes
262 | * chrome_ex_oauth.html.
263 | */
264 | ChromeExOAuth.initCallbackPage = function() {
265 | var background_page = chrome.extension.getBackgroundPage();
266 | var oauth_config = background_page.chromeExOAuthConfig;
267 | var oauth = ChromeExOAuth.fromConfig(oauth_config);
268 | background_page.chromeExOAuthRedirectStarted = true;
269 | oauth.initOAuthFlow(function (token, secret) {
270 | background_page.chromeExOAuthOnAuthorize(token, secret);
271 | background_page.chromeExOAuthRedirectStarted = false;
272 | chrome.tabs.getSelected(null, function (tab) {
273 | chrome.tabs.remove(tab.id);
274 | });
275 | });
276 | };
277 |
278 | /**
279 | * Sends an HTTP request. Convenience wrapper for XMLHttpRequest calls.
280 | * @param {String} method The HTTP method to use.
281 | * @param {String} url The URL to send the request to.
282 | * @param {Object} headers Optional request headers in key/value format.
283 | * @param {String} body Optional body content.
284 | * @param {Function} callback Function to call when the XMLHttpRequest's
285 | * ready state changes. See documentation for XMLHttpRequest's
286 | * onreadystatechange handler for more information.
287 | */
288 | ChromeExOAuth.sendRequest = function(method, url, headers, body, callback) {
289 | var xhr = new XMLHttpRequest();
290 | xhr.onreadystatechange = function(data) {
291 | callback(xhr, data);
292 | };
293 | xhr.open(method, url, true);
294 | if (headers) {
295 | for (var header in headers) {
296 | if (headers.hasOwnProperty(header)) {
297 | xhr.setRequestHeader(header, headers[header]);
298 | }
299 | }
300 | }
301 | xhr.send(body);
302 | };
303 |
304 | /**
305 | * Decodes a URL-encoded string into key/value pairs.
306 | * @param {String} encoded An URL-encoded string.
307 | * @return {Object} An object representing the decoded key/value pairs found
308 | * in the encoded string.
309 | */
310 | ChromeExOAuth.formDecode = function(encoded) {
311 | var params = encoded.split("&");
312 | var decoded = {};
313 | for (var i = 0, param; param = params[i]; i++) {
314 | var keyval = param.split("=");
315 | if (keyval.length == 2) {
316 | var key = ChromeExOAuth.fromRfc3986(keyval[0]);
317 | var val = ChromeExOAuth.fromRfc3986(keyval[1]);
318 | decoded[key] = val;
319 | }
320 | }
321 | return decoded;
322 | };
323 |
324 | /**
325 | * Returns the current window's querystring decoded into key/value pairs.
326 | * @return {Object} A object representing any key/value pairs found in the
327 | * current window's querystring.
328 | */
329 | ChromeExOAuth.getQueryStringParams = function() {
330 | var urlparts = window.location.href.split("?");
331 | if (urlparts.length >= 2) {
332 | var querystring = urlparts.slice(1).join("?");
333 | return ChromeExOAuth.formDecode(querystring);
334 | }
335 | return {};
336 | };
337 |
338 | /**
339 | * Binds a function call to a specific object. This function will also take
340 | * a variable number of additional arguments which will be prepended to the
341 | * arguments passed to the bound function when it is called.
342 | * @param {Function} func The function to bind.
343 | * @param {Object} obj The object to bind to the function's "this".
344 | * @return {Function} A closure that will call the bound function.
345 | */
346 | ChromeExOAuth.bind = function(func, obj) {
347 | var newargs = Array.prototype.slice.call(arguments).slice(2);
348 | return function() {
349 | var combinedargs = newargs.concat(Array.prototype.slice.call(arguments));
350 | func.apply(obj, combinedargs);
351 | };
352 | };
353 |
354 | /**
355 | * Encodes a value according to the RFC3986 specification.
356 | * @param {String} val The string to encode.
357 | */
358 | ChromeExOAuth.toRfc3986 = function(val){
359 | return encodeURIComponent(val)
360 | .replace(/\!/g, "%21")
361 | .replace(/\*/g, "%2A")
362 | .replace(/'/g, "%27")
363 | .replace(/\(/g, "%28")
364 | .replace(/\)/g, "%29");
365 | };
366 |
367 | /**
368 | * Decodes a string that has been encoded according to RFC3986.
369 | * @param {String} val The string to decode.
370 | */
371 | ChromeExOAuth.fromRfc3986 = function(val){
372 | var tmp = val
373 | .replace(/%21/g, "!")
374 | .replace(/%2A/g, "*")
375 | .replace(/%27/g, "'")
376 | .replace(/%28/g, "(")
377 | .replace(/%29/g, ")");
378 | return decodeURIComponent(tmp);
379 | };
380 |
381 | /**
382 | * Adds a key/value parameter to the supplied URL.
383 | * @param {String} url An URL which may or may not contain querystring values.
384 | * @param {String} key A key
385 | * @param {String} value A value
386 | * @return {String} The URL with URL-encoded versions of the key and value
387 | * appended, prefixing them with "&" or "?" as needed.
388 | */
389 | ChromeExOAuth.addURLParam = function(url, key, value) {
390 | var sep = (url.indexOf('?') >= 0) ? "&" : "?";
391 | return url + sep +
392 | ChromeExOAuth.toRfc3986(key) + "=" + ChromeExOAuth.toRfc3986(value);
393 | };
394 |
395 | /**
396 | * Stores an OAuth token for the configured scope.
397 | * @param {String} token The token to store.
398 | */
399 | ChromeExOAuth.prototype.setToken = function(token) {
400 | localStorage[this.key_token + encodeURI(this.oauth_scope)] = token;
401 | };
402 |
403 | /**
404 | * Retrieves any stored token for the configured scope.
405 | * @return {String} The stored token.
406 | */
407 | ChromeExOAuth.prototype.getToken = function() {
408 | return localStorage[this.key_token + encodeURI(this.oauth_scope)];
409 | };
410 |
411 | /**
412 | * Stores an OAuth token secret for the configured scope.
413 | * @param {String} secret The secret to store.
414 | */
415 | ChromeExOAuth.prototype.setTokenSecret = function(secret) {
416 | localStorage[this.key_token_secret + encodeURI(this.oauth_scope)] = secret;
417 | };
418 |
419 | /**
420 | * Retrieves any stored secret for the configured scope.
421 | * @return {String} The stored secret.
422 | */
423 | ChromeExOAuth.prototype.getTokenSecret = function() {
424 | return localStorage[this.key_token_secret + encodeURI(this.oauth_scope)];
425 | };
426 |
427 | /**
428 | * Starts an OAuth authorization flow for the current page. If a token exists,
429 | * no redirect is needed and the supplied callback is called immediately.
430 | * If this method detects that a redirect has finished, it grabs the
431 | * appropriate OAuth parameters from the URL and attempts to retrieve an
432 | * access token. If no token exists and no redirect has happened, then
433 | * an access token is requested and the page is ultimately redirected.
434 | * @param {Function} callback The function to call once the flow has finished.
435 | * This callback will be passed the following arguments:
436 | * token {String} The OAuth access token.
437 | * secret {String} The OAuth access token secret.
438 | */
439 | ChromeExOAuth.prototype.initOAuthFlow = function(callback) {
440 | if (!this.hasToken()) {
441 | var params = ChromeExOAuth.getQueryStringParams();
442 | if (params['chromeexoauthcallback'] == 'true') {
443 | var oauth_token = params['oauth_token'];
444 | var oauth_verifier = params['oauth_verifier'];
445 | this.getAccessToken(oauth_token, oauth_verifier, callback);
446 | } else {
447 | var request_params = {
448 | 'url_callback_param' : 'chromeexoauthcallback'
449 | };
450 | this.getRequestToken(function(url) {
451 | window.location.href = url;
452 | }, request_params);
453 | }
454 | } else {
455 | callback(this.getToken(), this.getTokenSecret());
456 | }
457 | };
458 |
459 | /**
460 | * Requests an OAuth request token.
461 | * @param {Function} callback Function to call once the authorize URL is
462 | * calculated. This callback will be passed the following arguments:
463 | * url {String} The URL the user must be redirected to in order to
464 | * approve the token.
465 | * @param {Object} opt_args Optional arguments. The following parameters
466 | * are accepted:
467 | * "url_callback" {String} The URL the OAuth provider will redirect to.
468 | * "url_callback_param" {String} A parameter to include in the callback
469 | * URL in order to indicate to this library that a redirect has
470 | * taken place.
471 | */
472 | ChromeExOAuth.prototype.getRequestToken = function(callback, opt_args) {
473 | if (typeof callback !== "function") {
474 | throw new Error("Specified callback must be a function.");
475 | }
476 | var url = opt_args && opt_args['url_callback'] ||
477 | window && window.top && window.top.location &&
478 | window.top.location.href;
479 |
480 | var url_param = opt_args && opt_args['url_callback_param'] ||
481 | "chromeexoauthcallback";
482 | var url_callback = ChromeExOAuth.addURLParam(url, url_param, "true");
483 |
484 | var result = OAuthSimple().sign({
485 | path : this.url_request_token,
486 | parameters: {
487 | "xoauth_displayname" : this.app_name,
488 | "scope" : this.oauth_scope,
489 | "oauth_callback" : url_callback
490 | },
491 | signatures: {
492 | consumer_key : this.consumer_key,
493 | shared_secret : this.consumer_secret
494 | }
495 | });
496 | var onToken = ChromeExOAuth.bind(this.onRequestToken, this, callback);
497 | ChromeExOAuth.sendRequest("GET", result.signed_url, null, null, onToken);
498 | };
499 |
500 | /**
501 | * Called when a request token has been returned. Stores the request token
502 | * secret for later use and sends the authorization url to the supplied
503 | * callback (for redirecting the user).
504 | * @param {Function} callback Function to call once the authorize URL is
505 | * calculated. This callback will be passed the following arguments:
506 | * url {String} The URL the user must be redirected to in order to
507 | * approve the token.
508 | * @param {XMLHttpRequest} xhr The XMLHttpRequest object used to fetch the
509 | * request token.
510 | */
511 | ChromeExOAuth.prototype.onRequestToken = function(callback, xhr) {
512 | if (xhr.readyState == 4) {
513 | if (xhr.status == 200) {
514 | var params = ChromeExOAuth.formDecode(xhr.responseText);
515 | var token = params['oauth_token'];
516 | this.setTokenSecret(params['oauth_token_secret']);
517 | var url = ChromeExOAuth.addURLParam(this.url_auth_token,
518 | "oauth_token", token);
519 | for (var key in this.auth_params) {
520 | if (this.auth_params.hasOwnProperty(key)) {
521 | url = ChromeExOAuth.addURLParam(url, key, this.auth_params[key]);
522 | }
523 | }
524 | callback(url);
525 | } else {
526 | throw new Error("Fetching request token failed. Status " + xhr.status);
527 | }
528 | }
529 | };
530 |
531 | /**
532 | * Requests an OAuth access token.
533 | * @param {String} oauth_token The OAuth request token.
534 | * @param {String} oauth_verifier The OAuth token verifier.
535 | * @param {Function} callback The function to call once the token is obtained.
536 | * This callback will be passed the following arguments:
537 | * token {String} The OAuth access token.
538 | * secret {String} The OAuth access token secret.
539 | */
540 | ChromeExOAuth.prototype.getAccessToken = function(oauth_token, oauth_verifier,
541 | callback) {
542 | if (typeof callback !== "function") {
543 | throw new Error("Specified callback must be a function.");
544 | }
545 | var bg = chrome.extension.getBackgroundPage();
546 | if (bg.chromeExOAuthRequestingAccess == false) {
547 | bg.chromeExOAuthRequestingAccess = true;
548 |
549 | var result = OAuthSimple().sign({
550 | path : this.url_access_token,
551 | parameters: {
552 | "oauth_token" : oauth_token,
553 | "oauth_verifier" : oauth_verifier
554 | },
555 | signatures: {
556 | consumer_key : this.consumer_key,
557 | shared_secret : this.consumer_secret,
558 | oauth_secret : this.getTokenSecret(this.oauth_scope)
559 | }
560 | });
561 |
562 | var onToken = ChromeExOAuth.bind(this.onAccessToken, this, callback);
563 | ChromeExOAuth.sendRequest("GET", result.signed_url, null, null, onToken);
564 | }
565 | };
566 |
567 | /**
568 | * Called when an access token has been returned. Stores the access token and
569 | * access token secret for later use and sends them to the supplied callback.
570 | * @param {Function} callback The function to call once the token is obtained.
571 | * This callback will be passed the following arguments:
572 | * token {String} The OAuth access token.
573 | * secret {String} The OAuth access token secret.
574 | * @param {XMLHttpRequest} xhr The XMLHttpRequest object used to fetch the
575 | * access token.
576 | */
577 | ChromeExOAuth.prototype.onAccessToken = function(callback, xhr) {
578 | if (xhr.readyState == 4) {
579 | var bg = chrome.extension.getBackgroundPage();
580 | if (xhr.status == 200) {
581 | var params = ChromeExOAuth.formDecode(xhr.responseText);
582 | var token = params["oauth_token"];
583 | var secret = params["oauth_token_secret"];
584 | this.setToken(token);
585 | this.setTokenSecret(secret);
586 | bg.chromeExOAuthRequestingAccess = false;
587 | callback(token, secret);
588 | } else {
589 | bg.chromeExOAuthRequestingAccess = false;
590 | throw new Error("Fetching access token failed with status " + xhr.status);
591 | }
592 | }
593 | };
594 |
595 |
--------------------------------------------------------------------------------
/javascripts/utils/chrome_ex_oauth_init.js:
--------------------------------------------------------------------------------
1 | window.onload = function() {
2 | ChromeExOAuth.initCallbackPage();
3 | };
4 |
--------------------------------------------------------------------------------
/javascripts/utils/chrome_ex_oauthsimple.js:
--------------------------------------------------------------------------------
1 | /* OAuthSimple
2 | * A simpler version of OAuth
3 | *
4 | * author: jr conlin
5 | * mail: src@anticipatr.com
6 | * copyright: unitedHeroes.net
7 | * version: 1.0
8 | * url: http://unitedHeroes.net/OAuthSimple
9 | *
10 | * Copyright (c) 2009, unitedHeroes.net
11 | * All rights reserved.
12 | *
13 | * Redistribution and use in source and binary forms, with or without
14 | * modification, are permitted provided that the following conditions are met:
15 | * * Redistributions of source code must retain the above copyright
16 | * notice, this list of conditions and the following disclaimer.
17 | * * Redistributions in binary form must reproduce the above copyright
18 | * notice, this list of conditions and the following disclaimer in the
19 | * documentation and/or other materials provided with the distribution.
20 | * * Neither the name of the unitedHeroes.net nor the
21 | * names of its contributors may be used to endorse or promote products
22 | * derived from this software without specific prior written permission.
23 | *
24 | * THIS SOFTWARE IS PROVIDED BY UNITEDHEROES.NET ''AS IS'' AND ANY
25 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
26 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
27 | * DISCLAIMED. IN NO EVENT SHALL UNITEDHEROES.NET BE LIABLE FOR ANY
28 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
29 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
31 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 | */
35 | var OAuthSimple;
36 |
37 | if (OAuthSimple === undefined)
38 | {
39 | /* Simple OAuth
40 | *
41 | * This class only builds the OAuth elements, it does not do the actual
42 | * transmission or reception of the tokens. It does not validate elements
43 | * of the token. It is for client use only.
44 | *
45 | * api_key is the API key, also known as the OAuth consumer key
46 | * shared_secret is the shared secret (duh).
47 | *
48 | * Both the api_key and shared_secret are generally provided by the site
49 | * offering OAuth services. You need to specify them at object creation
50 | * because nobody ing uses OAuth without that minimal set of
51 | * signatures.
52 | *
53 | * If you want to use the higher order security that comes from the
54 | * OAuth token (sorry, I don't provide the functions to fetch that because
55 | * sites aren't horribly consistent about how they offer that), you need to
56 | * pass those in either with .setTokensAndSecrets() or as an argument to the
57 | * .sign() or .getHeaderString() functions.
58 | *
59 | * Example:
60 |
61 | var oauthObject = OAuthSimple().sign({path:'http://example.com/rest/',
62 | parameters: 'foo=bar&gorp=banana',
63 | signatures:{
64 | api_key:'12345abcd',
65 | shared_secret:'xyz-5309'
66 | }});
67 | document.getElementById('someLink').href=oauthObject.signed_url;
68 |
69 | *
70 | * that will sign as a "GET" using "SHA1-MAC" the url. If you need more than
71 | * that, read on, McDuff.
72 | */
73 |
74 | /** OAuthSimple creator
75 | *
76 | * Create an instance of OAuthSimple
77 | *
78 | * @param api_key {string} The API Key (sometimes referred to as the consumer key) This value is usually supplied by the site you wish to use.
79 | * @param shared_secret (string) The shared secret. This value is also usually provided by the site you wish to use.
80 | */
81 | OAuthSimple = function (consumer_key,shared_secret)
82 | {
83 | /* if (api_key == undefined)
84 | throw("Missing argument: api_key (oauth_consumer_key) for OAuthSimple. This is usually provided by the hosting site.");
85 | if (shared_secret == undefined)
86 | throw("Missing argument: shared_secret (shared secret) for OAuthSimple. This is usually provided by the hosting site.");
87 | */ this._secrets={};
88 | this._parameters={};
89 |
90 | // General configuration options.
91 | if (consumer_key !== undefined) {
92 | this._secrets['consumer_key'] = consumer_key;
93 | }
94 | if (shared_secret !== undefined) {
95 | this._secrets['shared_secret'] = shared_secret;
96 | }
97 | this._default_signature_method= "HMAC-SHA1";
98 | this._action = "GET";
99 | this._nonce_chars = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
100 |
101 |
102 | this.reset = function() {
103 | this._parameters={};
104 | this._path=undefined;
105 | return this;
106 | };
107 |
108 | /** set the parameters either from a hash or a string
109 | *
110 | * @param {string,object} List of parameters for the call, this can either be a URI string (e.g. "foo=bar&gorp=banana" or an object/hash)
111 | */
112 | this.setParameters = function (parameters) {
113 | if (parameters === undefined) {
114 | parameters = {};
115 | }
116 | if (typeof(parameters) == 'string') {
117 | parameters=this._parseParameterString(parameters);
118 | }
119 | this._parameters = parameters;
120 | if (this._parameters['oauth_nonce'] === undefined) {
121 | this._getNonce();
122 | }
123 | if (this._parameters['oauth_timestamp'] === undefined) {
124 | this._getTimestamp();
125 | }
126 | if (this._parameters['oauth_method'] === undefined) {
127 | this.setSignatureMethod();
128 | }
129 | if (this._parameters['oauth_consumer_key'] === undefined) {
130 | this._getApiKey();
131 | }
132 | if(this._parameters['oauth_token'] === undefined) {
133 | this._getAccessToken();
134 | }
135 |
136 | return this;
137 | };
138 |
139 | /** convienence method for setParameters
140 | *
141 | * @param parameters {string,object} See .setParameters
142 | */
143 | this.setQueryString = function (parameters) {
144 | return this.setParameters(parameters);
145 | };
146 |
147 | /** Set the target URL (does not include the parameters)
148 | *
149 | * @param path {string} the fully qualified URI (excluding query arguments) (e.g "http://example.org/foo")
150 | */
151 | this.setURL = function (path) {
152 | if (path == '') {
153 | throw ('No path specified for OAuthSimple.setURL');
154 | }
155 | this._path = path;
156 | return this;
157 | };
158 |
159 | /** convienence method for setURL
160 | *
161 | * @param path {string} see .setURL
162 | */
163 | this.setPath = function(path){
164 | return this.setURL(path);
165 | };
166 |
167 | /** set the "action" for the url, (e.g. GET,POST, DELETE, etc.)
168 | *
169 | * @param action {string} HTTP Action word.
170 | */
171 | this.setAction = function(action) {
172 | if (action === undefined) {
173 | action="GET";
174 | }
175 | action = action.toUpperCase();
176 | if (action.match('[^A-Z]')) {
177 | throw ('Invalid action specified for OAuthSimple.setAction');
178 | }
179 | this._action = action;
180 | return this;
181 | };
182 |
183 | /** set the signatures (as well as validate the ones you have)
184 | *
185 | * @param signatures {object} object/hash of the token/signature pairs {api_key:, shared_secret:, oauth_token: oauth_secret:}
186 | */
187 | this.setTokensAndSecrets = function(signatures) {
188 | if (signatures)
189 | {
190 | for (var i in signatures) {
191 | this._secrets[i] = signatures[i];
192 | }
193 | }
194 | // Aliases
195 | if (this._secrets['api_key']) {
196 | this._secrets.consumer_key = this._secrets.api_key;
197 | }
198 | if (this._secrets['access_token']) {
199 | this._secrets.oauth_token = this._secrets.access_token;
200 | }
201 | if (this._secrets['access_secret']) {
202 | this._secrets.oauth_secret = this._secrets.access_secret;
203 | }
204 | // Gauntlet
205 | if (this._secrets.consumer_key === undefined) {
206 | throw('Missing required consumer_key in OAuthSimple.setTokensAndSecrets');
207 | }
208 | if (this._secrets.shared_secret === undefined) {
209 | throw('Missing required shared_secret in OAuthSimple.setTokensAndSecrets');
210 | }
211 | if ((this._secrets.oauth_token !== undefined) && (this._secrets.oauth_secret === undefined)) {
212 | throw('Missing oauth_secret for supplied oauth_token in OAuthSimple.setTokensAndSecrets');
213 | }
214 | return this;
215 | };
216 |
217 | /** set the signature method (currently only Plaintext or SHA-MAC1)
218 | *
219 | * @param method {string} Method of signing the transaction (only PLAINTEXT and SHA-MAC1 allowed for now)
220 | */
221 | this.setSignatureMethod = function(method) {
222 | if (method === undefined) {
223 | method = this._default_signature_method;
224 | }
225 | //TODO: accept things other than PlainText or SHA-MAC1
226 | if (method.toUpperCase().match(/(PLAINTEXT|HMAC-SHA1)/) === undefined) {
227 | throw ('Unknown signing method specified for OAuthSimple.setSignatureMethod');
228 | }
229 | this._parameters['oauth_signature_method']= method.toUpperCase();
230 | return this;
231 | };
232 |
233 | /** sign the request
234 | *
235 | * note: all arguments are optional, provided you've set them using the
236 | * other helper functions.
237 | *
238 | * @param args {object} hash of arguments for the call
239 | * {action:, path:, parameters:, method:, signatures:}
240 | * all arguments are optional.
241 | */
242 | this.sign = function (args) {
243 | if (args === undefined) {
244 | args = {};
245 | }
246 | // Set any given parameters
247 | if(args['action'] !== undefined) {
248 | this.setAction(args['action']);
249 | }
250 | if (args['path'] !== undefined) {
251 | this.setPath(args['path']);
252 | }
253 | if (args['method'] !== undefined) {
254 | this.setSignatureMethod(args['method']);
255 | }
256 | this.setTokensAndSecrets(args['signatures']);
257 | if (args['parameters'] !== undefined){
258 | this.setParameters(args['parameters']);
259 | }
260 | // check the parameters
261 | var normParams = this._normalizedParameters();
262 | this._parameters['oauth_signature']=this._generateSignature(normParams);
263 | return {
264 | parameters: this._parameters,
265 | signature: this._oauthEscape(this._parameters['oauth_signature']),
266 | signed_url: this._path + '?' + this._normalizedParameters(),
267 | header: this.getHeaderString()
268 | };
269 | };
270 |
271 | /** Return a formatted "header" string
272 | *
273 | * NOTE: This doesn't set the "Authorization: " prefix, which is required.
274 | * I don't set it because various set header functions prefer different
275 | * ways to do that.
276 | *
277 | * @param args {object} see .sign
278 | */
279 | this.getHeaderString = function(args) {
280 | if (this._parameters['oauth_signature'] === undefined) {
281 | this.sign(args);
282 | }
283 |
284 | var result = 'OAuth ';
285 | for (var pName in this._parameters)
286 | {
287 | if (!pName.match(/^oauth/)) {
288 | continue;
289 | }
290 | if ((this._parameters[pName]) instanceof Array)
291 | {
292 | var pLength = this._parameters[pName].length;
293 | for (var j=0;j>16)+(y>>16)+(l>>16);return(m<<16)|(l&0xFFFF);}function _r(n,c){return(n<>>(32-c));}function _c(x,l){x[l>>5]|=0x80<<(24-l%32);x[((l+64>>9)<<4)+15]=l;var w=[80],a=1732584193,b=-271733879,c=-1732584194,d=271733878,e=-1009589776;for(var i=0;i>5]|=(s.charCodeAt(i/8)&m)<<(32-_z-i%32);}return b;}function _h(k,d){var b=_b(k);if(b.length>16){b=_c(b,k.length*_z);}var p=[16],o=[16];for(var i=0;i<16;i++){p[i]=b[i]^0x36363636;o[i]=b[i]^0x5C5C5C5C;}var h=_c(p.concat(_b(d)),512+d.length*_z);return _c(o.concat(h),512+160);}function _n(b){var t="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",s='';for(var i=0;i>2]>>8*(3-i%4))&0xFF)<<16)|(((b[i+1>>2]>>8*(3-(i+1)%4))&0xFF)<<8)|((b[i+2>>2]>>8*(3-(i+2)%4))&0xFF);for(var j=0;j<4;j++){if(i*8+j*6>b.length*32){s+=_p;}else{s+=t.charAt((r>>6*(3-j))&0x3F);}}}return s;}function _x(k,d){return _n(_h(k,d));}return _x(k,d);
398 | }
399 |
400 |
401 | this._normalizedParameters = function() {
402 | var elements = new Array();
403 | var paramNames = [];
404 | var ra =0;
405 | for (var paramName in this._parameters)
406 | {
407 | if (ra++ > 1000) {
408 | throw('runaway 1');
409 | }
410 | paramNames.unshift(paramName);
411 | }
412 | paramNames = paramNames.sort();
413 | pLen = paramNames.length;
414 | for (var i=0;i 1000) {
427 | throw('runaway 1');
428 | }
429 | elements.push(this._oauthEscape(paramName) + '=' +
430 | this._oauthEscape(sorted[j]));
431 | }
432 | continue;
433 | }
434 | elements.push(this._oauthEscape(paramName) + '=' +
435 | this._oauthEscape(this._parameters[paramName]));
436 | }
437 | return elements.join('&');
438 | };
439 |
440 | this._generateSignature = function() {
441 |
442 | var secretKey = this._oauthEscape(this._secrets.shared_secret)+'&'+
443 | this._oauthEscape(this._secrets.oauth_secret);
444 | if (this._parameters['oauth_signature_method'] == 'PLAINTEXT')
445 | {
446 | return secretKey;
447 | }
448 | if (this._parameters['oauth_signature_method'] == 'HMAC-SHA1')
449 | {
450 | var sigString = this._oauthEscape(this._action)+'&'+this._oauthEscape(this._path)+'&'+this._oauthEscape(this._normalizedParameters());
451 | return this.b64_hmac_sha1(secretKey,sigString);
452 | }
453 | return null;
454 | };
455 |
456 | return this;
457 | };
458 | }
459 |
--------------------------------------------------------------------------------
/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "Microformats",
3 | "version": "0.4.13",
4 | "manifest_version": 2,
5 | "description": "Shows microformats on the page. Supports hCard, hCalendar, hReview, hReview-aggregate, hRecipe and geo.",
6 | "page_action": {
7 | "default_icon": "images/microformats-logo-19.png",
8 | "default_title" : "Click to view the microformats…",
9 | "default_popup": "popup.html"
10 | },
11 | "content_scripts": [ {
12 | "js": [
13 | "javascripts/utils/jquery.min.js",
14 | "javascripts/microformats/microformat.js",
15 | "javascripts/microformats/hreview-aggregate.js",
16 | "javascripts/microformats/hrecipe.js",
17 | "javascripts/microformats/microformats-shiv.min.js",
18 | "javascripts/microformats/microformats-coredefinition.min.js",
19 | "javascripts/microformats/microformats-hreviewdefinition.min.js",
20 | "javascripts/microformats/microformats-isodate.min.js",
21 | "javascripts/microformats/json2.min.js",
22 | "contentscript.js"
23 | ],
24 | "matches": [ "http://*/*", "https://*/*", "file://*/*" ]
25 | } ],
26 | "icons" : {
27 | "48" : "images/michromeformat-logo-48.png",
28 | "128" : "images/michromeformat-logo.png"
29 | },
30 | "permissions": [ "tabs", "http://*/*", "https://*/*" ],
31 | "background": {
32 | "page": "background.html",
33 | "persistent": false
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
41 |
42 |
--------------------------------------------------------------------------------
/stylesheets/popup.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'PictosRegular';
3 | src: url('/fonts/pictos-web-webfont.eot');
4 | src: url('/fonts/pictos-web-webfont.woff') format('woff'),
5 | url('/fonts/pictos-web-webfont.ttf') format('truetype'),
6 | url('/fonts/pictos-web-webfont.svg#PictosRegular') format('svg');
7 | font-weight: normal;
8 | font-style: normal;
9 | }
10 |
11 | body {
12 | margin: 15px 10px;
13 | padding: 0;
14 | width: 420px;
15 | font-family: 'Open Sans', sans-serif;
16 | color: #3b3b3b;
17 | overflow-x: hidden;
18 | }
19 | #header {
20 | overflow: hidden;
21 | }
22 | #icon {
23 | display: block;
24 | float: left;
25 | }
26 | #header h1 {
27 | margin-left: 51px;
28 | margin-top: 16px;
29 | font-size: 17px;
30 | color: rgb(0, 141, 43);
31 | }
32 | .loggedout .login {
33 | display: none;
34 | }
35 | .loggedin .logout {
36 | display: none;
37 | }
38 | ul {
39 | list-style-type: none;
40 | padding: 0;
41 | }
42 | h1 {
43 | font-size: 20px;
44 | font-weight: 700;
45 | margin: 0;
46 | }
47 | .hcard, .hcalendar, .hreview, .hreviewagg, .hrecipe, .geo {
48 | padding: 10px 0 5px 10px;
49 | overflow: hidden;
50 | border-bottom: 1px solid #ccc;
51 | }
52 | .hcard h1 a, .hcalendar h1 a {
53 | float: right;
54 | }
55 | .photo {
56 | height: 50px;
57 | display: block;
58 | }
59 | .vevent:hover, .vcard:hover {
60 | cursor: pointer;
61 | }
62 | .details ul li {
63 | font-size: 15px;
64 | font-weight: 300;
65 | margin-bottom: 7px;
66 | }
67 | .details {
68 | overflow: hidden;
69 | }
70 | .details h1 {
71 | white-space: nowrap;
72 | width: 100%;
73 | overflow: hidden;
74 | text-overflow: ellipsis;
75 | margin-bottom: 5px;
76 | }
77 | p {
78 | font-size: 12px;
79 | }
80 | .static-map {
81 | display: block;
82 | margin-top: 7px;
83 | border: 1px solid #888;
84 | }
85 | .googleconnection, .googleconnection a {
86 | color: #999;
87 | font-size: 12px;
88 | overflow: hidden;
89 | }
90 | .googleconnection div {
91 | float: right;
92 | background: #fff url(../images/googleIcon.png) no-repeat 0 0;
93 | padding-left: 20px;
94 | height: 16px;
95 | }
96 | .submitvevent, .submithcard {
97 | margin-right: 10px;
98 | }
99 | .webfont {
100 | font: normal 28px Pictos, 'PictosRegular';
101 | }
102 | .download {
103 | float: right;
104 | }
105 | .download a {
106 | display: inline-block;
107 | text-decoration: none;
108 | color: #676767;
109 | }
110 | .download a span {
111 | height: 28px;
112 | line-height: 28px;
113 | display: inline-block;
114 | }
115 | .download a:hover {
116 | -webkit-mask-image: -webkit-gradient(linear, left top, left bottom, from(rgba(0,0,0,0.5)), color-stop(100%, rgba(0,0,0,1)), to(rgba(0,0,0,1)));
117 | }
--------------------------------------------------------------------------------