http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/cssom/iframe/style.js
----------------------------------------------------------------------
diff --git a/externs/GCL/externs/goog/cssom/iframe/style.js 
b/externs/GCL/externs/goog/cssom/iframe/style.js
new file mode 100644
index 0000000..24c8c05
--- /dev/null
+++ b/externs/GCL/externs/goog/cssom/iframe/style.js
@@ -0,0 +1,1016 @@
+// Copyright 2007 The Closure Library Authors. All Rights Reserved.
+//
+// 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 Rights Reserved.
+
+/**
+ * @fileoverview Provides utility routines for copying modified
+ * {@code CSSRule} objects from the parent document into iframes so that any
+ * content in the iframe will be styled as if it was inline in the parent
+ * document.
+ *
+ * <p>
+ * For example, you might have this CSS rule:
+ *
+ * #content .highlighted { background-color: yellow; }
+ *
+ * And this DOM structure:
+ *
+ * <div id="content">
+ *   <iframe />
+ * </div>
+ *
+ * Then inside the iframe you have:
+ *
+ * <body>
+ * <div class="highlighted">
+ * </body>
+ *
+ * If you copied the CSS rule directly into the iframe, it wouldn't match the
+ * .highlighted div. So we rewrite the original stylesheets based on the
+ * context where the iframe is going to be inserted. In this case the CSS
+ * selector would be rewritten to:
+ *
+ * body .highlighted { background-color: yellow; }
+ * </p>
+ *
+ */
+
+
+goog.provide('goog.cssom.iframe.style');
+
+goog.require('goog.asserts');
+goog.require('goog.cssom');
+goog.require('goog.dom');
+goog.require('goog.dom.NodeType');
+goog.require('goog.dom.TagName');
+goog.require('goog.dom.classlist');
+goog.require('goog.string');
+goog.require('goog.style');
+goog.require('goog.userAgent');
+
+
+/**
+ * Regexp that matches "a", "a:link", "a:visited", etc.
+ * @type {RegExp}
+ * @private
+ */
+goog.cssom.iframe.style.selectorPartAnchorRegex_ =
+    /a(:(link|visited|active|hover))?/;
+
+
+/**
+ * Delimiter between selectors (h1, h2)
+ * @type {string}
+ * @private
+ */
+goog.cssom.iframe.style.SELECTOR_DELIMITER_ = ',';
+
+
+/**
+ * Delimiter between selector parts (.main h1)
+ * @type {string}
+ * @private
+ */
+goog.cssom.iframe.style.SELECTOR_PART_DELIMITER_ = ' ';
+
+
+/**
+ * Delimiter marking the start of a css rules section ( h1 { )
+ * @type {string}
+ * @private
+ */
+goog.cssom.iframe.style.DECLARATION_START_DELIMITER_ = '{';
+
+
+/**
+ * Delimiter marking the end of a css rules section ( } )
+ * @type {string}
+ * @private
+ */
+goog.cssom.iframe.style.DECLARATION_END_DELIMITER_ = '}\n';
+
+
+
+/**
+ * Class representing a CSS rule set. A rule set is something like this:
+ * h1, h2 { font-family: Arial; color: red; }
+ * @constructor
+ * @private
+ */
+goog.cssom.iframe.style.CssRuleSet_ = function() {
+  /**
+   * Text of the declarations inside the rule set.
+   * For example: 'font-family: Arial; color: red;'
+   * @type {string}
+   */
+  this.declarationText = '';
+
+  /**
+   * Array of CssSelector objects, one for each selector.
+   * Example: [h1, h2]
+   * @type {Array<goog.cssom.iframe.style.CssSelector_>}
+   */
+  this.selectors = [];
+};
+
+
+/**
+ * Initializes the rule set from a {@code CSSRule}.
+ *
+ * @param {CSSRule} cssRule The {@code CSSRule} to initialize from.
+ * @return {boolean} True if initialization succeeded. We only support
+ *     {@code CSSStyleRule} and {@code CSSFontFaceRule} objects.
+ */
+goog.cssom.iframe.style.CssRuleSet_.prototype.initializeFromCssRule =
+    function(cssRule) {
+  var ruleStyle = cssRule.style; // Cache object for performance.
+  if (!ruleStyle) {
+    return false;
+  }
+  var selector;
+  var declarations = '';
+  if (ruleStyle &&
+      (selector = cssRule.selectorText) &&
+      (declarations = ruleStyle.cssText)) {
+    // IE get confused about cssText context if a stylesheet uses the
+    // mid-pass hack, and it ends up with an open comment (/*) but no
+    // closing comment. This will effectively comment out large parts
+    // of generated stylesheets later. This errs on the safe side by
+    // always tacking on an empty comment to force comments to be closed
+    // We used to check for a troublesome open comment using a regular
+    // expression, but it's faster not to check and always do this.
+    if (goog.userAgent.IE) {
+      declarations += '/* */';
+    }
+  } else if (cssRule.cssText) {
+    var cssSelectorMatch = /([^\{]+)\{/;
+    var endTagMatch = /\}[^\}]*$/g;
+    // cssRule.cssText contains both selector and declarations:
+    // parse them out.
+    selector = cssSelectorMatch.exec(cssRule.cssText)[1];
+    // Remove selector, {, and trailing }.
+    declarations = cssRule.cssText.replace(cssSelectorMatch, '').replace(
+        endTagMatch, '');
+  }
+  if (selector) {
+    this.setSelectorsFromString(selector);
+    this.declarationText = declarations;
+    return true;
+  }
+  return false;
+};
+
+
+/**
+ * Parses a selectors string (which may contain multiple comma-delimited
+ * selectors) and loads the results into this.selectors.
+ * @param {string} selectorsString String containing selectors.
+ */
+goog.cssom.iframe.style.CssRuleSet_.prototype.setSelectorsFromString =
+    function(selectorsString) {
+  this.selectors = [];
+  var selectors = selectorsString.split(/,\s*/gm);
+  for (var i = 0; i < selectors.length; i++) {
+    var selector = selectors[i];
+    if (selector.length > 0) {
+      this.selectors.push(new goog.cssom.iframe.style.CssSelector_(selector));
+    }
+  }
+};
+
+
+/**
+ * Make a copy of this ruleset.
+ * @return {!goog.cssom.iframe.style.CssRuleSet_} A new CssRuleSet containing
+ *     the same data as this one.
+ */
+goog.cssom.iframe.style.CssRuleSet_.prototype.clone = function() {
+  var newRuleSet = new goog.cssom.iframe.style.CssRuleSet_();
+  newRuleSet.selectors = this.selectors.concat();
+  newRuleSet.declarationText = this.declarationText;
+  return newRuleSet;
+};
+
+
+/**
+ * Set the declaration text with properties from a given object.
+ * @param {Object} sourceObject Object whose properties and values should
+ *     be used to generate the declaration text.
+ * @param {boolean=} opt_important Whether !important should be added to each
+ *     declaration.
+ */
+goog.cssom.iframe.style.CssRuleSet_.prototype.setDeclarationTextFromObject =
+    function(sourceObject, opt_important) {
+  var stringParts = [];
+  // TODO(user): for ... in is costly in IE6 (extra garbage collection).
+  for (var prop in sourceObject) {
+    var value = sourceObject[prop];
+    if (value) {
+      stringParts.push(prop,
+                       ':',
+                       value, (opt_important ? ' !important' : ''),
+                       ';');
+    }
+  }
+  this.declarationText = stringParts.join('');
+};
+
+
+/**
+ * Serializes this CssRuleSet_ into an array as a series of strings.
+ * The array can then be join()-ed to get a string representation
+ * of this ruleset.
+ * @param {Array<string>} array The array to which to append strings.
+ */
+goog.cssom.iframe.style.CssRuleSet_.prototype.writeToArray = function(array) {
+  var selectorCount = this.selectors.length;
+  var matchesAnchorTag = false;
+  for (var i = 0; i < selectorCount; i++) {
+    var selectorParts = this.selectors[i].parts;
+    var partCount = selectorParts.length;
+    for (var j = 0; j < partCount; j++) {
+      array.push(selectorParts[j].inputString_,
+                 goog.cssom.iframe.style.SELECTOR_PART_DELIMITER_);
+    }
+    if (i < (selectorCount - 1)) {
+      array.push(goog.cssom.iframe.style.SELECTOR_DELIMITER_);
+    }
+    if (goog.userAgent.GECKO &&
+        !goog.userAgent.isVersionOrHigher('1.9a')) {
+      // In Gecko pre-1.9 (Firefox 2 and lower) we need to add !important
+      // to rulesets that match "A" tags, otherwise Gecko's built-in
+      // stylesheet will take precedence when designMode is on.
+      matchesAnchorTag = matchesAnchorTag ||
+          goog.cssom.iframe.style.selectorPartAnchorRegex_.test(
+              selectorParts[partCount - 1].inputString_);
+    }
+  }
+  var declarationText = this.declarationText;
+  if (matchesAnchorTag) {
+    declarationText = goog.cssom.iframe.style.makeColorRuleImportant_(
+        declarationText);
+  }
+  array.push(goog.cssom.iframe.style.DECLARATION_START_DELIMITER_,
+             declarationText,
+             goog.cssom.iframe.style.DECLARATION_END_DELIMITER_);
+};
+
+
+/**
+ * Regexp that matches "color: value;".
+ * @type {RegExp}
+ * @private
+ */
+goog.cssom.iframe.style.colorImportantReplaceRegex_ =
+    /(^|;|{)\s*color:([^;]+);/g;
+
+
+/**
+ * Adds !important to a css color: rule
+ * @param {string} cssText Text of the CSS rule(s) to modify.
+ * @return {string} Text with !important added to the color: rule if found.
+ * @private
+ */
+goog.cssom.iframe.style.makeColorRuleImportant_ = function(cssText) {
+  // Replace to insert a "! important" string.
+  return cssText.replace(goog.cssom.iframe.style.colorImportantReplaceRegex_,
+                         '$1 color: $2 ! important; ');
+};
+
+
+
+/**
+ * Represents a single CSS selector, as described in
+ * http://www.w3.org/TR/REC-CSS2/selector.html
+ * Currently UNSUPPORTED are the following selector features:
+ * <ul>
+ *   <li>pseudo-classes (:hover)
+ *   <li>child selectors (div > h1)
+ *   <li>adjacent sibling selectors (div + h1)
+ *   <li>attribute selectors (input[type=submit])
+ * </ul>
+ * @param {string=} opt_selectorString String containing selectors to parse.
+ * @constructor
+ * @private
+ */
+goog.cssom.iframe.style.CssSelector_ = function(opt_selectorString) {
+
+  /**
+   * Object to track ancestry matches to speed up repeatedly testing this
+   * CssSelector against the same NodeAncestry object.
+   * @type {Object}
+   * @private
+   */
+  this.ancestryMatchCache_ = {};
+  if (opt_selectorString) {
+    this.setPartsFromString_(opt_selectorString);
+  }
+};
+
+
+/**
+ * Parses a selector string into individual parts.
+ * @param {string} selectorString A string containing a CSS selector.
+ * @private
+ */
+goog.cssom.iframe.style.CssSelector_.prototype.setPartsFromString_ =
+    function(selectorString) {
+  var parts = [];
+  var selectorPartStrings = selectorString.split(/\s+/gm);
+  for (var i = 0; i < selectorPartStrings.length; i++) {
+    if (!selectorPartStrings[i]) {
+      continue; // Skip empty strings.
+    }
+    var part = new goog.cssom.iframe.style.CssSelectorPart_(
+        selectorPartStrings[i]);
+    parts.push(part);
+  }
+  this.parts = parts;
+};
+
+
+/**
+ * Tests to see what part of a DOM element hierarchy would be matched by
+ * this selector, and returns the indexes of the matching element and matching
+ * selector part.
+ * <p>
+ * For example, given this hierarchy:
+ *   document > html > body > div.content > div.sidebar > p
+ * and this CSS selector:
+ *   body div.sidebar h1
+ * This would return {elementIndex: 4, selectorPartIndex: 1},
+ * indicating that the element at index 4 matched
+ * the css selector at index 1.
+ * </p>
+ * @param {goog.cssom.iframe.style.NodeAncestry_} elementAncestry Object
+ *     representing an element and its ancestors.
+ * @return {Object} Object with the properties elementIndex and
+ *     selectorPartIndex, or null if there was no match.
+ */
+goog.cssom.iframe.style.CssSelector_.prototype.matchElementAncestry =
+    function(elementAncestry) {
+
+  var ancestryUid = elementAncestry.uid;
+  if (this.ancestryMatchCache_[ancestryUid]) {
+    return this.ancestryMatchCache_[ancestryUid];
+  }
+
+  // Walk through the selector parts and see how far down the element hierarchy
+  // we can go while matching the selector parts.
+  var elementIndex = 0;
+  var match = null;
+  var selectorPart = null;
+  var lastSelectorPart = null;
+  var ancestorNodes = elementAncestry.nodes;
+  var ancestorNodeCount = ancestorNodes.length;
+
+  for (var i = 0; i <= this.parts.length; i++) {
+    selectorPart = this.parts[i];
+    while (elementIndex < ancestorNodeCount) {
+      var currentElementInfo = ancestorNodes[elementIndex];
+      if (selectorPart &&
+          selectorPart.testElement(currentElementInfo)) {
+        match = {
+          elementIndex: elementIndex,
+          selectorPartIndex: i
+        };
+        elementIndex++;
+        break;
+      } else if (lastSelectorPart &&
+                 lastSelectorPart.testElement(currentElementInfo)) {
+        match = {
+          elementIndex: elementIndex,
+          selectorPartIndex: i - 1
+        };
+      }
+      elementIndex++;
+    }
+    lastSelectorPart = selectorPart;
+  }
+  this.ancestryMatchCache_[ancestryUid] = match;
+  return match;
+};
+
+
+
+/**
+ * Represents one part of a CSS Selector. For example in the selector
+ * 'body #foo .bar', body, #foo, and .bar would be considered selector parts.
+ * In the official CSS spec these are called "simple selectors".
+ * @param {string} selectorPartString A string containing the selector part
+ *     in css format.
+ * @constructor
+ * @private
+ */
+goog.cssom.iframe.style.CssSelectorPart_ = function(selectorPartString) {
+  // Only one CssSelectorPart instance should exist for a given string.
+  var cacheEntry = goog.cssom.iframe.style.CssSelectorPart_.instances_[
+      selectorPartString];
+  if (cacheEntry) {
+    return cacheEntry;
+  }
+
+  // Optimization to avoid the more-expensive lookahead.
+  var identifiers;
+  if (selectorPartString.match(/[#\.]/)) {
+    // Lookahead regexp, won't work on IE 5.0.
+    identifiers = selectorPartString.split(/(?=[#\.])/);
+  } else {
+    identifiers = [selectorPartString];
+  }
+  var properties = {};
+  for (var i = 0; i < identifiers.length; i++) {
+    var identifier = identifiers[i];
+    if (identifier.charAt(0) == '.') {
+      properties.className = identifier.substring(1, identifier.length);
+    } else if (identifier.charAt(0) == '#') {
+      properties.id = identifier.substring(1, identifier.length);
+    } else {
+      properties.tagName = identifier.toUpperCase();
+    }
+  }
+  this.inputString_ = selectorPartString;
+  this.matchProperties_ = properties;
+  this.testedElements_ = {};
+  goog.cssom.iframe.style.CssSelectorPart_.instances_[selectorPartString] =
+      this;
+};
+
+
+/**
+ * Cache of existing CssSelectorPart_ instances.
+ * @type {Object}
+ * @private
+ */
+goog.cssom.iframe.style.CssSelectorPart_.instances_ = {};
+
+
+/**
+ * Test whether an element matches this selector part, considered in isolation.
+ * @param {Object} elementInfo Element properties to test.
+ * @return {boolean} Whether the element matched.
+ */
+goog.cssom.iframe.style.CssSelectorPart_.prototype.testElement =
+    function(elementInfo) {
+
+  var elementUid = elementInfo.uid;
+  var cachedMatch = this.testedElements_[elementUid];
+  if (typeof cachedMatch != 'undefined') {
+    return cachedMatch;
+  }
+
+  var matchProperties = this.matchProperties_;
+  var testTag = matchProperties.tagName;
+  var testClass = matchProperties.className;
+  var testId = matchProperties.id;
+
+  var matched = true;
+  if (testTag && testTag != '*' && testTag != elementInfo.nodeName) {
+    matched = false;
+  } else if (testId && testId != elementInfo.id) {
+    matched = false;
+  } else if (testClass &&
+             !elementInfo.classNames[testClass]) {
+    matched = false;
+  }
+
+  this.testedElements_[elementUid] = matched;
+  return matched;
+};
+
+
+
+/**
+ * Represents an element and all its parent/ancestor nodes.
+ * This class exists as an optimization so we run tests on an element
+ * hierarchy multiple times without walking the dom each time.
+ * @param {Element} el The DOM element whose ancestry should be stored.
+ * @constructor
+ * @private
+ */
+goog.cssom.iframe.style.NodeAncestry_ = function(el) {
+  var node = el;
+  var nodeUid = goog.getUid(node);
+
+  // Return an existing object from the cache if one exits for this node.
+  var ancestry = goog.cssom.iframe.style.NodeAncestry_.instances_[nodeUid];
+  if (ancestry) {
+    return ancestry;
+  }
+
+  var nodes = [];
+  do {
+    var nodeInfo = {
+      id: node.id,
+      nodeName: node.nodeName
+    };
+    nodeInfo.uid = goog.getUid(nodeInfo);
+    var className = node.className;
+    var classNamesLookup = {};
+    if (className) {
+      var classNames = 
goog.dom.classlist.get(goog.asserts.assertElement(node));
+      for (var i = 0; i < classNames.length; i++) {
+        classNamesLookup[classNames[i]] = 1;
+      }
+    }
+    nodeInfo.classNames = classNamesLookup;
+    nodes.unshift(nodeInfo);
+  } while (node = node.parentNode);
+
+  /**
+   * Array of nodes in order of hierarchy from the top of the document
+   * to the node passed to the constructor
+   * @type {Array<Node>}
+   */
+  this.nodes = nodes;
+
+  this.uid = goog.getUid(this);
+  goog.cssom.iframe.style.NodeAncestry_.instances_[nodeUid] = this;
+};
+
+
+/**
+ * Object for caching existing NodeAncestry instances.
+ * @private
+ */
+goog.cssom.iframe.style.NodeAncestry_.instances_ = {};
+
+
+/**
+ * Throw away all cached dom information. Call this if you've modified
+ * the structure or class/id attributes of your document and you want
+ * to recalculate the currently applied CSS rules.
+ */
+goog.cssom.iframe.style.resetDomCache = function() {
+  goog.cssom.iframe.style.NodeAncestry_.instances_ = {};
+};
+
+
+/**
+ * Inspects a document and returns all active rule sets
+ * @param {Document} doc The document from which to read CSS rules.
+ * @return {!Array<goog.cssom.iframe.style.CssRuleSet_>} An array of CssRuleSet
+ *     objects representing all the active rule sets in the document.
+ * @private
+ */
+goog.cssom.iframe.style.getRuleSetsFromDocument_ = function(doc) {
+  var ruleSets = [];
+  var styleSheets = goog.cssom.getAllCssStyleSheets(doc.styleSheets);
+  for (var i = 0, styleSheet; styleSheet = styleSheets[i]; i++) {
+    var domRuleSets = goog.cssom.getCssRulesFromStyleSheet(styleSheet);
+    if (domRuleSets && domRuleSets.length) {
+      for (var j = 0, n = domRuleSets.length; j < n; j++) {
+        var ruleSet = new goog.cssom.iframe.style.CssRuleSet_();
+        if (ruleSet.initializeFromCssRule(domRuleSets[j])) {
+          ruleSets.push(ruleSet);
+        }
+      }
+    }
+  }
+  return ruleSets;
+};
+
+
+/**
+ * Static object to cache rulesets read from documents. Inspecting all
+ * active css rules is an expensive operation, so its best to only do
+ * it once and then cache the results.
+ * @type {Object}
+ * @private
+ */
+goog.cssom.iframe.style.ruleSetCache_ = {};
+
+
+/**
+ * Cache of ruleset objects keyed by document unique ID.
+ * @type {Object}
+ * @private
+ */
+goog.cssom.iframe.style.ruleSetCache_.ruleSetCache_ = {};
+
+
+/**
+ * Loads ruleset definitions from a document. If the cache already
+ * has rulesets for this document the cached version will be replaced.
+ * @param {Document} doc The document from which to load rulesets.
+ */
+goog.cssom.iframe.style.ruleSetCache_.loadRuleSetsForDocument = function(doc) {
+  var docUid = goog.getUid(doc);
+  goog.cssom.iframe.style.ruleSetCache_.ruleSetCache_[docUid] =
+      goog.cssom.iframe.style.getRuleSetsFromDocument_(doc);
+};
+
+
+/**
+ * Retrieves the array of css rulesets for this document. A cached
+ * version will be used when possible.
+ * @param {Document} doc The document for which to get rulesets.
+ * @return {!Array<goog.cssom.iframe.style.CssRuleSet_>} An array of CssRuleSet
+ *     objects representing the css rule sets in the supplied document.
+ */
+goog.cssom.iframe.style.ruleSetCache_.getRuleSetsForDocument = function(doc) {
+  var docUid = goog.getUid(doc);
+  var cache = goog.cssom.iframe.style.ruleSetCache_.ruleSetCache_;
+  if (!cache[docUid]) {
+    goog.cssom.iframe.style.ruleSetCache_.loadRuleSetsForDocument(doc);
+  }
+  // Build a cloned copy of rulesets array, so if object in the returned array
+  // get modified future calls will still return the original unmodified
+  // versions.
+  var ruleSets = cache[docUid];
+  var ruleSetsCopy = [];
+  for (var i = 0; i < ruleSets.length; i++) {
+    ruleSetsCopy.push(ruleSets[i].clone());
+  }
+  return ruleSetsCopy;
+};
+
+
+/**
+ * Array of CSS properties that are inherited by child nodes, according to
+ * the CSS 2.1 spec. Properties that may be set to relative values, such
+ * as font-size, and line-height, are omitted.
+ * @type {Array<string>}
+ * @private
+ */
+goog.cssom.iframe.style.inheritedProperties_ = [
+  'color',
+  'visibility',
+  'quotes',
+  'list-style-type',
+  'list-style-image',
+  'list-style-position',
+  'list-style',
+  'page-break-inside',
+  'orphans',
+  'widows',
+  'font-family',
+  'font-style',
+  'font-variant',
+  'font-weight',
+  'text-indent',
+  'text-align',
+  'text-transform',
+  'white-space',
+  'caption-side',
+  'border-collapse',
+  'border-spacing',
+  'empty-cells',
+  'cursor'
+];
+
+
+/**
+ * Array of CSS 2.1 properties that directly effect text nodes.
+ * @type {Array<string>}
+ * @private
+ */
+goog.cssom.iframe.style.textProperties_ = [
+  'font-family',
+  'font-size',
+  'font-weight',
+  'font-variant',
+  'font-style',
+  'color',
+  'text-align',
+  'text-decoration',
+  'text-indent',
+  'text-transform',
+  'letter-spacing',
+  'white-space',
+  'word-spacing'
+];
+
+
+/**
+ * Reads the current css rules from element's document, and returns them
+ * rewriting selectors so that any rules that formerly applied to element will
+ * be applied to doc.body. This makes it possible to replace a block in a page
+ * with an iframe and preserve the css styling of the contents.
+ *
+ * @param {Element} element The element for which context should be calculated.
+ * @param {boolean=} opt_forceRuleSetCacheUpdate Flag to force the internal
+ *     cache of rulesets to refresh itself before we read the same.
+ * @param {boolean=} opt_copyBackgroundContext Flag indicating that if the
+ *     {@code element} has a transparent background, background rules
+ *     from the nearest ancestor element(s) that have background-color
+ *     and/or background-image set should be copied.
+ * @return {string} String containing all CSS rules present in the original
+ *     document, with modified selectors.
+ * @see goog.cssom.iframe.style.getBackgroundContext.
+ */
+goog.cssom.iframe.style.getElementContext = function(
+    element,
+    opt_forceRuleSetCacheUpdate,
+    opt_copyBackgroundContext) {
+  var sourceDocument = element.ownerDocument;
+  if (opt_forceRuleSetCacheUpdate) {
+    goog.cssom.iframe.style.ruleSetCache_.loadRuleSetsForDocument(
+        sourceDocument);
+  }
+  var ruleSets = goog.cssom.iframe.style.ruleSetCache_.
+      getRuleSetsForDocument(sourceDocument);
+
+  var elementAncestry = new goog.cssom.iframe.style.NodeAncestry_(element);
+  var bodySelectorPart = new goog.cssom.iframe.style.CssSelectorPart_('body');
+
+  for (var i = 0; i < ruleSets.length; i++) {
+    var ruleSet = ruleSets[i];
+    var selectors = ruleSet.selectors;
+    // Cache selectors.length since we may be adding rules in the loop.
+    var ruleCount = selectors.length;
+    for (var j = 0; j < ruleCount; j++) {
+      var selector = selectors[j];
+      // Test whether all or part of this selector would match
+      // this element or one of its ancestors
+      var match = selector.matchElementAncestry(elementAncestry);
+      if (match) {
+        var ruleIndex = match.selectorPartIndex;
+        var selectorParts = selector.parts;
+        var lastSelectorPartIndex = selectorParts.length - 1;
+        var selectorCopy;
+        if (match.elementIndex == elementAncestry.nodes.length - 1 ||
+            ruleIndex < lastSelectorPartIndex) {
+          // Either the first part(s) of the selector matched this element,
+          // or the first part(s) of the selector matched a parent element
+          // and there are more parts of the selector that could target
+          // children of this element.
+          // So we inject a new selector, replacing the part that matched this
+          // element with 'body' so it will continue to match.
+          var selectorPartsCopy = selectorParts.concat();
+          selectorPartsCopy.splice(0,
+                                   ruleIndex + 1,
+                                   bodySelectorPart);
+          selectorCopy = new goog.cssom.iframe.style.CssSelector_();
+          selectorCopy.parts = selectorPartsCopy;
+          selectors.push(selectorCopy);
+        } else if (ruleIndex > 0 && ruleIndex == lastSelectorPartIndex) {
+          // The rule didn't match this element, but the entire rule did
+          // match an ancestor element. In this case we want to copy
+          // just the last part of the rule, to give it a chance to be applied
+          // to additional matching elements inside this element.
+          // Example DOM structure: body > div.funky > ul > li#editme
+          // Example CSS selector: .funky ul
+          // New CSS selector: body ul
+          selectorCopy = new goog.cssom.iframe.style.CssSelector_();
+          selectorCopy.parts = [
+            bodySelectorPart,
+            selectorParts[lastSelectorPartIndex]
+          ];
+          selectors.push(selectorCopy);
+        }
+      }
+    }
+  }
+
+  // Insert a new ruleset, setting the current inheritable styles of this
+  // element as the defaults for everything under in the frame.
+  var defaultPropertiesRuleSet = new goog.cssom.iframe.style.CssRuleSet_();
+  var computedStyle = goog.cssom.iframe.style.getComputedStyleObject_(element);
+
+  // Copy inheritable styles so they are applied to everything under HTML.
+  var htmlSelector = new goog.cssom.iframe.style.CssSelector_();
+  htmlSelector.parts = [new goog.cssom.iframe.style.CssSelectorPart_('html')];
+  defaultPropertiesRuleSet.selectors = [htmlSelector];
+  var defaultProperties = {};
+  for (var i = 0, prop;
+       prop = goog.cssom.iframe.style.inheritedProperties_[i];
+       i++) {
+    defaultProperties[prop] = computedStyle[goog.string.toCamelCase(prop)];
+  }
+  defaultPropertiesRuleSet.setDeclarationTextFromObject(defaultProperties);
+  ruleSets.push(defaultPropertiesRuleSet);
+
+  var bodyRuleSet = new goog.cssom.iframe.style.CssRuleSet_();
+  var bodySelector = new goog.cssom.iframe.style.CssSelector_();
+  bodySelector.parts = [new goog.cssom.iframe.style.CssSelectorPart_('body')];
+  // Core set of sane property values for BODY, to prevent copied
+  // styles from completely breaking the display.
+  var bodyProperties = {
+    position: 'relative',
+    top: '0',
+    left: '0',
+    right: 'auto', // Override any existing right value so 'left' works.
+    display: 'block',
+    visibility: 'visible'
+  };
+  // Text formatting property values, to keep text nodes directly under BODY
+  // looking right.
+  for (i = 0; prop = goog.cssom.iframe.style.textProperties_[i]; i++) {
+    bodyProperties[prop] = computedStyle[goog.string.toCamelCase(prop)];
+  }
+  if (opt_copyBackgroundContext &&
+      goog.cssom.iframe.style.isTransparentValue_(
+          computedStyle['backgroundColor'])) {
+    // opt_useAncestorBackgroundRules means that, if the original element
+    // has a transparent backgorund, background properties rules should be
+    // added to explicitly make the body have the same background appearance
+    // as in the original element, even if its positioned somewhere else
+    // in the DOM.
+    var bgProperties =
+        goog.cssom.iframe.style.getBackgroundContext(element);
+    bodyProperties['background-color'] = bgProperties['backgroundColor'];
+    var elementBgImage = computedStyle['backgroundImage'];
+    if (!elementBgImage || elementBgImage == 'none') {
+      bodyProperties['background-image'] = bgProperties['backgroundImage'];
+      bodyProperties['background-repeat'] = bgProperties['backgroundRepeat'];
+      bodyProperties['background-position'] =
+          bgProperties['backgroundPosition'];
+    }
+  }
+
+  bodyRuleSet.setDeclarationTextFromObject(bodyProperties, true);
+  bodyRuleSet.selectors = [bodySelector];
+  ruleSets.push(bodyRuleSet);
+
+  // Write outputTextParts to doc.
+  var ruleSetStrings = [];
+  ruleCount = ruleSets.length;
+  for (i = 0; i < ruleCount; i++) {
+    ruleSets[i].writeToArray(ruleSetStrings);
+  }
+  return ruleSetStrings.join('');
+};
+
+
+/**
+ * Tests whether a value is equivalent to 'transparent'.
+ * @param {string} colorValue The value to test.
+ * @return {boolean} Whether the value is transparent.
+ * @private
+ */
+goog.cssom.iframe.style.isTransparentValue_ = function(colorValue) {
+  return colorValue == 'transparent' || colorValue == 'rgba(0, 0, 0, 0)';
+};
+
+
+/**
+ * Returns an object containing the set of computedStyle/currentStyle
+ * values for the given element. Note that this should be used with
+ * caution as it ignores the fact that currentStyle and computedStyle
+ * are not the same for certain properties.
+ *
+ * @param {Element} element The element whose computed style to return.
+ * @return {Object} Object containing style properties and values.
+ * @private
+ */
+goog.cssom.iframe.style.getComputedStyleObject_ = function(element) {
+  // Return an object containing the element's computedStyle/currentStyle.
+  // The resulting object can be re-used to read multiple properties, which
+  // is faster than calling goog.style.getComputedStyle every time.
+  return element.currentStyle ||
+      goog.dom.getOwnerDocument(element).defaultView.getComputedStyle(
+          element, '') || {};
+};
+
+
+/**
+ * RegExp that splits a value like "10px" or "-1em" into parts.
+ * @private
+ * @type {RegExp}
+ */
+goog.cssom.iframe.style.valueWithUnitsRegEx_ = /^(-?)([0-9]+)([a-z]*|%)/;
+
+
+/**
+ * Given an object containing a set of styles, returns a two-element array
+ * containing the values of background-position-x and background-position-y.
+ * @param {Object} styleObject Object from which to read style properties.
+ * @return {Array<string>} The background-position values in the order [x, y].
+ * @private
+ */
+goog.cssom.iframe.style.getBackgroundXYValues_ = function(styleObject) {
+  // Gecko only has backgroundPosition, containing both values.
+  // IE has only backgroundPositionX/backgroundPositionY.
+  // WebKit has both.
+  if (styleObject['backgroundPositionY']) {
+    return [styleObject['backgroundPositionX'],
+            styleObject['backgroundPositionY']];
+  } else {
+    return (styleObject['backgroundPosition'] || '0 0').split(' ');
+  }
+};
+
+
+/**
+ * Generates a set of CSS properties that can be used to make another
+ * element's background look like the background of a given element.
+ * This is useful when you want to copy the CSS context of an element,
+ * but the element's background is transparent. In the original context
+ * you would see the ancestor's backround color/image showing through,
+ * but in the new context there might be a something different underneath.
+ * Note that this assumes the element you're copying context from has a
+ * fairly standard positioning/layout - it assumes that when the element
+ * has a transparent background what you're going to see through it is its
+ * ancestors.
+ * @param {Element} element The element from which to copy background styles.
+ * @return {!Object} Object containing background* properties.
+ */
+goog.cssom.iframe.style.getBackgroundContext = function(element) {
+  var propertyValues = {
+    'backgroundImage': 'none'
+  };
+  var ancestor = element;
+  var currentIframeWindow;
+  // Walk up the DOM tree to find the ancestor nodes whose backgrounds
+  // may be visible underneath this element. Background-image and
+  // background-color don't have to come from the same node, but as soon
+  // an element with background-color is found there's no need to continue
+  // because backgrounds farther up the chain won't be visible.
+  // (This implementation is not sophisticated enough to handle opacity,
+  // or multple layered partially-transparent background images.)
+  while ((ancestor = ancestor.parentNode) &&
+         ancestor.nodeType == goog.dom.NodeType.ELEMENT) {
+    var computedStyle = goog.cssom.iframe.style.getComputedStyleObject_(
+        /** @type {!Element} */ (ancestor));
+    // Copy background color if a non-transparent value is found.
+    var backgroundColorValue = computedStyle['backgroundColor'];
+    if (!goog.cssom.iframe.style.isTransparentValue_(backgroundColorValue)) {
+      propertyValues['backgroundColor'] = backgroundColorValue;
+    }
+    // If a background image value is found, copy background-image,
+    // background-repeat, and background-position.
+    if (computedStyle['backgroundImage'] &&
+        computedStyle['backgroundImage'] != 'none') {
+      propertyValues['backgroundImage'] = computedStyle['backgroundImage'];
+      propertyValues['backgroundRepeat'] = computedStyle['backgroundRepeat'];
+      // Calculate the offset between the original element and the element
+      // providing the background image, so the background position can be
+      // adjusted.
+      var relativePosition;
+      if (currentIframeWindow) {
+        relativePosition = goog.style.getFramedPageOffset(
+            element, currentIframeWindow);
+        var frameElement = currentIframeWindow.frameElement;
+        var iframeRelativePosition = goog.style.getRelativePosition(
+            /** @type {!Element} */ (frameElement),
+            /** @type {!Element} */ (ancestor));
+        var iframeBorders = goog.style.getBorderBox(frameElement);
+        relativePosition.x += iframeRelativePosition.x + iframeBorders.left;
+        relativePosition.y += iframeRelativePosition.y + iframeBorders.top;
+      } else {
+        relativePosition = goog.style.getRelativePosition(
+            element, /** @type {Element} */ (ancestor));
+      }
+      var backgroundXYValues = goog.cssom.iframe.style.getBackgroundXYValues_(
+          computedStyle);
+      // Parse background-repeat-* values in the form "10px", and adjust them.
+      for (var i = 0; i < 2; i++) {
+        var positionValue = backgroundXYValues[i];
+        var coordinate = i == 0 ? 'X' : 'Y';
+        var positionProperty = 'backgroundPosition' + coordinate;
+        // relative position to its ancestor.
+        var positionValueParts =
+            goog.cssom.iframe.style.valueWithUnitsRegEx_.exec(positionValue);
+        if (positionValueParts) {
+          var value = parseInt(
+              positionValueParts[1] + positionValueParts[2], 10);
+          var units = positionValueParts[3];
+          // This only attempts to handle pixel values for now (plus
+          // '0anything', which is equivalent to 0px).
+          // TODO(user) Convert non-pixel values to pixels when possible.
+          if (value == 0 || units == 'px') {
+            value -= (coordinate == 'X' ?
+                      relativePosition.x : relativePosition.y);
+          }
+          positionValue = value + units;
+        }
+        propertyValues[positionProperty] = positionValue;
+      }
+      propertyValues['backgroundPosition'] =
+          propertyValues['backgroundPositionX'] + ' ' +
+          propertyValues['backgroundPositionY'];
+    }
+    if (propertyValues['backgroundColor']) {
+      break;
+    }
+    if (ancestor.tagName == goog.dom.TagName.HTML) {
+      try {
+        currentIframeWindow = goog.dom.getWindow(
+            /** @type {Document} */ (ancestor.parentNode));
+        // This could theoretically throw a security exception if the parent
+        // iframe is in a different domain.
+        ancestor = currentIframeWindow.frameElement;
+        if (!ancestor) {
+          // Loop has reached the top level window.
+          break;
+        }
+      } catch (e) {
+        // We don't have permission to go up to the parent window, stop here.
+        break;
+      }
+    }
+  }
+  return propertyValues;
+};

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/cssom/iframe/style_test_import.css
----------------------------------------------------------------------
diff --git a/externs/GCL/externs/goog/cssom/iframe/style_test_import.css 
b/externs/GCL/externs/goog/cssom/iframe/style_test_import.css
new file mode 100644
index 0000000..d2c603f
--- /dev/null
+++ b/externs/GCL/externs/goog/cssom/iframe/style_test_import.css
@@ -0,0 +1,10 @@
+/*
+ * Copyright 2010 The Closure Library Authors. All Rights Reserved.
+ *
+ * Use of this source code is governed by the Apache License, Version 2.0.
+ * See the COPYING file for details.
+ */
+
+div div strong {
+  font-style: italic;
+}

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/datasource/datamanager.js
----------------------------------------------------------------------
diff --git a/externs/GCL/externs/goog/datasource/datamanager.js 
b/externs/GCL/externs/goog/datasource/datamanager.js
new file mode 100644
index 0000000..5eccd20
--- /dev/null
+++ b/externs/GCL/externs/goog/datasource/datamanager.js
@@ -0,0 +1,561 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview
+ * Central class for registering and accessing data sources
+ * Also handles processing of data events.
+ *
+ * There is a shared global instance that most client code should access via
+ * goog.ds.DataManager.getInstance(). However you can also create your own
+ * DataManager using new
+ *
+ * Implements DataNode to provide the top element in a data registry
+ * Prepends '$' to top level data names in path to denote they are root object
+ *
+ */
+goog.provide('goog.ds.DataManager');
+
+goog.require('goog.ds.BasicNodeList');
+goog.require('goog.ds.DataNode');
+goog.require('goog.ds.Expr');
+goog.require('goog.object');
+goog.require('goog.string');
+goog.require('goog.structs');
+goog.require('goog.structs.Map');
+
+
+
+/**
+ * Create a DataManger
+ * @extends {goog.ds.DataNode}
+ * @constructor
+ * @final
+ */
+goog.ds.DataManager = function() {
+  this.dataSources_ = new goog.ds.BasicNodeList();
+  this.autoloads_ = new goog.structs.Map();
+  this.listenerMap_ = {};
+  this.listenersByFunction_ = {};
+  this.aliases_ = {};
+  this.eventCount_ = 0;
+  this.indexedListenersByFunction_ = {};
+};
+
+
+/**
+ * Global instance
+ * @private
+ */
+goog.ds.DataManager.instance_ = null;
+goog.inherits(goog.ds.DataManager, goog.ds.DataNode);
+
+
+/**
+ * Get the global instance
+ * @return {!goog.ds.DataManager} The data manager singleton.
+ */
+goog.ds.DataManager.getInstance = function() {
+  if (!goog.ds.DataManager.instance_) {
+    goog.ds.DataManager.instance_ = new goog.ds.DataManager();
+  }
+  return goog.ds.DataManager.instance_;
+};
+
+
+/**
+ * Clears the global instance (for unit tests to reset state).
+ */
+goog.ds.DataManager.clearInstance = function() {
+  goog.ds.DataManager.instance_ = null;
+};
+
+
+/**
+ * Add a data source
+ * @param {goog.ds.DataNode} ds The data source.
+ * @param {boolean=} opt_autoload Whether to automatically load the data,
+ *   defaults to false.
+ * @param {string=} opt_name Optional name, can also get name
+ *   from the datasource.
+ */
+goog.ds.DataManager.prototype.addDataSource = function(ds, opt_autoload,
+    opt_name) {
+  var autoload = !!opt_autoload;
+  var name = opt_name || ds.getDataName();
+  if (!goog.string.startsWith(name, '$')) {
+    name = '$' + name;
+  }
+  ds.setDataName(name);
+  this.dataSources_.add(ds);
+  this.autoloads_.set(name, autoload);
+};
+
+
+/**
+ * Create an alias for a data path, very similar to assigning a variable.
+ * For example, you can set $CurrentContact -> $Request/Contacts[5], and all
+ * references to $CurrentContact will be procesed on $Request/Contacts[5].
+ *
+ * Aliases will hide datasources of the same name.
+ *
+ * @param {string} name Alias name, must be a top level path ($Foo).
+ * @param {string} dataPath Data path being aliased.
+ */
+goog.ds.DataManager.prototype.aliasDataSource = function(name, dataPath) {
+  if (!this.aliasListener_) {
+    this.aliasListener_ = goog.bind(this.listenForAlias_, this);
+  }
+  if (this.aliases_[name]) {
+    var oldPath = this.aliases_[name].getSource();
+    this.removeListeners(this.aliasListener_, oldPath + '/...', name);
+  }
+  this.aliases_[name] = goog.ds.Expr.create(dataPath);
+  this.addListener(this.aliasListener_, dataPath + '/...', name);
+  this.fireDataChange(name);
+};
+
+
+/**
+ * Listener function for matches of paths that have been aliased.
+ * Fires a data change on the alias as well.
+ *
+ * @param {string} dataPath Path of data event fired.
+ * @param {string} name Name of the alias.
+ * @private
+ */
+goog.ds.DataManager.prototype.listenForAlias_ = function(dataPath, name) {
+  var aliasedExpr = this.aliases_[name];
+
+  if (aliasedExpr) {
+    // If it's a subpath, appends the subpath to the alias name
+    // otherwise just fires on the top level alias
+    var aliasedPath = aliasedExpr.getSource();
+    if (dataPath.indexOf(aliasedPath) == 0) {
+      this.fireDataChange(name + dataPath.substring(aliasedPath.length));
+    } else {
+      this.fireDataChange(name);
+    }
+  }
+};
+
+
+/**
+ * Gets a named child node of the current node.
+ *
+ * @param {string} name The node name.
+ * @return {goog.ds.DataNode} The child node,
+ *   or null if no node of this name exists.
+ */
+goog.ds.DataManager.prototype.getDataSource = function(name) {
+  if (this.aliases_[name]) {
+    return this.aliases_[name].getNode();
+  } else {
+    return this.dataSources_.get(name);
+  }
+};
+
+
+/**
+ * Get the value of the node
+ * @return {!Object} The value of the node.
+ * @override
+ */
+goog.ds.DataManager.prototype.get = function() {
+  return this.dataSources_;
+};
+
+
+/** @override */
+goog.ds.DataManager.prototype.set = function(value) {
+  throw Error('Can\'t set on DataManager');
+};
+
+
+/** @override */
+goog.ds.DataManager.prototype.getChildNodes = function(opt_selector) {
+  if (opt_selector) {
+    return new goog.ds.BasicNodeList(
+        [this.getChildNode(/** @type {string} */(opt_selector))]);
+  } else {
+    return this.dataSources_;
+  }
+};
+
+
+/**
+ * Gets a named child node of the current node
+ * @param {string} name The node name.
+ * @return {goog.ds.DataNode} The child node,
+ *     or null if no node of this name exists.
+ * @override
+ */
+goog.ds.DataManager.prototype.getChildNode = function(name) {
+  return this.getDataSource(name);
+};
+
+
+/** @override */
+goog.ds.DataManager.prototype.getChildNodeValue = function(name) {
+  var ds = this.getDataSource(name);
+  return ds ? ds.get() : null;
+};
+
+
+/**
+ * Get the name of the node relative to the parent node
+ * @return {string} The name of the node.
+ * @override
+ */
+goog.ds.DataManager.prototype.getDataName = function() {
+  return '';
+};
+
+
+/**
+ * Gets the a qualified data path to this node
+ * @return {string} The data path.
+ * @override
+ */
+goog.ds.DataManager.prototype.getDataPath = function() {
+  return '';
+};
+
+
+/**
+ * Load or reload the backing data for this node
+ * only loads datasources flagged with autoload
+ * @override
+ */
+goog.ds.DataManager.prototype.load = function() {
+  var len = this.dataSources_.getCount();
+  for (var i = 0; i < len; i++) {
+    var ds = this.dataSources_.getByIndex(i);
+    var autoload = this.autoloads_.get(ds.getDataName());
+    if (autoload) {
+      ds.load();
+    }
+  }
+};
+
+
+/**
+ * Gets the state of the backing data for this node
+ * @return {goog.ds.LoadState} The state.
+ * @override
+ */
+goog.ds.DataManager.prototype.getLoadState = goog.abstractMethod;
+
+
+/**
+ * Whether the value of this node is a homogeneous list of data
+ * @return {boolean} True if a list.
+ * @override
+ */
+goog.ds.DataManager.prototype.isList = function() {
+  return false;
+};
+
+
+/**
+ * Get the total count of events fired (mostly for debugging)
+ * @return {number} Count of events.
+ */
+goog.ds.DataManager.prototype.getEventCount = function() {
+  return this.eventCount_;
+};
+
+
+/**
+ * Adds a listener
+ * Listeners should fire when any data with path that has dataPath as substring
+ * is changed.
+ * TODO(user) Look into better listener handling
+ *
+ * @param {Function} fn Callback function, signature function(dataPath, id).
+ * @param {string} dataPath Fully qualified data path.
+ * @param {string=} opt_id A value passed back to the listener when the 
dataPath
+ *   is matched.
+ */
+goog.ds.DataManager.prototype.addListener = function(fn, dataPath, opt_id) {
+  // maxAncestor sets how distant an ancestor you can be of the fired event
+  // and still fire (you always fire if you are a descendant).
+  // 0 means you don't fire if you are an ancestor
+  // 1 means you only fire if you are parent
+  // 1000 means you will fire if you are ancestor (effectively infinite)
+  var maxAncestors = 0;
+  if (goog.string.endsWith(dataPath, '/...')) {
+    maxAncestors = 1000;
+    dataPath = dataPath.substring(0, dataPath.length - 4);
+  } else if (goog.string.endsWith(dataPath, '/*')) {
+    maxAncestors = 1;
+    dataPath = dataPath.substring(0, dataPath.length - 2);
+  }
+
+  opt_id = opt_id || '';
+  var key = dataPath + ':' + opt_id + ':' + goog.getUid(fn);
+  var listener = {dataPath: dataPath, id: opt_id, fn: fn};
+  var expr = goog.ds.Expr.create(dataPath);
+
+  var fnUid = goog.getUid(fn);
+  if (!this.listenersByFunction_[fnUid]) {
+    this.listenersByFunction_[fnUid] = {};
+  }
+  this.listenersByFunction_[fnUid][key] = {listener: listener, items: []};
+
+  while (expr) {
+    var listenerSpec = {listener: listener, maxAncestors: maxAncestors};
+    var matchingListeners = this.listenerMap_[expr.getSource()];
+    if (matchingListeners == null) {
+      matchingListeners = {};
+      this.listenerMap_[expr.getSource()] = matchingListeners;
+    }
+    matchingListeners[key] = listenerSpec;
+    maxAncestors = 0;
+    expr = expr.getParent();
+    this.listenersByFunction_[fnUid][key].items.push(
+        {key: key, obj: matchingListeners});
+  }
+};
+
+
+/**
+ * Adds an indexed listener.
+ *
+ * Indexed listeners allow for '*' in data paths. If a * exists, will match
+ * all values and return the matched values in an array to the callback.
+ *
+ * Currently uses a promiscuous match algorithm: Matches everything before the
+ * first '*', and then does a regex match for all of the returned events.
+ * Although this isn't optimized, it is still an improvement as you can 
collapse
+ * 100's of listeners into a single regex match
+ *
+ * @param {Function} fn Callback function, signature (dataPath, id, indexes).
+ * @param {string} dataPath Fully qualified data path.
+ * @param {string=} opt_id A value passed back to the listener when the 
dataPath
+ *   is matched.
+ */
+goog.ds.DataManager.prototype.addIndexedListener = function(fn, dataPath,
+    opt_id) {
+  var firstStarPos = dataPath.indexOf('*');
+  // Just need a regular listener
+  if (firstStarPos == -1) {
+    this.addListener(fn, dataPath, opt_id);
+    return;
+  }
+
+  var listenPath = dataPath.substring(0, firstStarPos) + '...';
+
+  // Create regex that matches * to any non '\' character
+  var ext = '$';
+  if (goog.string.endsWith(dataPath, '/...')) {
+    dataPath = dataPath.substring(0, dataPath.length - 4);
+    ext = '';
+  }
+  var regExpPath = goog.string.regExpEscape(dataPath);
+  var matchRegExp = regExpPath.replace(/\\\*/g, '([^\\\/]+)') + ext;
+
+  // Matcher function applies the regex and calls back the original function
+  // if the regex matches, passing in an array of the matched values
+  var matchRegExpRe = new RegExp(matchRegExp);
+  var matcher = function(path, id) {
+    var match = matchRegExpRe.exec(path);
+    if (match) {
+      match.shift();
+      fn(path, opt_id, match);
+    }
+  };
+  this.addListener(matcher, listenPath, opt_id);
+
+  // Add the indexed listener to the map so that we can remove it later.
+  var fnUid = goog.getUid(fn);
+  if (!this.indexedListenersByFunction_[fnUid]) {
+    this.indexedListenersByFunction_[fnUid] = {};
+  }
+  var key = dataPath + ':' + opt_id;
+  this.indexedListenersByFunction_[fnUid][key] = {
+    listener: {dataPath: listenPath, fn: matcher, id: opt_id}
+  };
+};
+
+
+/**
+ * Removes indexed listeners with a given callback function, and optional
+ * matching datapath and matching id.
+ *
+ * @param {Function} fn Callback function, signature function(dataPath, id).
+ * @param {string=} opt_dataPath Fully qualified data path.
+ * @param {string=} opt_id A value passed back to the listener when the 
dataPath
+ *   is matched.
+ */
+goog.ds.DataManager.prototype.removeIndexedListeners = function(
+    fn, opt_dataPath, opt_id) {
+  this.removeListenersByFunction_(
+      this.indexedListenersByFunction_, true, fn, opt_dataPath, opt_id);
+};
+
+
+/**
+ * Removes listeners with a given callback function, and optional
+ * matching dataPath and matching id
+ *
+ * @param {Function} fn Callback function, signature function(dataPath, id).
+ * @param {string=} opt_dataPath Fully qualified data path.
+ * @param {string=} opt_id A value passed back to the listener when the 
dataPath
+ *   is matched.
+ */
+goog.ds.DataManager.prototype.removeListeners = function(fn, opt_dataPath,
+    opt_id) {
+
+  // Normalize data path root
+  if (opt_dataPath && goog.string.endsWith(opt_dataPath, '/...')) {
+    opt_dataPath = opt_dataPath.substring(0, opt_dataPath.length - 4);
+  } else if (opt_dataPath && goog.string.endsWith(opt_dataPath, '/*')) {
+    opt_dataPath = opt_dataPath.substring(0, opt_dataPath.length - 2);
+  }
+
+  this.removeListenersByFunction_(
+      this.listenersByFunction_, false, fn, opt_dataPath, opt_id);
+};
+
+
+/**
+ * Removes listeners with a given callback function, and optional
+ * matching dataPath and matching id from the given listenersByFunction
+ * data structure.
+ *
+ * @param {Object} listenersByFunction The listeners by function.
+ * @param {boolean} indexed Indicates whether the listenersByFunction are
+ *     indexed or not.
+ * @param {Function} fn Callback function, signature function(dataPath, id).
+ * @param {string=} opt_dataPath Fully qualified data path.
+ * @param {string=} opt_id A value passed back to the listener when the 
dataPath
+ *   is matched.
+ * @private
+ */
+goog.ds.DataManager.prototype.removeListenersByFunction_ = function(
+    listenersByFunction, indexed, fn, opt_dataPath, opt_id) {
+  var fnUid = goog.getUid(fn);
+  var functionMatches = listenersByFunction[fnUid];
+  if (functionMatches != null) {
+    for (var key in functionMatches) {
+      var functionMatch = functionMatches[key];
+      var listener = functionMatch.listener;
+      if ((!opt_dataPath || opt_dataPath == listener.dataPath) &&
+          (!opt_id || opt_id == listener.id)) {
+        if (indexed) {
+          this.removeListeners(
+              listener.fn, listener.dataPath, listener.id);
+        }
+        if (functionMatch.items) {
+          for (var i = 0; i < functionMatch.items.length; i++) {
+            var item = functionMatch.items[i];
+            delete item.obj[item.key];
+          }
+        }
+        delete functionMatches[key];
+      }
+    }
+  }
+};
+
+
+/**
+ * Get the total number of listeners (per expression listened to, so may be
+ * more than number of times addListener() has been called
+ * @return {number} Number of listeners.
+ */
+goog.ds.DataManager.prototype.getListenerCount = function() {
+  var count = 0;
+  goog.object.forEach(this.listenerMap_, function(matchingListeners) {
+    count += goog.structs.getCount(matchingListeners);
+  });
+  return count;
+};
+
+
+/**
+ * Disables the sending of all data events during the execution of the given
+ * callback. This provides a way to avoid useless notifications of small 
changes
+ * when you will eventually send a data event manually that encompasses them
+ * all.
+ *
+ * Note that this function can not be called reentrantly.
+ *
+ * @param {Function} callback Zero-arg function to execute.
+ */
+goog.ds.DataManager.prototype.runWithoutFiringDataChanges = function(callback) 
{
+  if (this.disableFiring_) {
+    throw Error('Can not nest calls to runWithoutFiringDataChanges');
+  }
+
+  this.disableFiring_ = true;
+  try {
+    callback();
+  } finally {
+    this.disableFiring_ = false;
+  }
+};
+
+
+/**
+ * Fire a data change event to all listeners
+ *
+ * If the path matches the path of a listener, the listener will fire
+ *
+ * If your path is the parent of a listener, the listener will fire. I.e.
+ * if $Contacts/[email protected] changes, then we will fire listener for
+ * $Contacts/[email protected]/Name as well, as the assumption is that when
+ * a parent changes, all children are invalidated.
+ *
+ * If your path is the child of a listener, the listener may fire, depending
+ * on the ancestor depth.
+ *
+ * A listener for $Contacts might only be interested if the contact name 
changes
+ * (i.e. $Contacts doesn't fire on $Contacts/[email protected]/Name),
+ * while a listener for a specific contact might
+ * (i.e. $Contacts/[email protected] would fire on $Contacts/[email protected]/Name).
+ * Adding "/..." to a lisetener path listens to all children, and adding "/*" 
to
+ * a listener path listens only to direct children
+ *
+ * @param {string} dataPath Fully qualified data path.
+ */
+goog.ds.DataManager.prototype.fireDataChange = function(dataPath) {
+  if (this.disableFiring_) {
+    return;
+  }
+
+  var expr = goog.ds.Expr.create(dataPath);
+  var ancestorDepth = 0;
+
+  // Look for listeners for expression and all its parents.
+  // Parents of listener expressions are all added to the listenerMap as well,
+  // so this will evaluate inner loop every time the dataPath is a child or
+  // an ancestor of the original listener path
+  while (expr) {
+    var matchingListeners = this.listenerMap_[expr.getSource()];
+    if (matchingListeners) {
+      for (var id in matchingListeners) {
+        var match = matchingListeners[id];
+        var listener = match.listener;
+        if (ancestorDepth <= match.maxAncestors) {
+          listener.fn(dataPath, listener.id);
+        }
+      }
+    }
+    ancestorDepth++;
+    expr = expr.getParent();
+  }
+  this.eventCount_++;
+};

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/datasource/datasource.js
----------------------------------------------------------------------
diff --git a/externs/GCL/externs/goog/datasource/datasource.js 
b/externs/GCL/externs/goog/datasource/datasource.js
new file mode 100644
index 0000000..4cb0db6
--- /dev/null
+++ b/externs/GCL/externs/goog/datasource/datasource.js
@@ -0,0 +1,658 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview Generic rich data access API.
+ *
+ * Abstraction for data sources that allows listening for changes at different
+ * levels of the data tree and updating the data via XHR requests
+ *
+ */
+
+
+goog.provide('goog.ds.BaseDataNode');
+goog.provide('goog.ds.BasicNodeList');
+goog.provide('goog.ds.DataNode');
+goog.provide('goog.ds.DataNodeList');
+goog.provide('goog.ds.EmptyNodeList');
+goog.provide('goog.ds.LoadState');
+goog.provide('goog.ds.SortedNodeList');
+goog.provide('goog.ds.Util');
+goog.provide('goog.ds.logger');
+
+goog.require('goog.array');
+goog.require('goog.log');
+
+
+
+/**
+ * Interface for node in rich data tree.
+ *
+ * Names that are reserved for system use and shouldn't be used for data node
+ * names: eval, toSource, toString, unwatch, valueOf, watch. Behavior is
+ * undefined if these names are used.
+ *
+ * @constructor
+ */
+goog.ds.DataNode = function() {};
+
+
+/**
+ * Get the value of the node
+ * @param {...?} var_args Do not check arity of arguments, because
+ *     some subclasses require args.
+ * @return {*} The value of the node, or null if no value.
+ */
+goog.ds.DataNode.prototype.get = goog.abstractMethod;
+
+
+/**
+ * Set the value of the node
+ * @param {*} value The new value of the node.
+ */
+goog.ds.DataNode.prototype.set = goog.abstractMethod;
+
+
+/**
+ * Gets all of the child nodes of the current node.
+ * Should return an empty DataNode list if no child nodes.
+ * @param {string=} opt_selector String selector to choose child nodes.
+ * @return {!goog.ds.DataNodeList} The child nodes.
+ */
+goog.ds.DataNode.prototype.getChildNodes = goog.abstractMethod;
+
+
+/**
+ * Gets a named child node of the current node
+ * @param {string} name The node name.
+ * @param {boolean=} opt_canCreate Whether to create a child node if it does 
not
+ *     exist.
+ * @return {goog.ds.DataNode} The child node, or null
+ * if no node of this name exists.
+ */
+goog.ds.DataNode.prototype.getChildNode = goog.abstractMethod;
+
+
+/**
+ * Gets the value of a child node
+ * @param {string} name The node name.
+ * @return {*} The value of the node, or null if no value or the child node
+ *     doesn't exist.
+ */
+goog.ds.DataNode.prototype.getChildNodeValue = goog.abstractMethod;
+
+
+/**
+ * Sets a named child node of the current node.
+ *
+ * @param {string} name The node name.
+ * @param {Object} value The value to set, can be DataNode, object, property,
+ *     or null. If value is null, removes the child node.
+ * @return {Object} The child node, if the node was set.
+ */
+goog.ds.DataNode.prototype.setChildNode = goog.abstractMethod;
+
+
+/**
+ * Get the name of the node relative to the parent node
+ * @return {string} The name of the node.
+ */
+goog.ds.DataNode.prototype.getDataName = goog.abstractMethod;
+
+
+/**
+ * Set the name of the node relative to the parent node
+ * @param {string} name The name of the node.
+ */
+goog.ds.DataNode.prototype.setDataName = goog.abstractMethod;
+
+
+/**
+ * Gets the a qualified data path to this node
+ * @return {string} The data path.
+ */
+goog.ds.DataNode.prototype.getDataPath = goog.abstractMethod;
+
+
+/**
+ * Load or reload the backing data for this node
+ */
+goog.ds.DataNode.prototype.load = goog.abstractMethod;
+
+
+/**
+ * Gets the state of the backing data for this node
+ * @return {goog.ds.LoadState} The state.
+ */
+goog.ds.DataNode.prototype.getLoadState = goog.abstractMethod;
+
+
+/**
+ * Whether the value of this node is a homogeneous list of data
+ * @return {boolean} True if a list.
+ */
+goog.ds.DataNode.prototype.isList = goog.abstractMethod;
+
+
+/**
+ * Enum for load state of a DataNode.
+ * @enum {string}
+ */
+goog.ds.LoadState = {
+  LOADED: 'LOADED',
+  LOADING: 'LOADING',
+  FAILED: 'FAILED',
+  NOT_LOADED: 'NOT_LOADED'
+};
+
+
+
+/**
+ * Base class for data node functionality, has default implementations for
+ * many of the functions.
+ *
+ * implements {goog.ds.DataNode}
+ * @constructor
+ */
+goog.ds.BaseDataNode = function() {};
+
+
+/**
+ * Set the value of the node
+ * @param {Object} value The new value of the node.
+ */
+goog.ds.BaseDataNode.prototype.set = goog.abstractMethod;
+
+
+/**
+ * Gets all of the child nodes of the current node.
+ * Should return an empty DataNode list if no child nodes.
+ * @param {string=} opt_selector String selector to choose child nodes.
+ * @return {!goog.ds.DataNodeList} The child nodes.
+ */
+goog.ds.BaseDataNode.prototype.getChildNodes = function(opt_selector) {
+  return new goog.ds.EmptyNodeList();
+};
+
+
+/**
+ * Gets a named child node of the current node
+ * @param {string} name The node name.
+ * @param {boolean=} opt_canCreate Whether you can create the child node if
+ *     it doesn't exist already.
+ * @return {goog.ds.DataNode} The child node, or null if no node of
+ *     this name exists and opt_create is false.
+ */
+goog.ds.BaseDataNode.prototype.getChildNode = function(name, opt_canCreate) {
+  return null;
+};
+
+
+/**
+ * Gets the value of a child node
+ * @param {string} name The node name.
+ * @return {Object} The value of the node, or null if no value or the
+ *     child node doesn't exist.
+ */
+goog.ds.BaseDataNode.prototype.getChildNodeValue = function(name) {
+  return null;
+};
+
+
+/**
+ * Get the name of the node relative to the parent node
+ * @return {string} The name of the node.
+ */
+goog.ds.BaseDataNode.prototype.getDataName = goog.abstractMethod;
+
+
+/**
+ * Gets the a qualified data path to this node
+ * @return {string} The data path.
+ */
+goog.ds.BaseDataNode.prototype.getDataPath = function() {
+  var parentPath = '';
+  var myName = this.getDataName();
+  if (this.getParent && this.getParent()) {
+    parentPath = this.getParent().getDataPath() +
+        (myName.indexOf(goog.ds.STR_ARRAY_START) != -1 ? '' :
+        goog.ds.STR_PATH_SEPARATOR);
+  }
+
+  return parentPath + myName;
+};
+
+
+/**
+ * Load or reload the backing data for this node
+ */
+goog.ds.BaseDataNode.prototype.load = goog.nullFunction;
+
+
+/**
+ * Gets the state of the backing data for this node
+ * @return {goog.ds.LoadState} The state.
+ */
+goog.ds.BaseDataNode.prototype.getLoadState = function() {
+  return goog.ds.LoadState.LOADED;
+};
+
+
+/**
+ * Gets the parent node. Subclasses implement this function
+ * @type {Function}
+ * @protected
+ */
+goog.ds.BaseDataNode.prototype.getParent = null;
+
+
+/**
+ * Interface for node list in rich data tree.
+ *
+ * Has both map and list-style accessors
+ *
+ * @constructor
+ * @extends {goog.ds.DataNode}
+ */
+// TODO(arv): Use interfaces when available.
+goog.ds.DataNodeList = function() {};
+
+
+/**
+ * Add a node to the node list.
+ * If the node has a dataName, uses this for the key in the map.
+ *
+ * @param {goog.ds.DataNode} node The node to add.
+ */
+goog.ds.DataNodeList.prototype.add = goog.abstractMethod;
+
+
+/**
+ * Get a node by string key.
+ * Returns null if node doesn't exist.
+ *
+ * @param {string} key String lookup key.
+ * @return {*} The node, or null if doesn't exist.
+ * @override
+ */
+goog.ds.DataNodeList.prototype.get = goog.abstractMethod;
+
+
+/**
+ * Get a node by index
+ * Returns null if the index is out of range
+ *
+ * @param {number} index The index of the node.
+ * @return {goog.ds.DataNode} The node, or null if doesn't exist.
+ */
+goog.ds.DataNodeList.prototype.getByIndex = goog.abstractMethod;
+
+
+/**
+ * Gets the size of the node list
+ *
+ * @return {number} The size of the list.
+ */
+goog.ds.DataNodeList.prototype.getCount = goog.abstractMethod;
+
+
+/**
+ * Sets a node in the list of a given name
+ * @param {string} name Name of the node.
+ * @param {goog.ds.DataNode} node The node.
+ */
+goog.ds.DataNodeList.prototype.setNode = goog.abstractMethod;
+
+
+/**
+ * Removes a node in the list of a given name
+ * @param {string} name Name of the node.
+ * @return {boolean} True if node existed and was deleted.
+ */
+goog.ds.DataNodeList.prototype.removeNode = goog.abstractMethod;
+
+
+/**
+ * Simple node list implementation with underlying array and map
+ * implements goog.ds.DataNodeList.
+ *
+ * Names that are reserved for system use and shouldn't be used for data node
+ * names: eval, toSource, toString, unwatch, valueOf, watch. Behavior is
+ * undefined if these names are used.
+ *
+ * @param {Array<goog.ds.DataNode>=} opt_nodes optional nodes to add to list.
+ * @constructor
+ * @extends {goog.ds.DataNodeList}
+ */
+// TODO(arv): Use interfaces when available.
+goog.ds.BasicNodeList = function(opt_nodes) {
+  this.map_ = {};
+  this.list_ = [];
+  this.indexMap_ = {};
+  if (opt_nodes) {
+    for (var i = 0, node; node = opt_nodes[i]; i++) {
+      this.add(node);
+    }
+  }
+};
+
+
+/**
+ * Add a node to the node list.
+ * If the node has a dataName, uses this for the key in the map.
+ * TODO(user) Remove function as well
+ *
+ * @param {goog.ds.DataNode} node The node to add.
+ * @override
+ */
+goog.ds.BasicNodeList.prototype.add = function(node) {
+  this.list_.push(node);
+  var dataName = node.getDataName();
+  if (dataName) {
+    this.map_[dataName] = node;
+    this.indexMap_[dataName] = this.list_.length - 1;
+  }
+};
+
+
+/**
+ * Get a node by string key.
+ * Returns null if node doesn't exist.
+ *
+ * @param {string} key String lookup key.
+ * @return {goog.ds.DataNode} The node, or null if doesn't exist.
+ * @override
+ */
+goog.ds.BasicNodeList.prototype.get = function(key) {
+  return this.map_[key] || null;
+};
+
+
+/**
+ * Get a node by index
+ * Returns null if the index is out of range
+ *
+ * @param {number} index The index of the node.
+ * @return {goog.ds.DataNode} The node, or null if doesn't exist.
+ * @override
+ */
+goog.ds.BasicNodeList.prototype.getByIndex = function(index) {
+  return this.list_[index] || null;
+};
+
+
+/**
+ * Gets the size of the node list
+ *
+ * @return {number} The size of the list.
+ * @override
+ */
+goog.ds.BasicNodeList.prototype.getCount = function() {
+  return this.list_.length;
+};
+
+
+/**
+ * Sets a node in the list of a given name
+ * @param {string} name Name of the node.
+ * @param {goog.ds.DataNode} node The node.
+ * @override
+ */
+goog.ds.BasicNodeList.prototype.setNode = function(name, node) {
+  if (node == null) {
+    this.removeNode(name);
+  } else {
+    var existingNode = this.indexMap_[name];
+    if (existingNode != null) {
+      this.map_[name] = node;
+      this.list_[existingNode] = node;
+    } else {
+      this.add(node);
+    }
+  }
+};
+
+
+/**
+ * Removes a node in the list of a given name
+ * @param {string} name Name of the node.
+ * @return {boolean} True if node existed and was deleted.
+ * @override
+ */
+goog.ds.BasicNodeList.prototype.removeNode = function(name) {
+  var existingNode = this.indexMap_[name];
+  if (existingNode != null) {
+    this.list_.splice(existingNode, 1);
+    delete this.map_[name];
+    delete this.indexMap_[name];
+    for (var index in this.indexMap_) {
+      if (this.indexMap_[index] > existingNode) {
+        this.indexMap_[index]--;
+      }
+    }
+  }
+  return existingNode != null;
+};
+
+
+/**
+ * Get the index of a named node
+ * @param {string} name The name of the node to get the index of.
+ * @return {number|undefined} The index.
+ */
+goog.ds.BasicNodeList.prototype.indexOf = function(name) {
+  return this.indexMap_[name];
+};
+
+
+/**
+ * Immulatable empty node list
+ * @extends {goog.ds.BasicNodeList}
+ * @constructor
+ * @final
+ */
+
+goog.ds.EmptyNodeList = function() {
+  goog.ds.BasicNodeList.call(this);
+};
+goog.inherits(goog.ds.EmptyNodeList, goog.ds.BasicNodeList);
+
+
+/**
+ * Add a node to the node list.
+ * If the node has a dataName, uses this for the key in the map.
+ *
+ * @param {goog.ds.DataNode} node The node to add.
+ * @override
+ */
+goog.ds.EmptyNodeList.prototype.add = function(node) {
+  throw Error('Can\'t add to EmptyNodeList');
+};
+
+
+
+/**
+ * Node list implementation which maintains sort order during insertion and
+ * modification operations based on a comparison function.
+ *
+ * The SortedNodeList does not guarantee sort order will be maintained if
+ * the underlying data nodes are modified externally.
+ *
+ * Names that are reserved for system use and shouldn't be used for data node
+ * names: eval, toSource, toString, unwatch, valueOf, watch. Behavior is
+ * undefined if these names are used.
+ *
+ * @param {Function} compareFn Comparison function by which the
+ *     node list is sorted. Should take 2 arguments to compare, and return a
+ *     negative integer, zero, or a positive integer depending on whether the
+ *     first argument is less than, equal to, or greater than the second.
+ * @param {Array<goog.ds.DataNode>=} opt_nodes optional nodes to add to list;
+ *    these are assumed to be in sorted order.
+ * @extends {goog.ds.BasicNodeList}
+ * @constructor
+ */
+goog.ds.SortedNodeList = function(compareFn, opt_nodes) {
+  this.compareFn_ = compareFn;
+  goog.ds.BasicNodeList.call(this, opt_nodes);
+};
+goog.inherits(goog.ds.SortedNodeList, goog.ds.BasicNodeList);
+
+
+/**
+ * Add a node to the node list, maintaining sort order.
+ * If the node has a dataName, uses this for the key in the map.
+ *
+ * @param {goog.ds.DataNode} node The node to add.
+ * @override
+ */
+goog.ds.SortedNodeList.prototype.add = function(node) {
+  if (!this.compareFn_) {
+    this.append(node);
+    return;
+  }
+
+  var searchLoc = goog.array.binarySearch(this.list_, node, this.compareFn_);
+
+  // if there is another node that is "equal" according to the comparison
+  // function, insert before that one; otherwise insert at the location
+  // goog.array.binarySearch indicated
+  if (searchLoc < 0) {
+    searchLoc = -(searchLoc + 1);
+  }
+
+  // update any indexes that are after the insertion point
+  for (var index in this.indexMap_) {
+    if (this.indexMap_[index] >= searchLoc) {
+      this.indexMap_[index]++;
+    }
+  }
+
+  goog.array.insertAt(this.list_, node, searchLoc);
+  var dataName = node.getDataName();
+  if (dataName) {
+    this.map_[dataName] = node;
+    this.indexMap_[dataName] = searchLoc;
+  }
+};
+
+
+/**
+ * Adds the given node to the end of the SortedNodeList. This should
+ * only be used when the caller can guarantee that the sort order will
+ * be maintained according to this SortedNodeList's compareFn (e.g.
+ * when initializing a new SortedNodeList from a list of nodes that has
+ * already been sorted).
+ * @param {goog.ds.DataNode} node The node to append.
+ */
+goog.ds.SortedNodeList.prototype.append = function(node) {
+  goog.ds.SortedNodeList.superClass_.add.call(this, node);
+};
+
+
+/**
+ * Sets a node in the list of a given name, maintaining sort order.
+ * @param {string} name Name of the node.
+ * @param {goog.ds.DataNode} node The node.
+ * @override
+ */
+goog.ds.SortedNodeList.prototype.setNode = function(name, node) {
+  if (node == null) {
+    this.removeNode(name);
+  } else {
+    var existingNode = this.indexMap_[name];
+    if (existingNode != null) {
+      if (this.compareFn_) {
+        var compareResult = this.compareFn_(this.list_[existingNode], node);
+        if (compareResult == 0) {
+          // the new node can just replace the old one
+          this.map_[name] = node;
+          this.list_[existingNode] = node;
+        } else {
+          // remove the old node, then add the new one
+          this.removeNode(name);
+          this.add(node);
+        }
+      }
+    } else {
+      this.add(node);
+    }
+  }
+};
+
+
+/**
+ * The character denoting an attribute.
+ * @type {string}
+ */
+goog.ds.STR_ATTRIBUTE_START = '@';
+
+
+/**
+ * The character denoting all children.
+ * @type {string}
+ */
+goog.ds.STR_ALL_CHILDREN_SELECTOR = '*';
+
+
+/**
+ * The wildcard character.
+ * @type {string}
+ */
+goog.ds.STR_WILDCARD = '*';
+
+
+/**
+ * The character denoting path separation.
+ * @type {string}
+ */
+goog.ds.STR_PATH_SEPARATOR = '/';
+
+
+/**
+ * The character denoting the start of an array.
+ * @type {string}
+ */
+goog.ds.STR_ARRAY_START = '[';
+
+
+/**
+ * Shared logger instance for data package
+ * @type {goog.log.Logger}
+ */
+goog.ds.logger = goog.log.getLogger('goog.ds');
+
+
+/**
+ * Create a data node that references another data node,
+ * useful for pointer-like functionality.
+ * All functions will return same values as the original node except for
+ * getDataName()
+ * @param {!goog.ds.DataNode} node The original node.
+ * @param {string} name The new name.
+ * @return {!goog.ds.DataNode} The new data node.
+ */
+goog.ds.Util.makeReferenceNode = function(node, name) {
+  /**
+   * @constructor
+   * @extends {goog.ds.DataNode}
+   * @final
+   */
+  var nodeCreator = function() {};
+  nodeCreator.prototype = node;
+  var newNode = new nodeCreator();
+  newNode.getDataName = function() {
+    return name;
+  };
+  return newNode;
+};

http://git-wip-us.apache.org/repos/asf/flex-falcon/blob/e2cad6e6/externs/GCL/externs/goog/datasource/expr.js
----------------------------------------------------------------------
diff --git a/externs/GCL/externs/goog/datasource/expr.js 
b/externs/GCL/externs/goog/datasource/expr.js
new file mode 100644
index 0000000..c384fd1
--- /dev/null
+++ b/externs/GCL/externs/goog/datasource/expr.js
@@ -0,0 +1,545 @@
+// Copyright 2006 The Closure Library Authors. All Rights Reserved.
+//
+// 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.
+
+/**
+ * @fileoverview
+ * Expression evaluation utilities. Expression format is very similar to XPath.
+ *
+ * Expression details:
+ * - Of format A/B/C, which will evaluate getChildNode('A').getChildNode('B').
+ *    getChildNodes('C')|getChildNodeValue('C')|getChildNode('C') depending on
+ *    call
+ * - If expression ends with '/name()', will get the name() of the node
+ *    referenced by the preceding path.
+ * - If expression ends with '/count()', will get the count() of the nodes that
+ *    match the expression referenced by the preceding path.
+ * - If expression ends with '?', the value is OK to evaluate to null. This is
+ *    not enforced by the expression evaluation functions, instead it is
+ *    provided as a flag for client code which may ignore depending on usage
+ * - If expression has [INDEX], will use getChildNodes().getByIndex(INDEX)
+ *
+ */
+
+
+goog.provide('goog.ds.Expr');
+
+goog.require('goog.ds.BasicNodeList');
+goog.require('goog.ds.EmptyNodeList');
+goog.require('goog.string');
+
+
+
+/**
+ * Create a new expression. An expression uses a string expression language, 
and
+ * from this string and a passed in DataNode can evaluate to a value, DataNode,
+ * or a DataNodeList.
+ *
+ * @param {string=} opt_expr The string expression.
+ * @constructor
+ * @final
+ */
+goog.ds.Expr = function(opt_expr) {
+  if (opt_expr) {
+    this.setSource_(opt_expr);
+  }
+};
+
+
+/**
+ * Set the source expression text & parse
+ *
+ * @param {string} expr The string expression source.
+ * @param {Array=} opt_parts Array of the parts of an expression.
+ * @param {goog.ds.Expr=} opt_childExpr Optional child of this expression,
+ *   passed in as a hint for processing.
+ * @param {goog.ds.Expr=} opt_prevExpr Optional preceding expression
+ *   (i.e. $A/B/C is previous expression to B/C) passed in as a hint for
+ *   processing.
+ * @private
+ */
+goog.ds.Expr.prototype.setSource_ = function(expr, opt_parts,
+    opt_childExpr, opt_prevExpr) {
+  this.src_ = expr;
+
+  if (!opt_childExpr && !opt_prevExpr) {
+    // Check whether it can be empty
+    if (goog.string.endsWith(expr, goog.ds.Expr.String_.CAN_BE_EMPTY)) {
+      this.canBeEmpty_ = true;
+      expr = expr.substring(0, expr.length - 1);
+    }
+
+    // Check whether this is an node function
+    if (goog.string.endsWith(expr, '()')) {
+      if (goog.string.endsWith(expr, goog.ds.Expr.String_.NAME_EXPR) ||
+          goog.string.endsWith(expr, goog.ds.Expr.String_.COUNT_EXPR) ||
+          goog.string.endsWith(expr, goog.ds.Expr.String_.POSITION_EXPR)) {
+        var lastPos = expr.lastIndexOf(goog.ds.Expr.String_.SEPARATOR);
+        if (lastPos != -1) {
+          this.exprFn_ = expr.substring(lastPos + 1);
+          expr = expr.substring(0, lastPos);
+        } else {
+          this.exprFn_ = expr;
+          expr = goog.ds.Expr.String_.CURRENT_NODE_EXPR;
+        }
+        if (this.exprFn_ == goog.ds.Expr.String_.COUNT_EXPR) {
+          this.isCount_ = true;
+        }
+      }
+    }
+  }
+
+  // Split into component parts
+  this.parts_ = opt_parts || expr.split('/');
+  this.size_ = this.parts_.length;
+  this.last_ = this.parts_[this.size_ - 1];
+  this.root_ = this.parts_[0];
+
+  if (this.size_ == 1) {
+    this.rootExpr_ = this;
+    this.isAbsolute_ = goog.string.startsWith(expr, '$');
+  } else {
+    this.rootExpr_ = goog.ds.Expr.createInternal_(this.root_, null,
+        this, null);
+    this.isAbsolute_ = this.rootExpr_.isAbsolute_;
+    this.root_ = this.rootExpr_.root_;
+  }
+
+  if (this.size_ == 1 && !this.isAbsolute_) {
+    // Check whether expression maps to current node, for convenience
+    this.isCurrent_ = (expr == goog.ds.Expr.String_.CURRENT_NODE_EXPR ||
+        expr == goog.ds.Expr.String_.EMPTY_EXPR);
+
+    // Whether this expression is just an attribute (i.e. '@foo')
+    this.isJustAttribute_ =
+        goog.string.startsWith(expr, goog.ds.Expr.String_.ATTRIBUTE_START);
+
+    // Check whether this is a common node expression
+    this.isAllChildNodes_ = expr == goog.ds.Expr.String_.ALL_CHILD_NODES_EXPR;
+    this.isAllAttributes_ = expr == goog.ds.Expr.String_.ALL_ATTRIBUTES_EXPR;
+    this.isAllElements_ = expr == goog.ds.Expr.String_.ALL_ELEMENTS_EXPR;
+  }
+};
+
+
+/**
+ * Get the source data path for the expression
+ * @return {string} The path.
+ */
+goog.ds.Expr.prototype.getSource = function() {
+  return this.src_;
+};
+
+
+/**
+ * Gets the last part of the expression.
+ * @return {?string} Last part of the expression.
+ */
+goog.ds.Expr.prototype.getLast = function() {
+  return this.last_;
+};
+
+
+/**
+ * Gets the parent expression of this expression, or null if this is top level
+ * @return {goog.ds.Expr} The parent.
+ */
+goog.ds.Expr.prototype.getParent = function() {
+  if (!this.parentExprSet_) {
+    if (this.size_ > 1) {
+      this.parentExpr_ = goog.ds.Expr.createInternal_(null,
+          this.parts_.slice(0, this.parts_.length - 1), this, null);
+    }
+    this.parentExprSet_ = true;
+  }
+  return this.parentExpr_;
+};
+
+
+/**
+ * Gets the parent expression of this expression, or null if this is top level
+ * @return {goog.ds.Expr} The parent.
+ */
+goog.ds.Expr.prototype.getNext = function() {
+  if (!this.nextExprSet_) {
+    if (this.size_ > 1) {
+      this.nextExpr_ = goog.ds.Expr.createInternal_(null, this.parts_.slice(1),
+          null, this);
+    }
+    this.nextExprSet_ = true;
+  }
+  return this.nextExpr_;
+};
+
+
+/**
+ * Evaluate an expression on a data node, and return a value
+ * Recursively walks through child nodes to evaluate
+ * TODO(user) Support other expression functions
+ *
+ * @param {goog.ds.DataNode=} opt_ds Optional datasource to evaluate against.
+ *     If not provided, evaluates against DataManager global root.
+ * @return {*} Value of the node, or null if doesn't exist.
+ */
+goog.ds.Expr.prototype.getValue = function(opt_ds) {
+  if (opt_ds == null) {
+    opt_ds = goog.ds.DataManager.getInstance();
+  } else if (this.isAbsolute_) {
+    opt_ds = opt_ds.getDataRoot ? opt_ds.getDataRoot() :
+        goog.ds.DataManager.getInstance();
+  }
+
+  if (this.isCount_) {
+    var nodes = this.getNodes(opt_ds);
+    return nodes.getCount();
+  }
+
+  if (this.size_ == 1) {
+    return opt_ds.getChildNodeValue(this.root_);
+  } else if (this.size_ == 0) {
+    return opt_ds.get();
+  }
+
+  var nextDs = opt_ds.getChildNode(this.root_);
+
+  if (nextDs == null) {
+    return null;
+  } else {
+    return this.getNext().getValue(nextDs);
+  }
+};
+
+
+/**
+ * Evaluate an expression on a data node, and return matching nodes
+ * Recursively walks through child nodes to evaluate
+ *
+ * @param {goog.ds.DataNode=} opt_ds Optional datasource to evaluate against.
+ *     If not provided, evaluates against data root.
+ * @param {boolean=} opt_canCreate If true, will try to create new nodes.
+ * @return {goog.ds.DataNodeList} Matching nodes.
+ */
+goog.ds.Expr.prototype.getNodes = function(opt_ds, opt_canCreate) {
+  return /** @type {goog.ds.DataNodeList} */(this.getNodes_(opt_ds,
+      false, opt_canCreate));
+};
+
+
+/**
+ * Evaluate an expression on a data node, and return the first matching node
+ * Recursively walks through child nodes to evaluate
+ *
+ * @param {goog.ds.DataNode=} opt_ds Optional datasource to evaluate against.
+ *     If not provided, evaluates against DataManager global root.
+ * @param {boolean=} opt_canCreate If true, will try to create new nodes.
+ * @return {goog.ds.DataNode} Matching nodes, or null if doesn't exist.
+ */
+goog.ds.Expr.prototype.getNode = function(opt_ds, opt_canCreate) {
+  return /** @type {goog.ds.DataNode} */(this.getNodes_(opt_ds,
+      true, opt_canCreate));
+};
+
+
+/**
+ * Evaluate an expression on a data node, and return the first matching node
+ * Recursively walks through child nodes to evaluate
+ *
+ * @param {goog.ds.DataNode=} opt_ds Optional datasource to evaluate against.
+ *     If not provided, evaluates against DataManager global root.
+ * @param {boolean=} opt_selectOne Whether to return single matching DataNode
+ *     or matching nodes in DataNodeList.
+ * @param {boolean=} opt_canCreate If true, will try to create new nodes.
+ * @return {goog.ds.DataNode|goog.ds.DataNodeList} Matching node or nodes,
+ *     depending on value of opt_selectOne.
+ * @private
+ */
+goog.ds.Expr.prototype.getNodes_ = function(opt_ds, opt_selectOne,
+    opt_canCreate) {
+  if (opt_ds == null) {
+    opt_ds = goog.ds.DataManager.getInstance();
+  } else if (this.isAbsolute_) {
+    opt_ds = opt_ds.getDataRoot ? opt_ds.getDataRoot() :
+        goog.ds.DataManager.getInstance();
+  }
+
+  if (this.size_ == 0 && opt_selectOne) {
+      return opt_ds;
+  } else if (this.size_ == 0 && !opt_selectOne) {
+    return new goog.ds.BasicNodeList([opt_ds]);
+  } else if (this.size_ == 1) {
+    if (opt_selectOne) {
+      return opt_ds.getChildNode(this.root_, opt_canCreate);
+    }
+    else {
+      var possibleListChild = opt_ds.getChildNode(this.root_);
+      if (possibleListChild && possibleListChild.isList()) {
+        return possibleListChild.getChildNodes();
+      } else {
+        return opt_ds.getChildNodes(this.root_);
+      }
+    }
+  } else {
+    var nextDs = opt_ds.getChildNode(this.root_, opt_canCreate);
+    if (nextDs == null && opt_selectOne) {
+      return null;
+    } else if (nextDs == null && !opt_selectOne) {
+      return new goog.ds.EmptyNodeList();
+    }
+    return this.getNext().getNodes_(nextDs, opt_selectOne, opt_canCreate);
+  }
+};
+
+
+/**
+ * Whether the expression can be null.
+ *
+ * @type {boolean}
+ * @private
+ */
+goog.ds.Expr.prototype.canBeEmpty_ = false;
+
+
+/**
+ * The parsed paths in the expression
+ *
+ * @type {Array<string>}
+ * @private
+ */
+goog.ds.Expr.prototype.parts_ = [];
+
+
+/**
+ * Number of paths in the expression
+ *
+ * @type {?number}
+ * @private
+ */
+goog.ds.Expr.prototype.size_ = null;
+
+
+/**
+ * The root node path in the expression
+ *
+ * @type {string}
+ * @private
+ */
+goog.ds.Expr.prototype.root_;
+
+
+/**
+ * The last path in the expression
+ *
+ * @type {?string}
+ * @private
+ */
+goog.ds.Expr.prototype.last_ = null;
+
+
+/**
+ * Whether the expression evaluates to current node
+ *
+ * @type {boolean}
+ * @private
+ */
+goog.ds.Expr.prototype.isCurrent_ = false;
+
+
+/**
+ * Whether the expression is just an attribute
+ *
+ * @type {boolean}
+ * @private
+ */
+goog.ds.Expr.prototype.isJustAttribute_ = false;
+
+
+/**
+ * Does this expression select all DOM-style child nodes (element and text)
+ *
+ * @type {boolean}
+ * @private
+ */
+goog.ds.Expr.prototype.isAllChildNodes_ = false;
+
+
+/**
+ * Does this expression select all DOM-style attribute nodes (starts with '@')
+ *
+ * @type {boolean}
+ * @private
+ */
+goog.ds.Expr.prototype.isAllAttributes_ = false;
+
+
+/**
+ * Does this expression select all DOM-style element child nodes
+ *
+ * @type {boolean}
+ * @private
+ */
+goog.ds.Expr.prototype.isAllElements_ = false;
+
+
+/**
+ * The function used by this expression
+ *
+ * @type {?string}
+ * @private
+ */
+goog.ds.Expr.prototype.exprFn_ = null;
+
+
+/**
+ * Cached value for the parent expression.
+ * @type {goog.ds.Expr?}
+ * @private
+ */
+goog.ds.Expr.prototype.parentExpr_ = null;
+
+
+/**
+ * Cached value for the next expression.
+ * @type {goog.ds.Expr?}
+ * @private
+ */
+goog.ds.Expr.prototype.nextExpr_ = null;
+
+
+/**
+ * Create an expression from a string, can use cached values
+ *
+ * @param {string} expr The expression string.
+ * @return {goog.ds.Expr} The expression object.
+ */
+goog.ds.Expr.create = function(expr) {
+  var result = goog.ds.Expr.cache_[expr];
+
+  if (result == null) {
+    result = new goog.ds.Expr(expr);
+    goog.ds.Expr.cache_[expr] = result;
+  }
+  return result;
+};
+
+
+/**
+ * Create an expression from a string, can use cached values
+ * Uses hints from related expressions to help in creation
+ *
+ * @param {?string=} opt_expr The string expression source.
+ * @param {Array=} opt_parts Array of the parts of an expression.
+ * @param {goog.ds.Expr=} opt_childExpr Optional child of this expression,
+ *   passed in as a hint for processing.
+ * @param {goog.ds.Expr=} opt_prevExpr Optional preceding expression
+ *   (i.e. $A/B/C is previous expression to B/C) passed in as a hint for
+ *   processing.
+ * @return {goog.ds.Expr} The expression object.
+ * @private
+ */
+goog.ds.Expr.createInternal_ = function(opt_expr, opt_parts, opt_childExpr,
+    opt_prevExpr) {
+  var expr = opt_expr || opt_parts.join('/');
+  var result = goog.ds.Expr.cache_[expr];
+
+  if (result == null) {
+    result = new goog.ds.Expr();
+    result.setSource_(expr, opt_parts, opt_childExpr, opt_prevExpr);
+    goog.ds.Expr.cache_[expr] = result;
+  }
+  return result;
+};
+
+
+/**
+ * Cache of pre-parsed expressions
+ * @private
+ */
+goog.ds.Expr.cache_ = {};
+
+
+/**
+ * Commonly used strings in expressions.
+ * @enum {string}
+ * @private
+ */
+goog.ds.Expr.String_ = {
+  SEPARATOR: '/',
+  CURRENT_NODE_EXPR: '.',
+  EMPTY_EXPR: '',
+  ATTRIBUTE_START: '@',
+  ALL_CHILD_NODES_EXPR: '*|text()',
+  ALL_ATTRIBUTES_EXPR: '@*',
+  ALL_ELEMENTS_EXPR: '*',
+  NAME_EXPR: 'name()',
+  COUNT_EXPR: 'count()',
+  POSITION_EXPR: 'position()',
+  INDEX_START: '[',
+  INDEX_END: ']',
+  CAN_BE_EMPTY: '?'
+};
+
+
+/**
+ * Standard expressions
+ */
+
+
+/**
+ * The current node
+ */
+goog.ds.Expr.CURRENT = goog.ds.Expr.create(
+    goog.ds.Expr.String_.CURRENT_NODE_EXPR);
+
+
+/**
+ * For DOM interop - all DOM child nodes (text + element).
+ * Text nodes have dataName #text
+ */
+goog.ds.Expr.ALL_CHILD_NODES =
+    goog.ds.Expr.create(goog.ds.Expr.String_.ALL_CHILD_NODES_EXPR);
+
+
+/**
+ * For DOM interop - all DOM element child nodes
+ */
+goog.ds.Expr.ALL_ELEMENTS =
+    goog.ds.Expr.create(goog.ds.Expr.String_.ALL_ELEMENTS_EXPR);
+
+
+/**
+ * For DOM interop - all DOM attribute nodes
+ * Attribute nodes have dataName starting with "@"
+ */
+goog.ds.Expr.ALL_ATTRIBUTES =
+    goog.ds.Expr.create(goog.ds.Expr.String_.ALL_ATTRIBUTES_EXPR);
+
+
+/**
+ * Get the dataName of a node
+ */
+goog.ds.Expr.NAME = goog.ds.Expr.create(goog.ds.Expr.String_.NAME_EXPR);
+
+
+/**
+ * Get the count of nodes matching an expression
+ */
+goog.ds.Expr.COUNT = goog.ds.Expr.create(goog.ds.Expr.String_.COUNT_EXPR);
+
+
+/**
+ * Get the position of the "current" node in the current node list
+ * This will only apply for datasources that support the concept of a current
+ * node (none exist yet). This is similar to XPath position() and concept of
+ * current node
+ */
+goog.ds.Expr.POSITION = 
goog.ds.Expr.create(goog.ds.Expr.String_.POSITION_EXPR);

Reply via email to