text: (function(window, document, ionic) {
  
    var readyCallbacks = [];
    var isDomReady = false;
  
    function domReady() {
      isDomReady = true;
      for(var x=0; x<readyCallbacks.length; x++) {
        ionic.requestAnimationFrame(readyCallbacks[x]);
      }
      readyCallbacks = [];
      document.removeEventListener('DOMContentLoaded', domReady);
    }
    document.addEventListener('DOMContentLoaded', domReady);
  
    // From the man himself, Mr. Paul Irish.
    // The requestAnimationFrame polyfill
    // Put it on window just to preserve its context
    // without having to use .call
    window._rAF = (function(){
      return  window.requestAnimationFrame       ||
              window.webkitRequestAnimationFrame ||
              window.mozRequestAnimationFrame    ||
              function( callback ){
                window.setTimeout(callback, 16);
              };
    })();
  
    var cancelAnimationFrame = window.cancelAnimationFrame ||
      window.webkitCancelAnimationFrame ||
      window.mozCancelAnimationFrame ||
      window.webkitCancelRequestAnimationFrame;
  
    /**
    * @ngdoc utility
    * @name ionic.DomUtil
    * @module ionic
    */
    ionic.DomUtil = {
      //Call with proper context
      /**
       * @ngdoc method
       * @name ionic.DomUtil#requestAnimationFrame
       * @alias ionic.requestAnimationFrame
       * @description Calls [requestAnimationFrame](https://developer.mozilla.org/en-US/docs/Web/API/window.requestAnimationFrame), or a polyfill if not available.
       * @param {function} callback The function to call when the next frame
       * happens.
       */
      requestAnimationFrame: function(cb) {
        return window._rAF(cb);
      },
  
      cancelAnimationFrame: function(requestId) {
        cancelAnimationFrame(requestId);
      },
  
      /**
       * @ngdoc method
       * @name ionic.DomUtil#animationFrameThrottle
       * @alias ionic.animationFrameThrottle
       * @description
       * When given a callback, if that callback is called 100 times between
       * animation frames, adding Throttle will make it only run the last of
       * the 100 calls.
       *
       * @param {function} callback a function which will be throttled to
       * requestAnimationFrame
       * @returns {function} A function which will then call the passed in callback.
       * The passed in callback will receive the context the returned function is
       * called with.
       */
      animationFrameThrottle: function(cb) {
        var args, isQueued, context;
        return function() {
          args = arguments;
          context = this;
          if (!isQueued) {
            isQueued = true;
            ionic.requestAnimationFrame(function() {
              cb.apply(context, args);
              isQueued = false;
            });
          }
        };
      },
  
      /**
       * @ngdoc method
       * @name ionic.DomUtil#getPositionInParent
       * @description
       * Find an element's scroll offset within its container.
       * @param {DOMElement} element The element to find the offset of.
       * @returns {object} A position object with the following properties:
       *   - `{number}` `left` The left offset of the element.
       *   - `{number}` `top` The top offset of the element.
       */
      getPositionInParent: function(el) {
        return {
          left: el.offsetLeft,
          top: el.offsetTop
        };
      },
  
      /**
       * @ngdoc method
       * @name ionic.DomUtil#ready
       * @description
       * Call a function when the DOM is ready, or if it is already ready
       * call the function immediately.
       * @param {function} callback The function to be called.
       */
      ready: function(cb) {
        if(isDomReady || document.readyState === "complete") {
          ionic.requestAnimationFrame(cb);
        } else {
          readyCallbacks.push(cb);
        }
      },
  
      /**
       * @ngdoc method
       * @name ionic.DomUtil#getTextBounds
       * @description
       * Get a rect representing the bounds of the given textNode.
       * @param {DOMElement} textNode The textNode to find the bounds of.
       * @returns {object} An object representing the bounds of the node. Properties:
       *   - `{number}` `left` The left positton of the textNode.
       *   - `{number}` `right` The right positton of the textNode.
       *   - `{number}` `top` The top positton of the textNode.
       *   - `{number}` `bottom` The bottom position of the textNode.
       *   - `{number}` `width` The width of the textNode.
       *   - `{number}` `height` The height of the textNode.
       */
      getTextBounds: function(textNode) {
        if(document.createRange) {
          var range = document.createRange();
          range.selectNodeContents(textNode);
          if(range.getBoundingClientRect) {
            var rect = range.getBoundingClientRect();
            if(rect) {
              var sx = window.scrollX;
              var sy = window.scrollY;
  
              return {
                top: rect.top + sy,
                left: rect.left + sx,
                right: rect.left + sx + rect.width,
                bottom: rect.top + sy + rect.height,
                width: rect.width,
                height: rect.height
              };
            }
          }
        }
        return null;
      },
  
      /**
       * @ngdoc method
       * @name ionic.DomUtil#getChildIndex
       * @description
       * Get the first index of a child node within the given element of the
       * specified type.
       * @param {DOMElement} element The element to find the index of.
       * @param {string} type The nodeName to match children of element against.
       * @returns {number} The index, or -1, of a child with nodeName matching type.
       */
      getChildIndex: function(element, type) {
        if(type) {
          var ch = element.parentNode.children;
          var c;
          for(var i = 0, k = 0, j = ch.length; i < j; i++) {
            c = ch[i];
            if(c.nodeName && c.nodeName.toLowerCase() == type) {
              if(c == element) {
                return k;
              }
              k++;
            }
          }
        }
        return Array.prototype.slice.call(element.parentNode.children).indexOf(element);
      },
  
      /**
       * @private
       */
      swapNodes: function(src, dest) {
        dest.parentNode.insertBefore(src, dest);
      },
  
      /**
       * @private
       */
      centerElementByMargin: function(el) {
        el.style.marginLeft = (-el.offsetWidth) / 2 + 'px';
        el.style.marginTop = (-el.offsetHeight) / 2 + 'px';
      },
      //Center twice, after raf, to fix a bug with ios and showing elements
      //that have just been attached to the DOM.
      centerElementByMarginTwice: function(el) {
        ionic.requestAnimationFrame(function() {
          ionic.DomUtil.centerElementByMargin(el);
          setTimeout(function() {
            ionic.DomUtil.centerElementByMargin(el);
            setTimeout(function() {
              ionic.DomUtil.centerElementByMargin(el);
            });
          });
        });
      },
  
      elementIsDescendant: function(el, parent, stopAt) {
        var current = el;
        do {
          if (current === parent) return true;
          current = current.parentNode;
        } while (current && current !== stopAt);
        return false;
      },
  
      /**
       * @ngdoc method
       * @name ionic.DomUtil#getParentWithClass
       * @param {DOMElement} element
       * @param {string} className
       * @returns {DOMElement} The closest parent of element matching the
       * className, or null.
       */
      getParentWithClass: function(e, className, depth) {
        depth = depth || 10;
        while(e.parentNode && depth--) {
          if(e.parentNode.classList && e.parentNode.classList.contains(className)) {
            return e.parentNode;
          }
          e = e.parentNode;
        }
        return null;
      },
      /**
       * @ngdoc method
       * @name ionic.DomUtil#getParentOrSelfWithClass
       * @param {DOMElement} element
       * @param {string} className
       * @returns {DOMElement} The closest parent or self matching the
       * className, or null.
       */
      getParentOrSelfWithClass: function(e, className, depth) {
        depth = depth || 10;
        while(e && depth--) {
          if(e.classList && e.classList.contains(className)) {
            return e;
          }
          e = e.parentNode;
        }
        return null;
      },
  
      /**
       * @ngdoc method
       * @name ionic.DomUtil#rectContains
       * @param {number} x
       * @param {number} y
       * @param {number} x1
       * @param {number} y1
       * @param {number} x2
       * @param {number} y2
       * @returns {boolean} Whether {x,y} fits within the rectangle defined by
       * {x1,y1,x2,y2}.
       */
      rectContains: function(x, y, x1, y1, x2, y2) {
        if(x < x1 || x > x2) return false;
        if(y < y1 || y > y2) return false;
        return true;
      }
    };
  
    //Shortcuts
    ionic.requestAnimationFrame = ionic.DomUtil.requestAnimationFrame;
    ionic.cancelAnimationFrame = ionic.DomUtil.cancelAnimationFrame;
    ionic.animationFrameThrottle = ionic.DomUtil.animationFrameThrottle;
  })(window, document, ionic);
  