﻿// Copyright 2009 Google Inc.  All Rights Reserved.

/**
 * @fileoverview Library of methods responsible for feed pull and manipiulation.
 *
 * @author Michal Drewniak
 */

/**
 * See if the global namespace object 'gweb' exists. If it doesn't, create it.
 */
var gweb = gweb || {};

/**
 * See if the object gweb.feed exists. If it doesn't create it.
 */
gweb.feed = gweb.feed || {};


/**
 * Feed method called by a user. Method calls appropriate feed pull
 * methods based on the passed arguments.
 * @param {Function} fn Callback function to which we're sending parsed data.
 * @param {string} url Url of the feed.
 * @param {number} numEntries Number of entries to pull from the feed.
 * @param {boolean} cached Use cache flag. When set to false, will pull data
 *     without using Feed API and thus will not use caching. When set to true,
 *     will use Feed API and thus cache the results.
 * @param {Array} args Additional arguments passed to the callback function.
 *
 * @this gweb.feed
 */
gweb.feed.loadSpreadsheet = function(fn, url, numEntries, cached, args) {
  if (!fn || !url) return;

  // Parse arguments. Account for possible optional arguments.
  if (numEntries === true || numEntries === false) {
    var args = cached;
    var cached = numEntries;
    var numEntries = 10000;
  } else if (typeof numEntries == 'number') {
    var args = cached;
    var numEntries = numEntries || 10000;
  } else {
    var args = numEntries;
    var numEntries = 10000;
  }

  // Default to cached version of the feed
  if (cached !== true && cached !== false) {
    var cached = true;
  }

  if (cached) {
    this.loadSpreadsheetCached_(fn, url, numEntries, args);
  } else {
    this.loadSpreadsheetNotCached_(fn, url, numEntries, args);
  }
};


/**
 * Extracts spreadsheet's key.
 * @param {string} url Url of the spreadsheet.
 * @return {string} Spreadsheet key.
 * @private
 */
gweb.feed.extractSpreadsheetKey_ = function(url) {
  return url.replace(/.*list\/([^\/]*?\/[^\/]*?)\/.*/g, '$1');
};


/**
 * Converts json string into javascript object.
 * @param {string} jsonString JSON string representing an object.
 * @return {Object} Decoded json string.
 * @private
 */
gweb.feed.jsonDecode_ = function(jsonString) {
  if (!jsonString || jsonString.$t === '' || jsonString === '') return {};
  var jsonString = jsonString.$t || jsonString;

  // Change the object delimeter to `|`
  jsonString = jsonString.replace(/,\s(\w+:)\s/g, '`|`$1');
  jsonString = jsonString.split('`|`');

  var jsObj = {};
  for (var i = 0, size = jsonString.length; i < size; i++) {
    jsonString[i] = jsonString[i].replace(':', '`|`');
    var item = jsonString[i].split('`|`');
    jsObj[item[0]] = item[1].replace(/^\s*|\s*$/, '');  // Trim spaces
  }
  return jsObj;
}


/**
 * Loads spreadsheet from the specified url and passes parsed data
 * into the callback function fn. Uses Feed API which automatically caches.
 * @param {Function} callback Callback function.
 * @param {string} url Url of the feed.
 * @param {number} numEntries Number of entries to pull from the feed.
 * @param {Array} args Additional arguments passed to the callback function.
 * @private
 */
gweb.feed.loadSpreadsheetCached_ = function(callback, url, numEntries, args) {
  if (!google.feeds) return;

  var feed = new google.feeds.Feed(url);

  feed.setNumEntries(numEntries);
  feed.load(parseSpreadsheetFeed_);

  /**
   * Parses json feed returned by Feed API.
   * @param {Object} data Spreadsheet json object.
   * @private
   */
  function parseSpreadsheetFeed_(data) {
    var parsedData = [];

    data = data.feed.entry || data.feed.entries;
    var size = data.length;

    // Limit the size if necessary
    if (size <= numEntries) {
      numEntries = size;
    }

    for (var i = 0; i < numEntries; i++) {
      parsedData[i] = gweb.feed.jsonDecode_(data[i].content);
      parsedData[i]['rowTitle'] = data[i].title; // First column
    }
    callback(parsedData, args);
  }
};


/**
 * Loads spreadsheet from the specified url and passes parsed data
 * into the callback function fn. Doesn't use Feed API/caching.
 * @param {Function} callback Callback function.
 * @param {string} url Url of the feed.
 * @param {number} numEntries Number of entries to pull from the feed.
 * @param {Array} args Additional arguments passed to the callback function.
 * @this gweb.feed
 * @private
 */
gweb.feed.loadSpreadsheetNotCached_ = function(callback,
                                               url,
                                               numEntries,
                                               args) {
  var sheetId = this.extractSpreadsheetKey_(url);
  var url = url.replace('basic',
      'values?alt=json-in-script&callback=gweb.feed.parseSpreadsheetFeed_');

  // Callbacks object is required to keep track of multiple feeds.
  // This assures appropriate callback function is called when multiple
  // feeds are being pulled.
  this.callbacks = this.callbacks || {};
  this.callbacks[sheetId] = [callback, numEntries, args];
  var script = document.createElement('script');
  script.type = 'text/javascript';
  script.src = unescape(url);

  if (document.body) {
    document.body.appendChild(script);
  } else {
    document.write('<script type="text/javascript"' +
                   'src="' + url + '"></script>');
  }
};


/**
 * Parses json feed returned from the spreadsheet.
 * @param {Object} data Spreadsheet json object.
 * @private
 */
gweb.feed.parseSpreadsheetFeed_ = function(data) {
  var FUNCT = 0;
  var NUM = 1;
  var ARGS = 2;
  var parsedData = [];
  var sheetId = gweb.feed.extractSpreadsheetKey_(data.feed.link[2].href);

  data = data.feed.entry || [];
  var size = data.length;

  // Limit the size if applicable.
  if (gweb.feed.callbacks[sheetId][NUM] < size) {
    size = gweb.feed.callbacks[sheetId][NUM];
  }

  // Get optional arguments
  var args = gweb.feed.callbacks[sheetId][ARGS];

  for (var i = 0; i < size; i++) {
    parsedData[i] = gweb.feed.jsonDecode_(data[i].content);
    parsedData[i]['rowTitle'] = data[i].title.$t; // First column
  }

  gweb.feed.callbacks[sheetId][FUNCT](parsedData, args);
};
