You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@shindig.apache.org by et...@apache.org on 2008/01/08 23:34:06 UTC

svn commit: r610205 - in /incubator/shindig/trunk/features: ./ analytics/ core/ ifpc/ setprefs/

Author: etnu
Date: Tue Jan  8 14:34:05 2008
New Revision: 610205

URL: http://svn.apache.org/viewvc?rev=610205&view=rev
Log:
Adding missing features directory from revision 610196.


Added:
    incubator/shindig/trunk/features/
    incubator/shindig/trunk/features/analytics/
    incubator/shindig/trunk/features/analytics/feature.xml
    incubator/shindig/trunk/features/core/
    incubator/shindig/trunk/features/core/core.js
    incubator/shindig/trunk/features/core/feature.xml
    incubator/shindig/trunk/features/core/io.js
    incubator/shindig/trunk/features/core/legacy.js
    incubator/shindig/trunk/features/core/prefs.js
    incubator/shindig/trunk/features/core/util.js
    incubator/shindig/trunk/features/ifpc/
    incubator/shindig/trunk/features/ifpc/feature.xml
    incubator/shindig/trunk/features/setprefs/
    incubator/shindig/trunk/features/setprefs/feature.xml
    incubator/shindig/trunk/features/setprefs/setprefs.js

Added: incubator/shindig/trunk/features/analytics/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/analytics/feature.xml?rev=610205&view=auto
==============================================================================
--- incubator/shindig/trunk/features/analytics/feature.xml (added)
+++ incubator/shindig/trunk/features/analytics/feature.xml Tue Jan  8 14:34:05 2008
@@ -0,0 +1,25 @@
+<?xml version="1.0"?>
+<!--
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+
+  This feature enables the google-proprietary analytics feature by linking
+  to the source externally. This is a stop gap solution until we can completely
+  externalize this functionality within the gadget spec.
+-->
+<feature>
+  <name>analytics</name>
+  <gadget>
+    <script src="http://www.google.com/ig/lib/liburchin.js"/>
+    <script src="http://www.google.com/ig/lib/libanalytics.js"/>
+  </gadget>
+</feature>

Added: incubator/shindig/trunk/features/core/core.js
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/core/core.js?rev=610205&view=auto
==============================================================================
--- incubator/shindig/trunk/features/core/core.js (added)
+++ incubator/shindig/trunk/features/core/core.js Tue Jan  8 14:34:05 2008
@@ -0,0 +1 @@
+var gadgets = {};

Added: incubator/shindig/trunk/features/core/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/core/feature.xml?rev=610205&view=auto
==============================================================================
--- incubator/shindig/trunk/features/core/feature.xml (added)
+++ incubator/shindig/trunk/features/core/feature.xml Tue Jan  8 14:34:05 2008
@@ -0,0 +1,30 @@
+<?xml version="1.0"?>
+<!--
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<feature>
+  <name>core</name>
+  <gadget>
+    <script src="core.js"/>
+    <script src="util.js"/>
+    <script src="prefs.js"/>
+    <script src="io.js"/>
+    <script><![CDATA[
+      gadgets.io.init({
+        proxyUrl: "http://www.gmodules.com/ig/proxy?url=%url%",
+        jsonProxyUrl: "/gadgets/proxy?url=%url%&output=js"
+      });
+    ]]></script>
+    <script src="legacy.js"/>
+  </gadget>
+</feature>

