3 * Copyright (c) 2010 The Chromium Authors. All rights reserved. Use of this
4 * source code is governed by a BSD-style license that can be found in the
7 * Author: Eric Bidelman <ericbidelman@chromium.org>
11 <title>Your Google Documents List</title>
12 <script type="text/javascript" src="js/jquery-1.4.1.min.js"></script>
13 <style type="text/css">
15 font: 12px 'Myriad Pro', 'Tw Cen MT', Arial, Verdana, sans-serif;
29 vertical-align: middle;
32 text-overflow: ellipsis;
43 background-color: #fffccc;
47 text-decoration: none;
51 text-decoration: underline;
55 background-color: #000033;
64 background-color: red;
69 #new_doc_container input[type='text'],textarea {
76 [contenteditable]:hover {
77 outline: 1px dotted #666;
84 background: no-repeat url(data:image/png;base64,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) !important;
87 background: no-repeat url(data:image/png;base64,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) !important;
93 <div style="height:15px;">
94 <div style="float:left;">
95 <a href="javascript:void(0);" onclick="gdocs.refreshDocs();return false;">Refresh list</a>,
96 <a href="javascript:void(0);" onclick="$('#new_doc_container').toggle();return false;">New Document</a>
98 <div id="butter">Fetching your docs</div>
100 <div id="new_doc_container">
101 Create a: <select id="doc_type">
102 <option value="document">document</option>
103 <option value="presentation">presentation</option>
104 <option value="spreadsheet">spreadsheet</option>
106 <input type="text" id="doc_title" placeholder="Enter a title"><br>
107 <textarea id="doc_content" placeholder="Enter document content"></textarea>
108 Star it? <input type="checkbox" id="doc_starred">
109 <button onclick="gdocs.createDoc();" style="float:right;">Create new doc</button>
111 <div id="output"></div>
113 <script type="text/javascript">
114 // Protected namespaces.
118 var bgPage = chrome.extension.getBackgroundPage();
119 var pollIntervalMax = 1000 * 60 * 60; // 1 hour
120 var requestFailureCount = 0; // used for exponential backoff
121 var requestTimeout = 1000 * 2; // 5 seconds
123 var DEFAULT_MIMETYPES = {
124 'atom': 'application/atom+xml',
125 'document': 'text/plain',
126 'spreadsheet': 'text/csv',
127 'presentation': 'text/plain',
128 'pdf': 'application/pdf'
131 // Persistent click handler for star icons.
132 $('#doc_type').change(function() {
133 if ($(this).val() === 'presentation') {
134 $('#doc_content').attr('disabled', 'true')
135 .attr('placeholder', 'N/A for presentations');
137 $('#doc_content').removeAttr('disabled')
138 .attr('placeholder', 'Enter document content');
143 // Persistent click handler for changing the title of a document.
144 $('[contenteditable="true"]').live('blur', function(index) {
145 var index = $(this).parent().parent().attr('data-index');
147 // Only make the XHR if the user chose a new title.
148 if ($(this).text() != bgPage.docs[index].title) {
149 bgPage.docs[index].title = $(this).text();
150 gdocs.updateDoc(bgPage.docs[index]);
154 // Persistent click handler for star icons.
155 $('.star').live('click', function() {
156 $(this).toggleClass('selected');
158 var index = $(this).parent().attr('data-index');
159 bgPage.docs[index].starred = $(this).hasClass('selected');
160 gdocs.updateDoc(bgPage.docs[index]);
164 * Class to compartmentalize properties of a Google document.
165 * @param {Object} entry A JSON representation of a DocList atom entry.
168 gdocs.GoogleDoc = function(entry) {
170 this.title = entry.title.$t;
171 this.resourceId = entry.gd$resourceId.$t;
172 this.type = gdocs.getCategory(
173 entry.category, 'http://schemas.google.com/g/2005#kind');
174 this.starred = gdocs.getCategory(
175 entry.category, 'http://schemas.google.com/g/2005/labels',
176 'http://schemas.google.com/g/2005/labels#starred') ? true : false;
178 'alternate': gdocs.getLink(entry.link, 'alternate').href
180 this.contentSrc = entry.content.src;
184 * Sets up a future poll for the user's document list.
186 util.scheduleRequest = function() {
187 var exponent = Math.pow(2, requestFailureCount);
188 var delay = Math.min(bgPage.pollIntervalMin * exponent,
190 delay = Math.round(delay);
192 if (bgPage.oauth.hasToken()) {
193 var req = bgPage.window.setTimeout(function() {
194 gdocs.getDocumentList();
195 util.scheduleRequest();
197 bgPage.requests.push(req);
202 * Urlencodes a JSON object of key/value query parameters.
203 * @param {Object} parameters Key value pairs representing URL parameters.
204 * @return {string} query parameters concatenated together.
206 util.stringify = function(parameters) {
208 for(var p in parameters) {
209 params.push(encodeURIComponent(p) + '=' +
210 encodeURIComponent(parameters[p]));
212 return params.join('&');
216 * Creates a JSON object of key/value pairs
217 * @param {string} paramStr A string of Url query parmeters.
218 * For example: max-results=5&startindex=2&showfolders=true
219 * @return {Object} The query parameters as key/value pairs.
221 util.unstringify = function(paramStr) {
222 var parts = paramStr.split('&');
225 for (var i = 0, pair; pair = parts[i]; ++i) {
226 var param = pair.split('=');
227 params[decodeURIComponent(param[0])] = decodeURIComponent(param[1]);
233 * Utility for displaying a message to the user.
234 * @param {string} msg The message.
236 util.displayMsg = function(msg) {
237 $('#butter').removeClass('error').text(msg).show();
241 * Utility for removing any messages currently showing to the user.
243 util.hideMsg = function() {
244 $('#butter').fadeOut(1500);
248 * Utility for displaying an error to the user.
249 * @param {string} msg The message.
251 util.displayError = function(msg) {
252 util.displayMsg(msg);
253 $('#butter').addClass('error');
257 * Returns the correct atom link corresponding to the 'rel' value passed in.
258 * @param {Array<Object>} links A list of atom link objects.
259 * @param {string} rel The rel value of the link to return. For example: 'next'.
260 * @return {string|null} The appropriate link for the 'rel' passed in, or null
261 * if one is not found.
263 gdocs.getLink = function(links, rel) {
264 for (var i = 0, link; link = links[i]; ++i) {
265 if (link.rel === rel) {
273 * Returns the correct atom category corresponding to the scheme/term passed in.
274 * @param {Array<Object>} categories A list of atom category objects.
275 * @param {string} scheme The category's scheme to look up.
276 * @param {opt_term?} An optional term value for the category to look up.
277 * @return {string|null} The appropriate category, or null if one is not found.
279 gdocs.getCategory = function(categories, scheme, opt_term) {
280 for (var i = 0, cat; cat = categories[i]; ++i) {
282 if (cat.scheme === scheme && opt_term === cat.term) {
285 } else if (cat.scheme === scheme) {
293 * A generic error handler for failed XHR requests.
294 * @param {XMLHttpRequest} xhr The xhr request that failed.
295 * @param {string} textStatus The server's returned status.
297 gdocs.handleError = function(xhr, textStatus) {
298 util.displayError('Failed to fetch docs. Please try again.');
299 ++requestFailureCount;
303 * A helper for constructing the raw Atom xml send in the body of an HTTP post.
304 * @param {XMLHttpRequest} xhr The xhr request that failed.
305 * @param {string} docTitle A title for the document.
306 * @param {string} docType The type of document to create.
307 * (eg. 'document', 'spreadsheet', etc.)
308 * @param {boolean?} opt_starred Whether the document should be starred.
309 * @return {string} The Atom xml as a string.
311 gdocs.constructAtomXml_ = function(docTitle, docType, opt_starred) {
312 var starred = opt_starred || null;
314 var starCat = ['<category scheme="http://schemas.google.com/g/2005/labels" ',
315 'term="http://schemas.google.com/g/2005/labels#starred" ',
316 'label="starred"/>'].join('');
318 var atom = ["<?xml version='1.0' encoding='UTF-8'?>",
319 '<entry xmlns="http://www.w3.org/2005/Atom">',
320 '<category scheme="http://schemas.google.com/g/2005#kind"',
321 ' term="http://schemas.google.com/docs/2007#', docType, '"/>',
322 starred ? starCat : '',
323 '<title>', docTitle, '</title>',
324 '</entry>'].join('');
329 * A helper for constructing the body of a mime-mutlipart HTTP request.
330 * @param {string} title A title for the new document.
331 * @param {string} docType The type of document to create.
332 * (eg. 'document', 'spreadsheet', etc.)
333 * @param {string} body The body of the HTTP request.
334 * @param {string} contentType The Content-Type of the (non-Atom) portion of the
336 * @param {boolean?} opt_starred Whether the document should be starred.
337 * @return {string} The Atom xml as a string.
339 gdocs.constructContentBody_ = function(title, docType, body, contentType,
341 var body = ['--END_OF_PART\r\n',
342 'Content-Type: application/atom+xml;\r\n\r\n',
343 gdocs.constructAtomXml_(title, docType, opt_starred), '\r\n',
345 'Content-Type: ', contentType, '\r\n\r\n',
347 '--END_OF_PART--\r\n'].join('');
352 * Creates a new document in Google Docs.
354 gdocs.createDoc = function() {
355 var title = $.trim($('#doc_title').val());
357 alert('Please provide a title');
360 var content = $('#doc_content').val();
361 var starred = $('#doc_starred').is(':checked');
362 var docType = $('#doc_type').val();
364 util.displayMsg('Creating doc...');
366 var handleSuccess = function(resp, xhr) {
367 bgPage.docs.splice(0, 0, new gdocs.GoogleDoc(JSON.parse(resp).entry));
369 gdocs.renderDocList();
370 bgPage.setIcon({'text': bgPage.docs.length.toString()});
372 $('#new_doc_container').hide();
373 $('#doc_title').val('');
374 $('#doc_content').val('');
375 util.displayMsg('Document created!');
378 requestFailureCount = 0;
384 'GData-Version': '3.0',
385 'Content-Type': 'multipart/related; boundary=END_OF_PART',
387 'parameters': {'alt': 'json'},
388 'body': gdocs.constructContentBody_(title, docType, content,
389 DEFAULT_MIMETYPES[docType], starred)
392 // Presentation can only be created from binary content. Instead, create a
393 // blank presentation.
394 if (docType === 'presentation') {
395 params['headers']['Content-Type'] = DEFAULT_MIMETYPES['atom'];
396 params['body'] = gdocs.constructAtomXml_(title, docType, starred);
399 bgPage.oauth.sendSignedRequest(bgPage.DOCLIST_FEED, handleSuccess, params);
403 * Updates a document's metadata (title, starred, etc.).
404 * @param {gdocs.GoogleDoc} googleDocObj An object containing the document to
407 gdocs.updateDoc = function(googleDocObj) {
408 var handleSuccess = function(resp) {
409 util.displayMsg('Updated!');
411 requestFailureCount = 0;
417 'GData-Version': '3.0',
418 'Content-Type': 'application/atom+xml',
421 'body': gdocs.constructAtomXml_(googleDocObj.title, googleDocObj.type,
422 googleDocObj.starred)
425 var url = bgPage.DOCLIST_FEED + googleDocObj.resourceId;
426 bgPage.oauth.sendSignedRequest(url, handleSuccess, params);
430 * Deletes a document from the user's document list.
431 * @param {integer} index An index intro the background page's docs array.
433 gdocs.deleteDoc = function(index) {
434 var handleSuccess = function(resp, xhr) {
435 util.displayMsg('Document trashed!');
437 requestFailureCount = 0;
438 bgPage.docs.splice(index, 1);
439 bgPage.setIcon({'text': bgPage.docs.length.toString()});
445 'GData-Version': '3.0',
450 $('#output li').eq(index).fadeOut('slow');
452 bgPage.oauth.sendSignedRequest(
453 bgPage.DOCLIST_FEED + bgPage.docs[index].resourceId,
454 handleSuccess, params);
458 * Callback for processing the JSON feed returned by the DocList API.
459 * @param {string} response The server's response.
460 * @param {XMLHttpRequest} xhr The xhr request that was made.
462 gdocs.processDocListResults = function(response, xhr) {
463 if (xhr.status != 200) {
464 gdocs.handleError(xhr, response);
467 requestFailureCount = 0;
470 var data = JSON.parse(response);
472 for (var i = 0, entry; entry = data.feed.entry[i]; ++i) {
473 bgPage.docs.push(new gdocs.GoogleDoc(entry));
476 var nextLink = gdocs.getLink(data.feed.link, 'next');
478 gdocs.getDocumentList(nextLink.href); // Fetch next page of results.
480 gdocs.renderDocList();
485 * Presents the in-memory documents that were fetched from the server as HTML.
487 gdocs.renderDocList = function() {
490 // Construct the iframe's HTML.
492 for (var i = 0, doc; doc = bgPage.docs[i]; ++i) {
493 // If we have an arbitrary file, use generic file icon.
494 var type = doc.type.label;
495 if (doc.type.term == 'http://schemas.google.com/docs/2007#file') {
499 var starred = doc.starred ? ' selected' : '';
501 '<li data-index="', i , '"><div class="star', starred, '"></div>',
502 '<div><img src="img/icons/', type, '.gif">',
503 '<span contenteditable="true" class="doc_title"></span></div>',
504 '<span>[<a href="', doc.link['alternate'],
505 '" target="_new">view</a> | <a href="javascript:void(0);" ',
506 'onclick="gdocs.deleteDoc(',i,
507 ');return false;">delete</a>]','</span></li>');
509 $('#output').html('<ul>' + html.join('') + '</ul>');
511 // Set each span's innerText to be the doc title. We're filling this after
512 // the html has been rendered to the page prevent XSS attacks when using
514 $('#output li span.doc_title').each(function(i, ul) {
515 $(ul).text(bgPage.docs[i].title);
518 bgPage.setIcon({'text': bgPage.docs.length.toString()});
522 * Fetches the user's document list.
523 * @param {string?} opt_url A url to query the doclist API with. If omitted,
524 * the main doclist feed uri is used.
526 gdocs.getDocumentList = function(opt_url) {
527 var url = opt_url || null;
531 'GData-Version': '3.0'
536 util.displayMsg('Fetching your docs');
537 bgPage.setIcon({'text': '...'});
539 bgPage.docs = []; // Clear document list. We're doing a refresh.
541 url = bgPage.DOCLIST_FEED;
542 params['parameters'] = {
544 'showfolders': 'true'
547 util.displayMsg($('#butter').text() + '.');
549 var parts = url.split('?');
550 if (parts.length > 1) {
551 url = parts[0]; // Extract base URI. Params are passed in separately.
552 params['parameters'] = util.unstringify(parts[1]);
556 bgPage.oauth.sendSignedRequest(url, gdocs.processDocListResults, params);
560 * Refreshes the user's document list.
562 gdocs.refreshDocs = function() {
563 bgPage.clearPendingRequests();
564 gdocs.getDocumentList();
565 util.scheduleRequest();
569 bgPage.oauth.authorize(function() {
570 if (!bgPage.docs.length) {
571 gdocs.getDocumentList();
573 gdocs.renderDocList();
575 util.scheduleRequest();