text: 
  (function() {
  
    Polymer('core-overlay', {
  
      publish: {
        /**
         * The target element that will be shown when the overlay is 
         * opened. If unspecified, the core-overlay itself is the target.
         *
         * @attribute target
         * @type Object
         * @default the overlay element
         */
        target: null,
  
  
        /**
         * A `core-overlay`'s size is guaranteed to be 
         * constrained to the window size. To achieve this, the sizingElement
         * is sized with a max-height/width. By default this element is the 
         * target element, but it can be specifically set to a specific element
         * inside the target if that is more appropriate. This is useful, for 
         * example, when a region inside the overlay should scroll if needed.
         *
         * @attribute sizingTarget
         * @type Object
         * @default the target element
         */
        sizingTarget: null,
      
        /**
         * Set opened to true to show an overlay and to false to hide it.
         * A `core-overlay` may be made initially opened by setting its
         * `opened` attribute.
         * @attribute opened
         * @type boolean
         * @default false
         */
        opened: false,
  
        /**
         * If true, the overlay has a backdrop darkening the rest of the screen.
         * The backdrop element is attached to the document body and may be styled
         * with the class `core-overlay-backdrop`. When opened the `core-opened`
         * class is applied.
         *
         * @attribute backdrop
         * @type boolean
         * @default false
         */    
        backdrop: false,
  
        /**
         * If true, the overlay is guaranteed to display above page content.
         *
         * @attribute layered
         * @type boolean
         * @default false
        */
        layered: false,
      
        /**
         * By default an overlay will close automatically if the user
         * taps outside it or presses the escape key. Disable this
         * behavior by setting the `autoCloseDisabled` property to true.
         * @attribute autoCloseDisabled
         * @type boolean
         * @default false
         */
        autoCloseDisabled: false,
  
        /**
         * By default an overlay will focus its target or an element inside
         * it with the `autoFocus` attribute. Disable this
         * behavior by setting the `autoFocusDisabled` property to true.
         * @attribute autoFocusDisabled
         * @type boolean
         * @default false
         */
        autoFocusDisabled: false,
  
        /**
         * This property specifies an attribute on elements that should
         * close the overlay on tap. Should not set `closeSelector` if this
         * is set.
         *
         * @attribute closeAttribute
         * @type string
         * @default "core-overlay-toggle"
         */
        closeAttribute: 'core-overlay-toggle',
  
        /**
         * This property specifies a selector matching elements that should
         * close the overlay on tap. Should not set `closeAttribute` if this
         * is set.
         *
         * @attribute closeSelector
         * @type string
         * @default ""
         */
        closeSelector: '',
  
        /**
         * The transition property specifies a string which identifies a 
         * <a href="../core-transition/">`core-transition`</a> element that 
         * will be used to help the overlay open and close. The default
         * `core-transition-fade` will cause the overlay to fade in and out.
         *
         * @attribute transition
         * @type string
         * @default 'core-transition-fade'
         */
        transition: 'core-transition-fade'
  
      },
  
      captureEventName: 'tap',
      targetListeners: {
        'tap': 'tapHandler',
        'keydown': 'keydownHandler',
        'core-transitionend': 'transitionend'
      },
      
      registerCallback: function(element) {
        this.layer = document.createElement('core-overlay-layer');
        this.keyHelper = document.createElement('core-key-helper');
        this.meta = document.createElement('core-transition');
        this.scrim = document.createElement('div');
        this.scrim.className = 'core-overlay-backdrop';
      },
  
      ready: function() {
        this.target = this.target || this;
        // flush to ensure styles are installed before paint
        Platform.flush();
      },
  
      /** 
       * Toggle the opened state of the overlay.
       * @method toggle
       */
      toggle: function() {
        this.opened = !this.opened;
      },
  
      /** 
       * Open the overlay. This is equivalent to setting the `opened`
       * property to true.
       * @method open
       */
      open: function() {
        this.opened = true;
      },
  
      /** 
       * Close the overlay. This is equivalent to setting the `opened` 
       * property to false.
       * @method close
       */
      close: function() {
        this.opened = false;
      },
  
      domReady: function() {
        this.ensureTargetSetup();
      },
  
      targetChanged: function(old) {
        if (this.target) {
          // really make sure tabIndex is set
          if (this.target.tabIndex < 0) {
            this.target.tabIndex = -1;
          }
          this.addElementListenerList(this.target, this.targetListeners);
          this.target.style.display = 'none';
          this.target.__overlaySetup = false;
        }
        if (old) {
          this.removeElementListenerList(old, this.targetListeners);
          var transition = this.getTransition();
          if (transition) {
            transition.teardown(old);
          } else {
            old.style.position = '';
            old.style.outline = '';
          }
          old.style.display = '';
        }
      },
  
      transitionChanged: function(old) {
        if (!this.target) {
          return;
        }
        if (old) {
          this.getTransition(old).teardown(this.target);
        }
        this.target.__overlaySetup = false;
      },
  
      // NOTE: wait to call this until we're as sure as possible that target
      // is styled.
      ensureTargetSetup: function() {
        if (!this.target || this.target.__overlaySetup) {
          return;
        }
        if (!this.sizingTarget) {
          this.sizingTarget = this.target;
        }
        this.target.__overlaySetup = true;
        this.target.style.display = '';
        var transition = this.getTransition();
        if (transition) {
          transition.setup(this.target);
        }
        var style = this.target.style;
        var computed = getComputedStyle(this.target);
        if (computed.position === 'static') {
          style.position = 'fixed';
        }
        style.outline = 'none';
        style.display = 'none';
      },
  
      openedChanged: function() {
        this.transitioning = true;
        this.ensureTargetSetup();
        this.prepareRenderOpened();
        // async here to allow overlay layer to become visible.
        this.async(function() {
          this.target.style.display = '';
          // force layout to ensure transitions will go
          this.target.offsetWidth;
          this.renderOpened();
        });
        this.fire('core-overlay-open', this.opened);
      },
  
      // tasks which must occur before opening; e.g. making the element visible
      prepareRenderOpened: function() {
        if (this.opened) {
          addOverlay(this);
        }
        this.prepareBackdrop();
        // async so we don't auto-close immediately via a click.
        this.async(function() {
          if (!this.autoCloseDisabled) {
            this.enableElementListener(this.opened, document,
                this.captureEventName, 'captureHandler', true);
          }
        });
        this.enableElementListener(this.opened, window, 'resize',
            'resizeHandler');
  
        if (this.opened) {
          // force layout so SD Polyfill renders
          this.target.offsetHeight;
          this.discoverDimensions();
          // if we are showing, then take care when positioning
          this.preparePositioning();
          this.positionTarget();
          this.updateTargetDimensions();
          this.finishPositioning();
          if (this.layered) {
            this.layer.addElement(this.target);
            this.layer.opened = this.opened;
          }
        }
      },
  
      // tasks which cause the overlay to actually open; typically play an
      // animation
      renderOpened: function() {
        var transition = this.getTransition();
        if (transition) {
          transition.go(this.target, {opened: this.opened});
        } else {
          this.transitionend();
        }
        this.renderBackdropOpened();
      },
  
      // finishing tasks; typically called via a transition
      transitionend: function(e) {
        // make sure this is our transition event.
        if (e && e.target !== this.target) {
          return;
        }
        this.transitioning = false;
        if (!this.opened) {
          this.resetTargetDimensions();
          this.target.style.display = 'none';
          this.completeBackdrop();
          removeOverlay(this);
          if (this.layered) {
            if (!currentOverlay()) {
              this.layer.opened = this.opened;
            }
            this.layer.removeElement(this.target);
          }
        }
        this.fire('core-overlay-' + (this.opened ? 'open' : 'close') + 
            '-completed');
        this.applyFocus();
      },
  
      prepareBackdrop: function() {
        if (this.backdrop && this.opened) {
          if (!this.scrim.parentNode) {
            document.body.appendChild(this.scrim);
            this.scrim.style.zIndex = currentOverlayZ() - 1;
          }
          trackBackdrop(this);
        }
      },
  
      renderBackdropOpened: function() {
        if (this.backdrop && getBackdrops().length < 2) {
          this.scrim.classList.toggle('core-opened', this.opened);
        }
      },
  
      completeBackdrop: function() {
        if (this.backdrop) {
          trackBackdrop(this);
          if (getBackdrops().length === 0) {
            this.scrim.parentNode.removeChild(this.scrim);
          }
        }
      },
  
      preparePositioning: function() {
        this.target.style.transition = this.target.style.webkitTransition = 'none';
        this.target.style.transform = this.target.style.webkitTransform = 'none';
        this.target.style.display = '';
      },
  
      discoverDimensions: function() {
        if (this.dimensions) {
          return;
        }
        var target = getComputedStyle(this.target);
        var sizer = getComputedStyle(this.sizingTarget);
        this.dimensions = {
          position: {
            v: target.top !== 'auto' ? 'top' : (target.bottom !== 'auto' ?
              'bottom' : null),
            h: target.left !== 'auto' ? 'left' : (target.right !== 'auto' ?
              'right' : null),
            css: target.position
          },
          size: {
            v: sizer.maxHeight !== 'none',
            h: sizer.maxWidth !== 'none'
          },
          margin: {
            top: parseInt(target.marginTop) || 0,
            right: parseInt(target.marginRight) || 0,
            bottom: parseInt(target.marginBottom) || 0,
            left: parseInt(target.marginLeft) || 0
          }
        };
      },
  
      finishPositioning: function(target) {
        this.target.style.display = 'none';
        this.target.style.transform = this.target.style.webkitTransform = '';
        // force layout to avoid application of transform
        this.target.offsetWidth;
        this.target.style.transition = this.target.style.webkitTransition = '';
      },
  
      getTransition: function(name) {
        return this.meta.byId(name || this.transition);
      },
  
      getFocusNode: function() {
        return this.target.querySelector('[autofocus]') || this.target;
      },
  
      applyFocus: function() {
        var focusNode = this.getFocusNode();
        if (this.opened) {
          if (!this.autoFocusDisabled) {
            focusNode.focus();
          }
        } else {
          focusNode.blur();
          if (currentOverlay() == this) {
            console.warn('Current core-overlay is attempting to focus itself as next! (bug)');
          } else {
            focusOverlay();
          }
        }
      },
  
      positionTarget: function() {
        // fire positioning event
        this.fire('core-overlay-position', {target: this.target,
            sizingTarget: this.sizingTarget, opened: this.opened});
        if (!this.dimensions.position.v) {
          this.target.style.top = '0px';
        }
        if (!this.dimensions.position.h) {
          this.target.style.left = '0px';
        }
      },
  
      updateTargetDimensions: function() {
        this.sizeTarget();
        this.repositionTarget();
      },
  
      sizeTarget: function() {
        this.sizingTarget.style.boxSizing = 'border-box';
        var dims = this.dimensions;
        var rect = this.target.getBoundingClientRect();
        if (!dims.size.v) {
          this.sizeDimension(rect, dims.position.v, 'top', 'bottom', 'Height');
        }
        if (!dims.size.h) {
          this.sizeDimension(rect, dims.position.h, 'left', 'right', 'Width');
        }
      },
  
      sizeDimension: function(rect, positionedBy, start, end, extent) {
        var dims = this.dimensions;
        var flip = (positionedBy === end);
        var m = flip ? start : end;
        var ws = window['inner' + extent];
        var o = dims.margin[m] + (flip ? ws - rect[end] : 
            rect[start]);
        var offset = 'offset' + extent;
        var o2 = this.target[offset] - this.sizingTarget[offset];
        this.sizingTarget.style['max' + extent] = (ws - o - o2) + 'px';
      },
  
      // vertically and horizontally center if not positioned
      repositionTarget: function() {
        // only center if position fixed.      
        if (this.dimensions.position.css !== 'fixed') {
          return; 
        }
        if (!this.dimensions.position.v) {
          var t = (window.innerHeight - this.target.offsetHeight) / 2;
          t -= this.dimensions.margin.top;
          this.target.style.top = t + 'px';
        }
  
        if (!this.dimensions.position.h) {
          var l = (window.innerWidth - this.target.offsetWidth) / 2;
          l -= this.dimensions.margin.left;
          this.target.style.left = l + 'px';
        }
      },
  
      resetTargetDimensions: function() {
        if (!this.dimensions.size.v) {
          this.sizingTarget.style.maxHeight = '';  
        }
        if (!this.dimensions.size.h) {
          this.sizingTarget.style.maxWidth = '';  
        }
        this.dimensions = null;
      },
  
      tapHandler: function(e) {
        // closeSelector takes precedence since closeAttribute has a default non-null value.
        if (e.target &&
            (this.closeSelector && e.target.matches(this.closeSelector)) ||
            (this.closeAttribute && e.target.hasAttribute(this.closeAttribute))) {
          this.toggle();
        } else {
          if (this.autoCloseJob) {
            this.autoCloseJob.stop();
            this.autoCloseJob = null;
          }
        }
      },
      
      // We use the traditional approach of capturing events on document
      // to to determine if the overlay needs to close. However, due to 
      // ShadowDOM event retargeting, the event target is not useful. Instead
      // of using it, we attempt to close asynchronously and prevent the close
      // if a tap event is immediately heard on the target.
      // TODO(sorvell): This approach will not work with modal. For
      // this we need a scrim.
      captureHandler: function(e) {
        if (!this.autoCloseDisabled && (currentOverlay() == this)) {
          this.autoCloseJob = this.job(this.autoCloseJob, function() {
            this.close();
          });
        }
      },
  
      keydownHandler: function(e) {
        if (!this.autoCloseDisabled && (e.keyCode == this.keyHelper.ESCAPE_KEY)) {
          this.close();
          e.stopPropagation();
        }
      },
  
      /**
       * Extensions of core-overlay should implement the `resizeHandler`
       * method to adjust the size and position of the overlay when the 
       * browser window resizes.
       * @method resizeHandler
       */
      resizeHandler: function() {
        this.updateTargetDimensions();
      },
  
      // TODO(sorvell): these utility methods should not be here.
      addElementListenerList: function(node, events) {
        for (var i in events) {
          this.addElementListener(node, i, events[i]);
        }
      },
  
      removeElementListenerList: function(node, events) {
        for (var i in events) {
          this.removeElementListener(node, i, events[i]);
        }
      },
  
      enableElementListener: function(enable, node, event, methodName, capture) {
        if (enable) {
          this.addElementListener(node, event, methodName, capture);
        } else {
          this.removeElementListener(node, event, methodName, capture);
        }
      },
  
      addElementListener: function(node, event, methodName, capture) {
        var fn = this._makeBoundListener(methodName);
        if (node && fn) {
          Polymer.addEventListener(node, event, fn, capture);
        }
      },
  
      removeElementListener: function(node, event, methodName, capture) {
        var fn = this._makeBoundListener(methodName);
        if (node && fn) {
          Polymer.removeEventListener(node, event, fn, capture);
        }
      },
  
      _makeBoundListener: function(methodName) {
        var self = this, method = this[methodName];
        if (!method) {
          return;
        }
        var bound = '_bound' + methodName;
        if (!this[bound]) {
          this[bound] = function(e) {
            method.call(self, e);
          };
        }
        return this[bound];
      },
    });
  
    // TODO(sorvell): This should be an element with private state so it can
    // be independent of overlay.
    // track overlays for z-index and focus managemant
    var overlays = [];
    function addOverlay(overlay) {
      var z0 = currentOverlayZ();
      overlays.push(overlay);
      var z1 = currentOverlayZ();
      if (z1 <= z0) {
        applyOverlayZ(overlay, z0);
      }
    }
  
    function removeOverlay(overlay) {
      var i = overlays.indexOf(overlay);
      if (i >= 0) {
        overlays.splice(i, 1);
        setZ(overlay, '');
      }
    }
    
    function applyOverlayZ(overlay, aboveZ) {
      setZ(overlay.target, aboveZ + 2);
    }
    
    function setZ(element, z) {
      element.style.zIndex = z;
    }
  
    function currentOverlay() {
      return overlays[overlays.length-1];
    }
    
    var DEFAULT_Z = 10;
    
    function currentOverlayZ() {
      var z;
      var current = currentOverlay();
      if (current) {
        var z1 = window.getComputedStyle(current.target).zIndex;
        if (!isNaN(z1)) {
          z = Number(z1);
        }
      }
      return z || DEFAULT_Z;
    }
    
    function focusOverlay() {
      var current = currentOverlay();
      // We have to be careful to focus the next overlay _after_ any current
      // transitions are complete (due to the state being toggled prior to the
      // transition). Otherwise, we risk infinite recursion when a transitioning
      // (closed) overlay becomes the current overlay.
      //
      // NOTE: We make the assumption that any overlay that completes a transition
      // will call into focusOverlay to kick the process back off. Currently:
      // transitionend -> applyFocus -> focusOverlay.
      if (current && !current.transitioning) {
        current.applyFocus();
      }
    }
  
    var backdrops = [];
    function trackBackdrop(element) {
      if (element.opened) {
        backdrops.push(element);
      } else {
        var i = backdrops.indexOf(element);
        if (i >= 0) {
          backdrops.splice(i, 1);
        }
      }
    }
  
    function getBackdrops() {
      return backdrops;
    }
  })();
  