Added: incubator/shindig/trunk/features/core/io.js
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/core/io.js?rev=610205&view=auto
==============================================================================
--- incubator/shindig/trunk/features/core/io.js (added)
+++ incubator/shindig/trunk/features/core/io.js Tue Jan  8 14:34:05 2008
@@ -0,0 +1,169 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var gadgets = gadgets || {};
+
+/**
+ * Provides remote content retrieval facilities. Available to every gadget.
+ */
+gadgets.io = function() {
+  /**
+   * Holds configuration-related data such as proxy urls.
+   */
+  var config = {};
+
+  /**
+   * Internal facility to create an xhr request.
+   */
+  function makeXhr() {
+    if (window.XMLHttpRequest) {
+      return new XMLHttpRequest();
+    } else if (window.ActiveXObject) {
+      var x = new ActiveXObject("Msxml2.XMLHTTP");
+      if (!x) {
+        x = new ActiveXObject("Microsoft.XMLHTTP");
+      }
+      return x;
+    }
+  }
+
+  var UNPARSEABLE_CRUFT = "throw 1; < don't be evil' >";
+
+  /**
+   * Handles XHR callback processing.
+   *
+   * @param {String} url
+   * @param {Function} callback
+   * @param {Object} params
+   * @param {Object} xobj
+   */
+  function processResponse(url, callback, params, xobj) {
+    if (xobj.readyState !== 4) {
+      return;
+    }
+    var txt = xobj.responseText;
+    // remove unparseable cruft.
+    // TODO: really remove this by eliminating it. It's not any real security
+    //    to begin with, and we can solve this problem by using post requests
+    //    and / or passing the url in the http headers.
+    txt = txt.substr(UNPARSEABLE_CRUFT.length);
+    // TODO: safe JSON parser.
+    var data = eval("(" + txt + ")");
+    data = data[url];
+    var resp = {
+     text: data.body,
+     errors: []
+    };
+    switch (params.contentType) {
+      case "json":
+        // TODO: safe JSON parser.
+        resp.data = eval("(" + resp.text + ")");
+        break;
+     case "dom":
+      var dom;
+      if (window.ActiveXObject) {
+        dom = new ActiveXObject("Microsoft.XMLDOM");
+        dom.async = false;
+        dom.validateOnParse = false;
+        dom.resolveExternals = false;
+        dom.loadXML(resp.text);
+      } else {
+        var parser = new DOMParser();
+        dom = parser.parseFromString(resp.text, "text/xml");
+      }
+      resp.data = dom;
+      break;
+    default:
+      resp.data = resp.text;
+      break;
+   }
+   callback(resp);
+  }
+
+  /**
+   * Retrieves the content at the specified url.
+   *
+   * @param {String} url The url to fetch.
+   * @param {Function} callback Invoked when the request completes. The
+   *     response object will be passed in as a parameter.
+   * @param {Object} opt_params Optional parameters. May be modified.
+   *
+   * <pre>
+   * gadgets.IO.makeRequest(url, fn, {type: gadgets.IO.ContentType.JSON});
+   * </pre>
+   */
+  function makeRequest(url, callback, opt_params) {
+    var xhr = makeXhr();
+    var params = opt_params || {};
+    var newUrl = config.jsonProxyUrl.replace("%url%", encodeURIComponent(url));
+    xhr.open(params.postData ? "POST" : "GET", newUrl, true);
+    if (callback) {
+      xhr.onreadystatechange = gadgets.util.makeClosure(null, processResponse,
+                                                        url,
+                                                        callback,
+                                                        params,
+                                                        xhr);
+    }
+    xhr.send(params.postData);
+  }
+
+  /**
+   * Converts an input object into a url encoded data string (key=value&...)
+   *
+   * @param {Object} fields The post fields you wish to encode
+   * @return {String} The processed post data. This will include a trialing
+   *    ampersand (&).
+   */
+  function encodeValues(fields) {
+    var buf = [];
+    for (var i in fields) {
+      buf.push(encodeURIComponent(i));
+      buf.push("=");
+      buf.push(encodeURIComponent(fields[i]));
+      buf.push("&");
+    }
+    return buf.join("");
+  }
+
+  /**
+   * @param {String} url The url to get the proxy url for.
+   * @return {String} The proxied version of the url.
+   */
+  function getProxyUrl(url) {
+    return config.proxyUrl.replace("%url%", encodeURIComponent(url));
+  }
+
+  /**
+   * Initializes fetchers
+   * @param {Object} configuration Configuration settings.
+   *     Required:
+   *       - proxyUrl: The url for content proxy requests. Include %url%
+   *           as a placeholder for the actual url.
+   *       - jsonProxyUrl: The url for dynamic proxy requests. Include %url%
+   *           as a placeholder for the actual url.
+   */
+  function init(configuration) {
+    config = configuration;
+    if (!config.proxyUrl || !config.jsonProxyUrl) {
+      throw new Error("proxyUrl and jsonProxyUrl are required.");
+    }
+  }
+
+  return {
+    makeRequest: makeRequest,
+    getProxyUrl: getProxyUrl,
+    encodeValues: encodeValues,
+    init: init
+  };
+}();

