|
@@ -0,0 +1,208 @@
|
|
|
|
|
+
|
|
|
|
|
+/*
|
|
|
|
|
+A cache for the left/right/top/bottom/width/height values for one or more elements.
|
|
|
|
|
+Works with both offset (from topleft document) and position (from offsetParent).
|
|
|
|
|
+
|
|
|
|
|
+options:
|
|
|
|
|
+- els
|
|
|
|
|
+- isHorizontal
|
|
|
|
|
+- isVertical
|
|
|
|
|
+*/
|
|
|
|
|
+var CoordCache = fc.CoordCache = Class.extend({
|
|
|
|
|
+
|
|
|
|
|
+ els: null, // jQuery set (assumed to be siblings)
|
|
|
|
|
+ origin: null, // {left,top} position of offsetParent of els
|
|
|
|
|
+ boundingRect: null, // constrain cordinates to this rectangle. {left,right,top,bottom} or null
|
|
|
|
|
+ isHorizontal: false, // whether to query for left/right/width
|
|
|
|
|
+ isVertical: false, // whether to query for top/bottom/height
|
|
|
|
|
+
|
|
|
|
|
+ // arrays of coordinates (offsets from topleft of document)
|
|
|
|
|
+ lefts: null,
|
|
|
|
|
+ rights: null,
|
|
|
|
|
+ tops: null,
|
|
|
|
|
+ bottoms: null,
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ constructor: function(options) {
|
|
|
|
|
+ this.els = $(options.els);
|
|
|
|
|
+ this.isHorizontal = options.isHorizontal;
|
|
|
|
|
+ this.isVertical = options.isVertical;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Queries the els for coordinates and stores them.
|
|
|
|
|
+ // Call this method before using and of the get* methods below.
|
|
|
|
|
+ build: function() {
|
|
|
|
|
+ this.origin = this.els.eq(0).offsetParent().offset();
|
|
|
|
|
+ this.boundingRect = this.queryBoundingRect();
|
|
|
|
|
+
|
|
|
|
|
+ if (this.isHorizontal) {
|
|
|
|
|
+ this.buildElHorizontals();
|
|
|
|
|
+ }
|
|
|
|
|
+ if (this.isVertical) {
|
|
|
|
|
+ this.buildElVerticals();
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Destroys all internal data about coordinates, freeing memory
|
|
|
|
|
+ clear: function() {
|
|
|
|
|
+ this.origin = null;
|
|
|
|
|
+ this.boundingRect = null;
|
|
|
|
|
+ this.lefts = null;
|
|
|
|
|
+ this.rights = null;
|
|
|
|
|
+ this.tops = null;
|
|
|
|
|
+ this.bottoms = null;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Compute and return what the elements' bounding rectangle is, from the user's perspective.
|
|
|
|
|
+ // Right now, only returns a rectangle if constrained by an overflow:scroll element.
|
|
|
|
|
+ queryBoundingRect: function() {
|
|
|
|
|
+ var scrollParentEl = getScrollParent(this.els.eq(0));
|
|
|
|
|
+
|
|
|
|
|
+ if (!scrollParentEl.is(document)) {
|
|
|
|
|
+ return getOuterRect(scrollParentEl);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Populates the left/right internal coordinate arrays
|
|
|
|
|
+ buildElHorizontals: function() {
|
|
|
|
|
+ var lefts = [];
|
|
|
|
|
+ var rights = [];
|
|
|
|
|
+
|
|
|
|
|
+ this.els.each(function(i, node) {
|
|
|
|
|
+ var el = $(node);
|
|
|
|
|
+ var left = el.offset().left;
|
|
|
|
|
+ var width = el.outerWidth();
|
|
|
|
|
+
|
|
|
|
|
+ lefts.push(left);
|
|
|
|
|
+ rights.push(left + width);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ this.lefts = lefts;
|
|
|
|
|
+ this.rights = rights;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Populates the top/bottom internal coordinate arrays
|
|
|
|
|
+ buildElVerticals: function() {
|
|
|
|
|
+ var tops = [];
|
|
|
|
|
+ var bottoms = [];
|
|
|
|
|
+
|
|
|
|
|
+ this.els.each(function(i, node) {
|
|
|
|
|
+ var el = $(node);
|
|
|
|
|
+ var top = el.offset().top;
|
|
|
|
|
+ var height = el.outerHeight();
|
|
|
|
|
+
|
|
|
|
|
+ tops.push(top);
|
|
|
|
|
+ bottoms.push(top + height);
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ this.tops = tops;
|
|
|
|
|
+ this.bottoms = bottoms;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Given a left offset (from document left), returns the index of the el that it horizontally intersects.
|
|
|
|
|
+ // If no intersection is made, or outside of the boundingRect, returns undefined.
|
|
|
|
|
+ getHorizontalIndex: function(leftOffset) {
|
|
|
|
|
+ var boundingRect = this.boundingRect;
|
|
|
|
|
+ var lefts = this.lefts;
|
|
|
|
|
+ var rights = this.rights;
|
|
|
|
|
+ var len = lefts.length;
|
|
|
|
|
+ var i;
|
|
|
|
|
+
|
|
|
|
|
+ if (!boundingRect || (leftOffset >= boundingRect.left && leftOffset < boundingRect.right)) {
|
|
|
|
|
+ for (i = 0; i < len; i++) {
|
|
|
|
|
+ if (leftOffset >= lefts[i] && leftOffset < rights[i]) {
|
|
|
|
|
+ return i;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Given a top offset (from document top), returns the index of the el that it vertically intersects.
|
|
|
|
|
+ // If no intersection is made, or outside of the boundingRect, returns undefined.
|
|
|
|
|
+ getVerticalIndex: function(topOffset) {
|
|
|
|
|
+ var boundingRect = this.boundingRect;
|
|
|
|
|
+ var tops = this.tops;
|
|
|
|
|
+ var bottoms = this.bottoms;
|
|
|
|
|
+ var len = tops.length;
|
|
|
|
|
+ var i;
|
|
|
|
|
+
|
|
|
|
|
+ if (!boundingRect || (topOffset >= boundingRect.top && topOffset < boundingRect.bottom)) {
|
|
|
|
|
+ for (i = 0; i < len; i++) {
|
|
|
|
|
+ if (topOffset >= tops[i] && topOffset < bottoms[i]) {
|
|
|
|
|
+ return i;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Gets the left offset (from document left) of the element at the given index
|
|
|
|
|
+ getLeftOffset: function(leftIndex) {
|
|
|
|
|
+ return this.lefts[leftIndex];
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Gets the left position (from offsetParent left) of the element at the given index
|
|
|
|
|
+ getLeftPosition: function(leftIndex) {
|
|
|
|
|
+ return this.lefts[leftIndex] - this.origin.left;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Gets the right offset (from document left) of the element at the given index.
|
|
|
|
|
+ // This value is NOT relative to the document's right edge, like the CSS concept of "right" would be.
|
|
|
|
|
+ getRightOffset: function(leftIndex) {
|
|
|
|
|
+ return this.rights[leftIndex];
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Gets the right position (from offsetParent left) of the element at the given index.
|
|
|
|
|
+ // This value is NOT relative to the offsetParent's right edge, like the CSS concept of "right" would be.
|
|
|
|
|
+ getRightPosition: function(leftIndex) {
|
|
|
|
|
+ return this.rights[leftIndex] - this.origin.left;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Gets the width of the element at the given index
|
|
|
|
|
+ getWidth: function(leftIndex) {
|
|
|
|
|
+ return this.rights[leftIndex] - this.lefts[leftIndex];
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Gets the top offset (from document top) of the element at the given index
|
|
|
|
|
+ getTopOffset: function(topIndex) {
|
|
|
|
|
+ return this.tops[topIndex];
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Gets the top position (from offsetParent top) of the element at the given position
|
|
|
|
|
+ getTopPosition: function(topIndex) {
|
|
|
|
|
+ return this.tops[topIndex] - this.origin.top;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ // Gets the bottom offset (from the document top) of the element at the given index.
|
|
|
|
|
+ // This value is NOT relative to the offsetParent's bottom edge, like the CSS concept of "bottom" would be.
|
|
|
|
|
+ getBottomOffset: function(topIndex) {
|
|
|
|
|
+ return this.bottoms[topIndex];
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Gets the bottom position (from the offsetParent top) of the element at the given index.
|
|
|
|
|
+ // This value is NOT relative to the offsetParent's bottom edge, like the CSS concept of "bottom" would be.
|
|
|
|
|
+ getBottomPosition: function(topIndex) {
|
|
|
|
|
+ return this.bottoms[topIndex] - this.origin.top;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ // Gets the height of the element at the given index
|
|
|
|
|
+ getHeight: function(topIndex) {
|
|
|
|
|
+ return this.bottoms[topIndex] - this.tops[topIndex];
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+});
|