mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-23 23:32:40 +01:00
351 lines
11 KiB
JavaScript
351 lines
11 KiB
JavaScript
|
/**
|
||
|
* @requires javelin-install
|
||
|
* javelin-event
|
||
|
* @provides javelin-vector
|
||
|
*
|
||
|
* @javelin-installs JX.$V
|
||
|
*
|
||
|
* @javelin
|
||
|
*/
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Convenience function that returns a @{class:JX.Vector} instance. This allows
|
||
|
* you to concisely write things like:
|
||
|
*
|
||
|
* JX.$V(x, y).add(10, 10); // Explicit coordinates.
|
||
|
* JX.$V(node).add(50, 50).setDim(node); // Position of a node.
|
||
|
*
|
||
|
* @param number|Node If a node, returns the node's position vector.
|
||
|
* If numeric, the x-coordinate for the new vector.
|
||
|
* @param number? The y-coordinate for the new vector.
|
||
|
* @return @{class:JX.Vector} New vector.
|
||
|
*
|
||
|
* @group dom
|
||
|
*/
|
||
|
JX.$V = function(x, y) {
|
||
|
return new JX.Vector(x, y);
|
||
|
};
|
||
|
|
||
|
|
||
|
/**
|
||
|
* Query and update positions and dimensions of nodes (and other things) within
|
||
|
* within a document. Each vector has two elements, 'x' and 'y', which usually
|
||
|
* represent width/height ('dimension vector') or left/top ('position vector').
|
||
|
*
|
||
|
* Vectors are used to manage the sizes and positions of elements, events,
|
||
|
* the document, and the viewport (the visible section of the document, i.e.
|
||
|
* how much of the page the user can actually see in their browser window).
|
||
|
* Unlike most Javelin classes, @{class:JX.Vector} exposes two bare properties,
|
||
|
* 'x' and 'y'. You can read and manipulate these directly:
|
||
|
*
|
||
|
* // Give the user information about elements when they click on them.
|
||
|
* JX.Stratcom.listen(
|
||
|
* 'click',
|
||
|
* null,
|
||
|
* function(e) {
|
||
|
* var p = new JX.Vector(e);
|
||
|
* var d = JX.Vector.getDim(e.getTarget());
|
||
|
*
|
||
|
* alert('You clicked at <' + p.x + ',' + p.y + '> and the element ' +
|
||
|
* 'you clicked is ' + d.x + 'px wide and ' + d.y + 'px high.');
|
||
|
* });
|
||
|
*
|
||
|
* You can also update positions and dimensions using vectors:
|
||
|
*
|
||
|
* // When the user clicks on something, make it 10px wider and 10px taller.
|
||
|
* JX.Stratcom.listen(
|
||
|
* 'click',
|
||
|
* null,
|
||
|
* function(e) {
|
||
|
* var target = e.getTarget();
|
||
|
* JX.$V(target).add(10, 10).setDim(target);
|
||
|
* });
|
||
|
*
|
||
|
* Additionally, vectors can be used to query document and viewport information:
|
||
|
*
|
||
|
* var v = JX.Vector.getViewport(); // Viewport (window) width and height.
|
||
|
* var d = JX.Vector.getDocument(); // Document width and height.
|
||
|
* var visible_area = parseInt(100 * (v.x * v.y) / (d.x * d.y), 10);
|
||
|
* alert('You can currently see ' + visible_area + ' % of the document.');
|
||
|
*
|
||
|
* The function @{function:JX.$V} provides convenience construction of common
|
||
|
* vectors.
|
||
|
*
|
||
|
* @task query Querying Positions and Dimensions
|
||
|
* @task update Changing Positions and Dimensions
|
||
|
* @task manip Manipulating Vectors
|
||
|
*
|
||
|
* @group dom
|
||
|
*/
|
||
|
JX.install('Vector', {
|
||
|
|
||
|
/**
|
||
|
* Construct a vector, either from explicit coordinates or from a node
|
||
|
* or event. You can pass two Numbers to construct an explicit vector:
|
||
|
*
|
||
|
* var p = new JX.Vector(35, 42);
|
||
|
*
|
||
|
* Otherwise, you can pass a @{class:JX.Event} or a Node to implicitly
|
||
|
* construct a vector:
|
||
|
*
|
||
|
* var q = new JX.Vector(some_event);
|
||
|
* var r = new JX.Vector(some_node);
|
||
|
*
|
||
|
* These are just like calling JX.Vector.getPos() on the @{class:JX.Event} or
|
||
|
* Node.
|
||
|
*
|
||
|
* For convenience, @{function:JX.$V} constructs a new vector so you don't
|
||
|
* need to use the 'new' keyword. That is, these are equivalent:
|
||
|
*
|
||
|
* var s = new JX.Vector(x, y);
|
||
|
* var t = JX.$V(x, y);
|
||
|
*
|
||
|
* Methods like @{method:getScroll}, @{method:getViewport} and
|
||
|
* @{method:getDocument} also create new vectors.
|
||
|
*
|
||
|
* Once you have a vector, you can manipulate it with add():
|
||
|
*
|
||
|
* var u = JX.$V(35, 42);
|
||
|
* var v = u.add(5, -12); // v = <40, 30>
|
||
|
*
|
||
|
* @param wild 'x' component of the vector, or a @{class:JX.Event}, or a
|
||
|
* Node.
|
||
|
* @param Number? If providing an 'x' component, the 'y' component of the
|
||
|
* vector.
|
||
|
* @return @{class:JX.Vector} Specified vector.
|
||
|
* @task query
|
||
|
*/
|
||
|
construct : function(x, y) {
|
||
|
if (typeof y == 'undefined') {
|
||
|
return JX.Vector.getPos(x);
|
||
|
}
|
||
|
|
||
|
this.x = (x === null) ? null : parseFloat(x);
|
||
|
this.y = (y === null) ? null : parseFloat(y);
|
||
|
},
|
||
|
|
||
|
members : {
|
||
|
x : null,
|
||
|
y : null,
|
||
|
|
||
|
/**
|
||
|
* Move a node around by setting the position of a Node to the vector's
|
||
|
* coordinates. For instance, if you want to move an element to the top left
|
||
|
* corner of the document, you could do this (assuming it has 'position:
|
||
|
* absolute'):
|
||
|
*
|
||
|
* JX.$V(0, 0).setPos(node);
|
||
|
*
|
||
|
* @param Node Node to move.
|
||
|
* @return this
|
||
|
* @task update
|
||
|
*/
|
||
|
setPos : function(node) {
|
||
|
node.style.left = (this.x === null) ? '' : (parseInt(this.x, 10) + 'px');
|
||
|
node.style.top = (this.y === null) ? '' : (parseInt(this.y, 10) + 'px');
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Change the size of a node by setting its dimensions to the vector's
|
||
|
* coordinates. For instance, if you want to change an element to be 100px
|
||
|
* by 100px:
|
||
|
*
|
||
|
* JX.$V(100, 100).setDim(node);
|
||
|
*
|
||
|
* Or if you want to expand a node's dimensions by 50px:
|
||
|
*
|
||
|
* JX.$V(node).add(50, 50).setDim(node);
|
||
|
*
|
||
|
* @param Node Node to resize.
|
||
|
* @return this
|
||
|
* @task update
|
||
|
*/
|
||
|
setDim : function(node) {
|
||
|
node.style.width =
|
||
|
(this.x === null)
|
||
|
? ''
|
||
|
: (parseInt(this.x, 10) + 'px');
|
||
|
node.style.height =
|
||
|
(this.y === null)
|
||
|
? ''
|
||
|
: (parseInt(this.y, 10) + 'px');
|
||
|
return this;
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Change a vector's x and y coordinates by adding numbers to them, or
|
||
|
* adding the coordinates of another vector. For example:
|
||
|
*
|
||
|
* var u = JX.$V(3, 4).add(100, 200); // u = <103, 204>
|
||
|
*
|
||
|
* You can also add another vector:
|
||
|
*
|
||
|
* var q = JX.$V(777, 999);
|
||
|
* var r = JX.$V(1000, 2000);
|
||
|
* var s = q.add(r); // s = <1777, 2999>
|
||
|
*
|
||
|
* Note that this method returns a new vector. It does not modify the
|
||
|
* 'this' vector.
|
||
|
*
|
||
|
* @param wild Value to add to the vector's x component, or another
|
||
|
* vector.
|
||
|
* @param Number? Value to add to the vector's y component.
|
||
|
* @return @{class:JX.Vector} New vector, with summed components.
|
||
|
* @task manip
|
||
|
*/
|
||
|
add : function(x, y) {
|
||
|
if (x instanceof JX.Vector) {
|
||
|
y = x.y;
|
||
|
x = x.x;
|
||
|
}
|
||
|
return new JX.Vector(this.x + parseFloat(x), this.y + parseFloat(y));
|
||
|
}
|
||
|
},
|
||
|
|
||
|
statics : {
|
||
|
_viewport: null,
|
||
|
|
||
|
/**
|
||
|
* Determine where in a document an element is (or where an event, like
|
||
|
* a click, occurred) by building a new vector containing the position of a
|
||
|
* Node or @{class:JX.Event}. The 'x' component of the vector will
|
||
|
* correspond to the pixel offset of the argument relative to the left edge
|
||
|
* of the document, and the 'y' component will correspond to the pixel
|
||
|
* offset of the argument relative to the top edge of the document. Note
|
||
|
* that all vectors are generated in document coordinates, so the scroll
|
||
|
* position does not affect them.
|
||
|
*
|
||
|
* See also @{method:getDim}, used to determine an element's dimensions.
|
||
|
*
|
||
|
* @param Node|@{class:JX.Event} Node or event to determine the position
|
||
|
* of.
|
||
|
* @return @{class:JX.Vector} New vector with the argument's position.
|
||
|
* @task query
|
||
|
*/
|
||
|
getPos : function(node) {
|
||
|
JX.Event && (node instanceof JX.Event) && (node = node.getRawEvent());
|
||
|
|
||
|
if (('pageX' in node) || ('clientX' in node)) {
|
||
|
var c = JX.Vector._viewport;
|
||
|
return new JX.Vector(
|
||
|
node.pageX || (node.clientX + c.scrollLeft),
|
||
|
node.pageY || (node.clientY + c.scrollTop)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
var x = 0;
|
||
|
var y = 0;
|
||
|
do {
|
||
|
x += node.offsetLeft;
|
||
|
y += node.offsetTop;
|
||
|
node = node.offsetParent;
|
||
|
} while (node && node != document.body);
|
||
|
|
||
|
return new JX.Vector(x, y);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Determine the width and height of a node by building a new vector with
|
||
|
* dimension information. The 'x' component of the vector will correspond
|
||
|
* to the element's width in pixels, and the 'y' component will correspond
|
||
|
* to its height in pixels.
|
||
|
*
|
||
|
* See also @{method:getPos}, used to determine an element's position.
|
||
|
*
|
||
|
* @param Node Node to determine the display size of.
|
||
|
* @return @{JX.$V} New vector with the node's dimensions.
|
||
|
* @task query
|
||
|
*/
|
||
|
getDim : function(node) {
|
||
|
return new JX.Vector(node.offsetWidth, node.offsetHeight);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Determine the current scroll position by building a new vector where
|
||
|
* the 'x' component corresponds to how many pixels the user has scrolled
|
||
|
* from the left edge of the document, and the 'y' component corresponds to
|
||
|
* how many pixels the user has scrolled from the top edge of the document.
|
||
|
*
|
||
|
* See also @{method:getViewport}, used to determine the size of the
|
||
|
* viewport.
|
||
|
*
|
||
|
* @return @{JX.$V} New vector with the document scroll position.
|
||
|
* @task query
|
||
|
*/
|
||
|
getScroll : function() {
|
||
|
// We can't use JX.Vector._viewport here because there's diversity between
|
||
|
// browsers with respect to where position/dimension and scroll position
|
||
|
// information is stored.
|
||
|
var b = document.body;
|
||
|
var e = document.documentElement;
|
||
|
return new JX.Vector(
|
||
|
window.pageXOffset || b.scrollLeft || e.scrollLeft,
|
||
|
window.pageYOffset || b.scrollTop || e.scrollTop
|
||
|
);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Determine the size of the viewport (basically, the browser window) by
|
||
|
* building a new vector where the 'x' component corresponds to the width
|
||
|
* of the viewport in pixels and the 'y' component corresponds to the height
|
||
|
* of the viewport in pixels.
|
||
|
*
|
||
|
* See also @{method:getScroll}, used to determine the position of the
|
||
|
* viewport, and @{method:getDocument}, used to determine the size of the
|
||
|
* entire document.
|
||
|
*
|
||
|
* @return @{class:JX.Vector} New vector with the viewport dimensions.
|
||
|
* @task query
|
||
|
*/
|
||
|
getViewport : function() {
|
||
|
var c = JX.Vector._viewport;
|
||
|
return new JX.Vector(
|
||
|
window.innerWidth || c.clientWidth || 0,
|
||
|
window.innerHeight || c.clientHeight || 0
|
||
|
);
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* Determine the size of the document, including any area outside the
|
||
|
* current viewport which the user would need to scroll in order to see, by
|
||
|
* building a new vector where the 'x' component corresponds to the document
|
||
|
* width in pixels and the 'y' component corresponds to the document height
|
||
|
* in pixels.
|
||
|
*
|
||
|
* @return @{class:JX.Vector} New vector with the document dimensions.
|
||
|
* @task query
|
||
|
*/
|
||
|
getDocument : function() {
|
||
|
var c = JX.Vector._viewport;
|
||
|
return new JX.Vector(c.scrollWidth || 0, c.scrollHeight || 0);
|
||
|
}
|
||
|
},
|
||
|
|
||
|
/**
|
||
|
* On initialization, the browser-dependent viewport root is determined and
|
||
|
* stored.
|
||
|
*
|
||
|
* In ##__DEV__##, @{class:JX.Vector} installs a toString() method so
|
||
|
* vectors print in a debuggable way:
|
||
|
*
|
||
|
* <23, 92>
|
||
|
*
|
||
|
* This string representation of vectors is not available in a production
|
||
|
* context.
|
||
|
*
|
||
|
* @return void
|
||
|
*/
|
||
|
initialize : function() {
|
||
|
JX.Vector._viewport = document.documentElement || document.body;
|
||
|
|
||
|
if (__DEV__) {
|
||
|
JX.Vector.prototype.toString = function() {
|
||
|
return '<' + this.x + ', ' + this.y + '>';
|
||
|
};
|
||
|
}
|
||
|
}
|
||
|
|
||
|
});
|