Added: incubator/shindig/trunk/features/core/legacy.js
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/core/legacy.js?rev=610205&view=auto
==============================================================================
--- incubator/shindig/trunk/features/core/legacy.js (added)
+++ incubator/shindig/trunk/features/core/legacy.js Tue Jan  8 14:34:05 2008
@@ -0,0 +1,242 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ // All functions in this file should be treated as deprecated legacy routines.
+ // Gadget authors are explicitly discouraged from using any of them.
+
+var _IG_Prefs = gadgets.Prefs;
+
+// Yes, these technically modifiy gadget.Prefs as well. Unfortunately,
+// simply setting IG_Prefs.prototype to a new gadgets.Prefs object means
+// that we'd have to duplicate the gadgets.Prefs constructor.
+_IG_Prefs._parseURL = gadgets.Prefs.parseUrl;
+
+function _IG_Fetch_wrapper(callback, obj) {
+  callback(obj.data);
+}
+
+function _IG_FetchContent(url, callback, opt_params) {
+  var params = opt_params || {};
+  var cb = gadgets.util.makeClosure(null, _IG_Fetch_wrapper, callback);
+  gadgets.io.makeRequest(url, cb, params);
+}
+
+function _IG_FetchXmlContent(url, callback, opt_params) {
+  var params = opt_params || {};
+  params.contentType = "dom";
+  var cb = gadgets.util.makeClosure(null, _IG_Fetch_wrapper, callback);
+  gadgets.io.makeRequest(url, cb, params);
+}
+
+function _IG_FetchFeedAsJSON_cb(callback, obj) {
+  if (obj.data.fr_1) {
+    callback(obj.data.fr_1);
+  } else {
+    callback(null);
+  }
+}
+
+// NOTE: this implementation does not batch calls as is the case on igoogle.
+function _IG_FetchFeedAsJSON(url, callback, numItems, getDescriptions,
+                             opt_params) {
+  var params = opt_params || {};
+  // TODO: this no longer works. The proxy needs to support POST requests
+  // to make it work.
+  var finalUrl = "http://www.gmodules.com/ig/feedjson?fr_1=";
+  finalUrl += gadgets.io.encodeValues({
+    url: encodeURIComponent(url),
+    val: numItems,
+    sum: getDescriptions ? 1 : 0
+  });
+  params.contentType = "json";
+  var cb = gadgets.util.makeClosure(null, _IG_FetchFeedAsJSON_cb, callback);
+  gadgets.io.makeRequest(finalUrl, cb, params);
+}
+
+function _IG_GetCachedUrl(url) {
+  return gadgets.io.getProxyUrl(url);
+}
+function _IG_GetImageUrl(url) {
+  return gadgets.io.getProxyUrl(url);
+}
+
+function _IG_RegisterOnloadHandler(callback) {
+  gadgets.util.registerOnLoadHandler(callback);
+}
+
+var _args = gadgets.util.getUrlParameters;
+
+/**
+ * Fetches an object by document id.
+ *
+ * @param {String | Object} el The element you wish to fetch. You may pass
+ *     an object in which allows this to be called regardless of whether or
+ *     not the type of the input is known.
+ * @return {HTMLElement} The element, if it exists in the document, or null.
+ */
+function _gel(el) {
+  return document.getElementById ? document.getElementById(el) : null;
+}
+
+/**
+ * Fetches elements by tag name.
+ * This is functionally identical to document.getElementsByTagName()
+ *
+ * @param {String} tag The tag to match elements against.
+ * @return {Array.<HTMLElement>} All elements of this tag type.
+ */
+function _gelstn(tag) {
+  if (n === "*" && document.all) {
+    return document.all;
+  }
+  return document.getElementsByTagName ?
+         document.getElementsByTagName(n) : [];
+}
+
+/**
+ * Fetches elements with ids matching a given regular expression.
+ *
+ * @param {tagName} tag The tag to match elements against.
+ * @param {RegEx} regex The expression to match.
+ * @return {Array.<HTMLElement>} All elements of this tag type that match
+ *     regex.
+ */
+function _gelsbyregex(tagName, regex) {
+  var matchingTags = _gelstn(tagName);
+  var matchingRegex = [];
+  for (var i = 0, j = matchingTags.length; i < j; ++i) {
+    if (regex.test(matchingTags[i].id)) {
+      matchingRegex.push(matchingTags[i]);
+    }
+  }
+  return matchingRegex;
+}
+
+/**
+ * URI escapes the given string.
+ * @param {String} str The string to escape.
+ * @return {String} The escaped string.
+ */
+function _esc(str) {
+  return window.encodeURIComponent ? encodeURIComponent(str) : escape(str);
+}
+
+/**
+ * URI unescapes the given string.
+ * @param {String} str The string to unescape.
+ * @return {String} The unescaped string.
+ */
+function _unesc(str) {
+  return window.decodeURIComponent ? decodeURIComponent(str) : unescape(str);
+}
+
+/**
+ * Encodes HTML entities such as <, " and >.
+ *
+ * @param {String} str The string to escape.
+ * @return The escaped string.
+ */
+function _hesc(str) {
+  // '<' and '>'
+  str = str.replace(/</g, "&lt;").replace(/>/g, "&gt;");
+  // '"' and '
+  str = str.replace(/"/g, "&quot;").replace(/'/g, "&#39;");
+
+  return str;
+}
+
+/**
+ * Removes HTML tags from the given input string.
+ *
+ * @param {String} str The string to strip.
+ * @return The stripped string.
+ */
+function _striptags(str) {
+  return s.replace(/<\/?[^>]+>/g, "");
+}
+
+/**
+ * Trims leading & trailing whitespace from the given string.
+ *
+ * @param {String} str The string to trim.
+ * @return {String} The trimmed string.
+ */
+function _trim(str) {
+  return str.replace(/^\s+|\s+$/g, "");
+}
+
+/**
+ * Toggles the given element between being shown and block-style display.
+ *
+ * @param {String | HTMLElement} el The element to toggle.
+ */
+function _toggle(el) {
+  el = _gel(el);
+  if (el !== null) {
+    if (el.style.display.length === 0 || el.style.display === "block") {
+      el.style.display = "none";
+    } else if (el.style.display === "none") {
+      el.style.display = "block";
+    }
+  }
+}
+
+/**
+ * {Number} A counter used by uniqueId().
+ */
+var _global_legacy_uidCounter = 0;
+
+/**
+ * @return a unique number.
+ */
+function _uid() {
+  return _global_legacy_uidCounter++;
+}
+
+/**
+ * @param {Number} a
+ * @param {Number} b
+ * @return The lesser of a or b.
+ */
+function _min(a, b) {
+  return (a < b ? a : b);
+}
+
+/**
+ * @param {Number} a
+ * @param {Number} b
+ * @return The greater of a or b.
+ */
+function _max(a, b) {
+  return (a > b ? a : b);
+}
+
+/**
+ * @param {String} name
+ * @param {Array.<String | Object>} sym
+ */
+function _exportSymbols(name, sym) {
+  var obj = {};
+  for (var i = 0, j = sym.length; i < j; i += 2) {
+    obj[sym[i]] = sym[i + 1];
+  }
+  var parts = name.split(".");
+  var attach = window;
+  for (var i = 0, j = parts.length - 1; i < j; ++i) {
+    var tmp = {};
+    attach[parts[i]] = tmp;
+    attach = tmp;
+  }
+  attach[parts[parts.length - 1]] = obj;
+}

Added: incubator/shindig/trunk/features/core/prefs.js
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/core/prefs.js?rev=610205&view=auto
==============================================================================
--- incubator/shindig/trunk/features/core/prefs.js (added)
+++ incubator/shindig/trunk/features/core/prefs.js Tue Jan  8 14:34:05 2008
@@ -0,0 +1,363 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * Provides access to user prefs, module dimensions, and messages.
+ *
+ * Clients can access their preferences by constructing an instance of
+ * gadgets.Prefs and passing in their module id.  Example:
+ *
+ *   var prefs = new gadgets.Prefs();
+ *   var name = prefs.getString("name");
+ *   var lang = prefs.getLang();
+ *
+ * Modules with type=url can also use this library to parse arguments passed
+ * by URL, but this is not the common case:
+ *
+ *   <script src="http://apache.org/shindig/prefs.js"></script>
+ *   <script>
+ *   gadgets.Prefs.parseUrl();
+ *   var prefs = new gadgets.Prefs();
+ *   var name = prefs.getString("name");
+ *   </script>
+ */
+
+var gadgets = gadgets || {};
+
+/**
+ * Stores preferences for the default shindig implementation.
+ * @private
+ */
+gadgets.PrefStore_ = function() {
+  var modules = {};
+
+  /**
+   * Returns the module named by moduleId
+   * @param {Number | String} moduleId The module id to fetch.
+   * @return {Object} An object containing module data.
+   */
+  function getModuleData(moduleId) {
+    if (!modules[moduleId]) {
+      modules[moduleId] = {
+        prefs:{},
+        msgs:{},
+        language:"all",
+        country:"all"
+      };
+    }
+    return modules[moduleId];
+  }
+
+  /**
+   * Adds a new user preference to the stored set for the given module id.
+   *
+   * @param {Number | String} moduleId The module id to add the pref for.
+   * @param {String} key The key to add. May be an object where keys = key and
+   *     values = value.
+   * @param {String} value The value for the key. Optional.
+   */
+  function setPref(moduleId, key, value) {
+    var module = getModuleData(moduleId);
+    if (typeof key !== "string") {
+      for (var i in key) {
+        module.prefs[i] = key[i];
+      }
+    } else {
+      module.prefs[key] = value;
+    }
+  }
+
+  /**
+   * Adds a new message to the stored set for the given module id.
+   *
+   * @param {Number | String} moduleId The module id to add the pref for.
+   * @param {String | Object} key The key to add. May be an object where keys =
+   *     key and values = value.
+   * @param {String} value The value for the key. Optional.
+   */
+  function setMsg(moduleId, key, value) {
+    var module = getModuleData(moduleId);
+    if (typeof key !== "string") {
+      for (var i in key) {
+        module.msgs[i] = key[i];
+      }
+    } else {
+      module.msgs[key] = value;
+    }
+  }
+
+  var defaultModuleId = 0;
+
+  /**
+   * @param {String | Number} moduleId The module id to set as default.
+   */
+  function setDefaultModuleId(moduleId) {
+    defaultModuleId = moduleId;
+  }
+
+  /**
+   * @return {String | Number} The default module id.
+   */
+  function getDefaultModuleId() {
+    return defaultModuleId;
+  }
+
+  /**
+   * @param {Number | String} moduleId The module id to set the language for.
+   * @param {String} language The language to use.
+   */
+  function setLanguage(moduleId, language) {
+    getModuleData(moduleId).language = language;
+  }
+
+  /**
+   * Sets the default country for this module id.
+   */
+  function setCountry(moduleId, country) {
+    getModuleData(moduleId).country = country;
+  }
+
+  // Export public API.
+  return {
+    setPref:setPref,
+    setMsg:setMsg,
+    setCountry:setCountry,
+    setLanguage:setLanguage,
+    getModuleData:getModuleData,
+    setDefaultModuleId:setDefaultModuleId,
+    getDefaultModuleId:getDefaultModuleId
+  };
+}();
+
+/**
+ * @constructor
+ * @param {String | Number} moduleId The module id to create prefs for.
+ */
+gadgets.Prefs = function(moduleId) {
+  if (typeof moduleId === "undefined") {
+    this.moduleId_ = gadgets.PrefStore_.getDefaultModuleId();
+  } else {
+    this.moduleId_ = moduleId;
+  }
+  this.data_ = gadgets.PrefStore_.getModuleData(this.moduleId_);
+  // This is used to eliminate one hash table lookup per value fetched.
+  this.prefs_ = this.data_.prefs;
+  this.msgs_ = this.data_.msgs;
+};
+
+/**
+ * Static pref parser. Parses all parameters from the url and stores them
+ * for later use when creating a new gadgets.Prefs object.
+ * You should only ever call this if you are a type=url gadget.
+ */
+gadgets.Prefs.parseUrl = function(moduleId) {
+  var prefs = {};
+  var msgs = {};
+  var country = "all";
+  var language = "all";
+  if (gadgets.util) {
+    var params = gadgets.util.getUrlParameters();
+    for (var i in params) {
+      if (i.indexOf("up_") === 0 && i.length > 3) {
+        prefs[i.substr(3)] = String(params[i]);
+      } else if (i.indexOf("msg_") === 0 && i.length > 4) {
+        msgs[i.substr(4)] = String(params[i]);
+      } else if (i === "country") {
+        country = params[i];
+      } else if (i === "lang") {
+        language = params[i];
+      } else if (i === "mid") {
+        moduleId = params[i];
+      }
+    }
+  }
+  gadgets.PrefStore_.setDefaultModuleId(moduleId);
+  gadgets.PrefStore_.setPref(moduleId, prefs);
+  gadgets.PrefStore_.setMsg(moduleId, msgs);
+  gadgets.PrefStore_.setLanguage(moduleId, language);
+  gadgets.PrefStore_.setCountry(moduleId, country);
+};
+
+/**
+ * Internal helper for pref fetching.
+ * @param {String} key The key to fetch.
+ * @return {String}
+ */
+gadgets.Prefs.prototype.getPref_ = function(key) {
+  var val = this.prefs_[key];
+  return typeof val === "undefined" ? null : val;
+}
+
+/**
+ * Retrieves the named preference as a string.
+ * @param {String} key The preference to fetch.
+ * @return {String} The preference. If not set, an empty string.
+ */
+gadgets.Prefs.prototype.getString = function(key) {
+  var val = this.getPref_(key);
+  return val === null ? "" : val;
+};
+
+/**
+ * Retrieves the named preference as an integer.
+ * @param {String} key The preference to fetch.
+ * @return {Number} The preference. If not set, 0.
+ */
+gadgets.Prefs.prototype.getInt = function(key) {
+  var val = parseInt(this.getPref_(key), 10);
+  return isNaN(val) ? 0 : val;
+};
+
+/**
+ * Retrieve the named preference as a floating point value.
+ * @param {String} key The preference to fetch.
+ * @return {Number} The preference. If not set, 0.
+ */
+gadgets.Prefs.prototype.getFloat = function(key) {
+  var val = parseFloat(this.getPref_(key));
+  return isNaN(val) ? 0 : val;
+};
+
+/**
+ * Retrieves the named preference as a boolean.
+ * @param {String} key The preference to fetch.
+ * @return {Boolean} The preference. If not set, false.
+ */
+gadgets.Prefs.prototype.getBool = function(key) {
+  var val = this.getPref_(key);
+  if (val !== null) {
+    return val === "true" || val === true || !!parseInt(val, 10);
+  }
+  return false;
+};
+
+/**
+ * Stores a preference.
+ * @param {String | Object} key The pref to store.
+ * @param {String} val The values to store.
+ */
+gadgets.Prefs.prototype.set = function(key, value) {
+  throw new Error("setprefs feature required to make this call.");
+};
+
+/**
+ * Retrieves the named preference as an array.
+ * @param {String} key The preference to fetch.
+ * @return {Array.<String>} The preference. If not set, an empty array.
+ *     UserPref values that were not declared as lists will be treated as
+ *     1 element arrays.
+ */
+gadgets.Prefs.prototype.getArray = function(key) {
+  var val = this.getPref_(key);
+  if (val !== null) {
+    var arr = val.split("|");
+    // Decode pipe characters.
+    for (var i = 0, j = arr.length; i < j; ++i) {
+      arr[i] = arr[i].replace(/%7C/g, "|");
+    }
+    return arr;
+  }
+  return [];
+};
+
+/**
+ * Stores a preference from the given list.
+ * @param {String} key The pref to store.
+ * @param {Array.<String | Number>} val The values to store.
+ */
+gadgets.Prefs.prototype.setArray = function(key, val) {
+  throw new Error("setprefs feature required to make this call.");
+};
+
+/**
+ * Fetches an unformatted message.
+ * @param {String} key The message to fetch
+ * @return {String} The message.
+ */
+gadgets.Prefs.prototype.getMsg = function(key) {
+  var val = this.msgs_[key];
+  return typeof val === "undefined" ? "" : val;
+};
+
+/**
+ * The regex pulls out the text before and after the positional argument
+ * and digs down for a possible example value in case no actual value
+ * was provided.  It is used by the function getMsgFormatted.
+ *
+ * Example: "Foo <ph name="number"><ex>bar</ex>%1</ph> baz."
+ * 0 = "Foo <ph name="number"><ex>bar</ex>%1</ph> baz." : match for the
+ *     whole regex.
+ *
+ * 1 = "Foo " : matches first (.*) in regex
+ *
+ * 2 = "<ph name="number"><ex>bar</ex>%1</ph>" : matches
+ *     (\<ph.*?\>\s*(\<ex\>(.*?)\<\/ex\>)?\s*%1\s*\<\/ph\>) in regex
+ * 3 = "<ex>bar</ex>" : matches (\<ex\>(.*?)\<\/ex\>)? in regex, since it
+ *     is an optional param it may have the value "undefined"
+ * 4 = "bar" : matches (.*?) in regex (it is a non-greedy regex)
+ *     if 3=undefined then 4 = "undefined".
+ *
+ * 5 = " baz." : matches final (.*) in regex
+ *
+ * TODO: this may need to be a single line even though it's > 80 characters
+ * because some browsers may not properly interepret the line continuation.
+ */
+gadgets.Prefs.MESSAGE_SUBST_REGEX =
+    /(.*)(\<ph.*?\>\s*(\<ex\>(.*?)\<\/ex\>)?\s*%1\s*\<\/ph\>)(.*)/;
+
+/**
+ * Returns a message value with the positional argument opt_subst in place if
+ * it is provided or the provided example value if it is not, or the empty
+ * string if the message is not found.
+ * Eventually we may provide controls to return different default messages.
+ *
+ * @param {String} key The message to fetch.
+ * @param {String} subst ????
+ * @return {String} The formatted string.
+ */
+gadgets.Prefs.prototype.getMsgFormatted = function(key, opt_subst) {
+  var val = this.getMsg(key);
+
+  var result = val.match(gadgets.Prefs.MESSAGE_SUBST_REGEX);
+  // Allows string that should be getMsg to also call getMsgFormatted
+  if (!result || !result[0]) {
+    return val;
+  }
+  if (typeof opt_subst === "undefined") {
+    var sub = result[4] || "";
+    return result[1] + sub + result[5];
+  }
+  return result[1] + opt_subst + result[5];
+};
+
+/**
+ * @return {String} The country for this module instance.
+ */
+gadgets.Prefs.prototype.getCountry = function() {
+  return this.data_.country;
+};
+
+/**
+ * @return {String} The language for this module instance.
+ */
+gadgets.Prefs.prototype.getLang = function() {
+  return this.data_.language;
+};
+
+/**
+ * @return {String | Number} The module id for this module instance.
+ */
+gadgets.Prefs.prototype.getModuleId = function() {
+  return this.moduleId_;
+};

Added: incubator/shindig/trunk/features/core/util.js
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/core/util.js?rev=610205&view=auto
==============================================================================
--- incubator/shindig/trunk/features/core/util.js (added)
+++ incubator/shindig/trunk/features/core/util.js Tue Jan  8 14:34:05 2008
@@ -0,0 +1,153 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var gadgets = gadgets || {};
+
+/**
+ * General purpose utilities.
+ */
+gadgets.util = function() {
+  /**
+   * Parses url parameters into an object.
+   * @return {Array.<String>} the parameters.
+   */
+  function parseUrlParams() {
+    // Get settings from url, 'hash' takes precedence over 'search' component
+    // don't use document.location.hash due to browser differences.
+    var query;
+    var l = document.location.href;
+    var queryIdx = l.indexOf("?");
+    var hashIdx = l.indexOf("#");
+    if (hashIdx === -1) {
+      query = l.substr(queryIdx + 1);
+    } else {
+      // essentially replaces "#" with "&"
+      query = [l.substr(queryIdx + 1, hashIdx - queryIdx - 1), "&",
+               l.substr(hashIdx + 1)].join("");
+    }
+    return query.split("&");
+  }
+
+  var parameters = null;
+
+  /**
+   * @return {Object} Parameters passed into the query string.
+   */
+  function getUrlParameters() {
+    if (parameters !== null) {
+      return parameters;
+    }
+    parameters = {};
+    var pairs = parseUrlParams();
+    var unesc = window.decodeURIComponent ? decodeURIComponent : unescape;
+    for (var i = 0, j = pairs.length; i < j; ++i) {
+      var pos = pairs[i].indexOf('=');
+      if (pos === -1) {
+        continue;
+      }
+      var argName = pairs[i].substring(0, pos);
+      var value = pairs[i].substring(pos + 1);
+      // difference to IG_Prefs, is that args doesn't replace spaces in argname:
+      // unclear on if it should do: argname = argname.replace(/\+/g, " ");
+      value = value.replace(/\+/g, " ");
+      parameters[argName] = unesc(value);
+    }
+    return parameters;
+  }
+
+  /**
+   * Creates a closure which is suitable for passing as a callback.
+   *
+   * @param {Object} scope The execution scope. May be null if there is no
+   *     need to associate a specific instance of an object with this callback.
+   * @param {Function} callback The callback to invoke when this is run.
+   *     any arguments passed in will be passed after your initial arguments.
+   * @param {Object} var_args Any number of arguments may be passed to the
+   *     callback. They will be received in the order they are passed in.
+   */
+  function makeClosure(scope, callback, var_args) {
+    // arguments isn't a real array, so we copy it into one.
+    var tmpArgs = [];
+    for (var i = 2, j = arguments.length; i < j; ++i) {
+     tmpArgs.push(arguments[i]);
+    }
+    return function() {
+      // append new arguments.
+      for (var i = 0, j = arguments.length; i < j; ++i) {
+        tmpArgs.push(arguments[i]);
+      }
+      callback.apply(scope, tmpArgs);
+    };
+  }
+
+  var features = {};
+
+  /**
+   * @param {String} feature The feature to get parameters for.
+   * @return {Object} The parameters for the given feature, or null.
+   */
+  function getFeatureParameters(feature) {
+    return typeof features[feature] === "undefined" ? null : features[feature];
+  }
+
+  /**
+   * @param {String} feature The feature to test for.
+   * @return {Boolean} True if the feature is supported.
+   */
+  function hasFeature(feature) {
+    return typeof features[feature] === "undefined";
+  }
+
+  var onLoadHandlers = [];
+
+  /**
+   * Registers an onload handler.
+   * @param {Function} callback The handler to run.
+   */
+  function registerOnLoadHandler(callback) {
+    onLoadHandlers.push(callback);
+  }
+
+  /**
+   * Runs all functions registered via registerOnLoadHandler.
+   */
+  function runOnLoadHandlers() {
+    for (var i = 0, j = onLoadHandlers.length; i < j; ++i) {
+      onLoadHandlers[i]();
+    }
+  }
+
+  /**
+   *  @param {Object} featureData The features that are supported, and
+   *    their parameters.
+   */
+  function init(featureData) {
+    features = featureData;
+  }
+
+  // Export public API.
+  return {
+    getUrlParameters: getUrlParameters,
+    getFeatureParameters: getFeatureParameters,
+    hasFeature: hasFeature,
+    makeClosure: makeClosure,
+    registerOnLoadHandler: registerOnLoadHandler,
+
+    // only used by container
+    runOnLoadHandlers: runOnLoadHandlers,
+    init: init
+  };
+}();
+
+// TODO: Check for any other commonly used aliases

Added: incubator/shindig/trunk/features/ifpc/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/ifpc/feature.xml?rev=610205&view=auto
==============================================================================
--- incubator/shindig/trunk/features/ifpc/feature.xml (added)
+++ incubator/shindig/trunk/features/ifpc/feature.xml Tue Jan  8 14:34:05 2008
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!--
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<feature>
+  <name>ifpc</name>
+  <gadget>
+    <script>/* IFPC NOT SUPPORTED */</script>
+  </gadget>
+</feature>

Added: incubator/shindig/trunk/features/setprefs/feature.xml
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/setprefs/feature.xml?rev=610205&view=auto
==============================================================================
--- incubator/shindig/trunk/features/setprefs/feature.xml (added)
+++ incubator/shindig/trunk/features/setprefs/feature.xml Tue Jan  8 14:34:05 2008
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!--
+  Licensed under the Apache License, Version 2.0 (the "License");
+  you may not use this file except in compliance with the License.
+  You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+  Unless required by applicable law or agreed to in writing, software
+  distributed under the License is distributed on an "AS IS" BASIS,
+  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  See the License for the specific language governing permissions and
+  limitations under the License.
+-->
+<feature>
+  <name>setprefs</name>
+  <dependency>ifpc</dependency>
+  <gadget>
+    <script src="setprefs.js"/>
+  </gadget>
+</feature>

Added: incubator/shindig/trunk/features/setprefs/setprefs.js
URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/setprefs/setprefs.js?rev=610205&view=auto
==============================================================================
--- incubator/shindig/trunk/features/setprefs/setprefs.js (added)
+++ incubator/shindig/trunk/features/setprefs/setprefs.js Tue Jan  8 14:34:05 2008
@@ -0,0 +1,59 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+ /* This library augments gadgets.Prefs with functionality to store prefs
+  * dynamically.
+  */
+
+/**
+ * Stores a preference.
+ * @param {String | Object} key The pref to store.
+ * @param {String} val The values to store.
+ */
+gadgets.Prefs.prototype.set = function(key, value) {
+  if (arguments.length > 2) {
+    // For backwards compatibility. This can take the form:
+    // prefs.set(key0, value0, key1, value1, key2, value2);
+
+    // prefs.set({key0: value0, key1: value1, key2: value2});
+    var obj = {};
+    for (var i = 0, j = arguments.length; i < j; i += 2) {
+      obj[arguments[i]] = arguments[i + 1];
+    }
+    gadgets.PrefStore_.setPref(this.moduleId_, obj);
+  } else {
+    gadgets.PrefStore_.setPref(this.moduleId_, key, value);
+  }
+  // TODO: Update user pref store somehow.
+};
+
+/**
+ * Stores a preference from the given list.
+ * @param {String} key The pref to store.
+ * @param {Array.<String | Number>} val The values to store.
+ */
+gadgets.Prefs.prototype.setArray = function(key, val) {
+  if (!val.length || !val.join) {
+    throw new Error("Value is not an array.");
+  }
+
+  // We must escape pipe (|) characters to ensure that decoding in
+  // getArray actually works properly.
+  for (var i = 0, j = val.length; i < j; ++i) {
+    val[i] = val[i].replace(/\|/g, "%7C");
+  }
+  gadgets.PrefStore_.setPref(this.moduleId_, key, val.join("|"));
+
+  // TODO: Update user pref store somehow. Where do we hook into the container?
+};
\ No newline at end of file