Files
ionic-framework/js/utils/dom.js

242 lines
7.4 KiB
JavaScript

(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);
};
})();
/**
* @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) {
window._rAF(cb);
},
/**
* @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);
},
/**
* @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#getParentWithClass
* @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.animationFrameThrottle = ionic.DomUtil.animationFrameThrottle;
})(this, document, ionic);