Path: blob/master/webroot/rsrc/externals/javelin/lib/Vector.js
12242 views
/**1* @requires javelin-install2* javelin-event3* @provides javelin-vector4*5* @javelin-installs JX.$V6*7* @javelin8*/91011/**12* Convenience function that returns a @{class:JX.Vector} instance. This allows13* you to concisely write things like:14*15* JX.$V(x, y).add(10, 10); // Explicit coordinates.16* JX.$V(node).add(50, 50).setDim(node); // Position of a node.17*18* @param number|Node If a node, returns the node's position vector.19* If numeric, the x-coordinate for the new vector.20* @param number? The y-coordinate for the new vector.21* @return @{class:JX.Vector} New vector.22*/23JX.$V = function(x, y) {24return new JX.Vector(x, y);25};262728/**29* Query and update positions and dimensions of nodes (and other things) within30* within a document. Each vector has two elements, 'x' and 'y', which usually31* represent width/height ('dimension vector') or left/top ('position vector').32*33* Vectors are used to manage the sizes and positions of elements, events,34* the document, and the viewport (the visible section of the document, i.e.35* how much of the page the user can actually see in their browser window).36* Unlike most Javelin classes, @{class:JX.Vector} exposes two bare properties,37* 'x' and 'y'. You can read and manipulate these directly:38*39* // Give the user information about elements when they click on them.40* JX.Stratcom.listen(41* 'click',42* null,43* function(e) {44* var p = new JX.Vector(e);45* var d = JX.Vector.getDim(e.getTarget());46*47* alert('You clicked at <' + p.x + ',' + p.y + '> and the element ' +48* 'you clicked is ' + d.x + 'px wide and ' + d.y + 'px high.');49* });50*51* You can also update positions and dimensions using vectors:52*53* // When the user clicks on something, make it 10px wider and 10px taller.54* JX.Stratcom.listen(55* 'click',56* null,57* function(e) {58* var target = e.getTarget();59* JX.$V(target).add(10, 10).setDim(target);60* });61*62* Additionally, vectors can be used to query document and viewport information:63*64* var v = JX.Vector.getViewport(); // Viewport (window) width and height.65* var d = JX.Vector.getDocument(); // Document width and height.66* var visible_area = parseInt(100 * (v.x * v.y) / (d.x * d.y), 10);67* alert('You can currently see ' + visible_area + ' % of the document.');68*69* The function @{function:JX.$V} provides convenience construction of common70* vectors.71*72* @task query Querying Positions and Dimensions73* @task update Changing Positions and Dimensions74* @task manip Manipulating Vectors75*/76JX.install('Vector', {7778/**79* Construct a vector, either from explicit coordinates or from a node80* or event. You can pass two Numbers to construct an explicit vector:81*82* var p = new JX.Vector(35, 42);83*84* Otherwise, you can pass a @{class:JX.Event} or a Node to implicitly85* construct a vector:86*87* var q = new JX.Vector(some_event);88* var r = new JX.Vector(some_node);89*90* These are just like calling JX.Vector.getPos() on the @{class:JX.Event} or91* Node.92*93* For convenience, @{function:JX.$V} constructs a new vector so you don't94* need to use the 'new' keyword. That is, these are equivalent:95*96* var s = new JX.Vector(x, y);97* var t = JX.$V(x, y);98*99* Methods like @{method:getScroll}, @{method:getViewport} and100* @{method:getDocument} also create new vectors.101*102* Once you have a vector, you can manipulate it with add():103*104* var u = JX.$V(35, 42);105* var v = u.add(5, -12); // v = <40, 30>106*107* @param wild 'x' component of the vector, or a @{class:JX.Event}, or a108* Node.109* @param Number? If providing an 'x' component, the 'y' component of the110* vector.111* @return @{class:JX.Vector} Specified vector.112* @task query113*/114construct : function(x, y) {115if (typeof y == 'undefined') {116return JX.Vector.getPos(x);117}118119this.x = (x === null) ? null : parseFloat(x);120this.y = (y === null) ? null : parseFloat(y);121},122123members : {124x : null,125y : null,126127/**128* Move a node around by setting the position of a Node to the vector's129* coordinates. For instance, if you want to move an element to the top left130* corner of the document, you could do this (assuming it has 'position:131* absolute'):132*133* JX.$V(0, 0).setPos(node);134*135* @param Node Node to move.136* @return this137* @task update138*/139setPos : function(node) {140node.style.left = (this.x === null) ? '' : (parseInt(this.x, 10) + 'px');141node.style.top = (this.y === null) ? '' : (parseInt(this.y, 10) + 'px');142return this;143},144145/**146* Change the size of a node by setting its dimensions to the vector's147* coordinates. For instance, if you want to change an element to be 100px148* by 100px:149*150* JX.$V(100, 100).setDim(node);151*152* Or if you want to expand a node's dimensions by 50px:153*154* JX.$V(node).add(50, 50).setDim(node);155*156* @param Node Node to resize.157* @return this158* @task update159*/160setDim : function(node) {161node.style.width =162(this.x === null) ? '' : (parseInt(this.x, 10) + 'px');163node.style.height =164(this.y === null) ? '' : (parseInt(this.y, 10) + 'px');165return this;166},167168/**169* Change a vector's x and y coordinates by adding numbers to them, or170* adding the coordinates of another vector. For example:171*172* var u = JX.$V(3, 4).add(100, 200); // u = <103, 204>173*174* You can also add another vector:175*176* var q = JX.$V(777, 999);177* var r = JX.$V(1000, 2000);178* var s = q.add(r); // s = <1777, 2999>179*180* Note that this method returns a new vector. It does not modify the181* 'this' vector.182*183* @param wild Value to add to the vector's x component, or another184* vector.185* @param Number? Value to add to the vector's y component.186* @return @{class:JX.Vector} New vector, with summed components.187* @task manip188*/189add : function(x, y) {190if (x instanceof JX.Vector) {191y = x.y;192x = x.x;193}194return new JX.Vector(this.x + parseFloat(x), this.y + parseFloat(y));195}196},197198statics : {199_viewport: null,200201/**202* Determine where in a document an element is (or where an event, like203* a click, occurred) by building a new vector containing the position of a204* Node or @{class:JX.Event}. The 'x' component of the vector will205* correspond to the pixel offset of the argument relative to the left edge206* of the document, and the 'y' component will correspond to the pixel207* offset of the argument relative to the top edge of the document. Note208* that all vectors are generated in document coordinates, so the scroll209* position does not affect them.210*211* See also @{method:getDim}, used to determine an element's dimensions.212*213* @param Node|@{class:JX.Event} Node or event to determine the position214* of.215* @return @{class:JX.Vector} New vector with the argument's position.216* @task query217*/218getPos : function(node) {219JX.Event && (node instanceof JX.Event) && (node = node.getRawEvent());220221if (node.getBoundingClientRect) {222var rect;223try {224rect = node.getBoundingClientRect();225} catch (e) {226rect = { top : 0, left : 0 };227}228return new JX.Vector(229rect.left + window.pageXOffset,230rect.top + window.pageYOffset);231}232233if (('pageX' in node) || ('clientX' in node)) {234var c = JX.Vector._viewport;235return new JX.Vector(236node.pageX || (node.clientX + c.scrollLeft),237node.pageY || (node.clientY + c.scrollTop)238);239}240241var x = 0;242var y = 0;243do {244var offsetParent = node.offsetParent;245var scrollLeft = 0;246var scrollTop = 0;247if (offsetParent && offsetParent != document.body) {248scrollLeft = offsetParent.scrollLeft;249scrollTop = offsetParent.scrollTop;250}251x += (node.offsetLeft - scrollLeft);252y += (node.offsetTop - scrollTop);253node = offsetParent;254} while (node && node != document.body);255256return new JX.Vector(x, y);257},258259/**260* Determine the width and height of a node by building a new vector with261* dimension information. The 'x' component of the vector will correspond262* to the element's width in pixels, and the 'y' component will correspond263* to its height in pixels.264*265* See also @{method:getPos}, used to determine an element's position.266*267* @param Node Node to determine the display size of.268* @return @{JX.$V} New vector with the node's dimensions.269* @task query270*/271getDim : function(node) {272return new JX.Vector(node.offsetWidth, node.offsetHeight);273},274275/**276* Determine the current scroll position by building a new vector where277* the 'x' component corresponds to how many pixels the user has scrolled278* from the left edge of the document, and the 'y' component corresponds to279* how many pixels the user has scrolled from the top edge of the document.280*281* See also @{method:getViewport}, used to determine the size of the282* viewport.283*284* @return @{JX.$V} New vector with the document scroll position.285* @task query286*/287getScroll : function() {288// We can't use JX.Vector._viewport here because there's diversity between289// browsers with respect to where position/dimension and scroll position290// information is stored.291var b = document.body;292var e = document.documentElement;293return new JX.Vector(294window.pageXOffset || b.scrollLeft || e.scrollLeft,295window.pageYOffset || b.scrollTop || e.scrollTop296);297},298299300/**301* Get the aggregate scroll offsets for a node and all of its parents.302*303* Note that this excludes scroll at the document level, because it does304* not normally impact operations in document coordinates, which everything305* on this class returns. Use @{method:getScroll} to get the document scroll306* position.307*308* @param Node Node to determine offsets for.309* @return JX.Vector New vector with aggregate scroll offsets.310*/311getAggregateScrollForNode: function(node) {312var x = 0;313var y = 0;314315do {316if (node == document.body || node == document.documentElement) {317break;318}319320x += node.scrollLeft || 0;321y += node.scrollTop || 0;322node = node.parentNode;323} while (node);324325return new JX.$V(x, y);326},327328329/**330* Get the sum of a node's position and its parent scroll offsets.331*332* @param Node Node to determine aggregate position for.333* @return JX.Vector New vector with aggregate position.334*/335getPosWithScroll: function(node) {336return JX.$V(node).add(JX.Vector.getAggregateScrollForNode(node));337},338339340/**341* Determine the size of the viewport (basically, the browser window) by342* building a new vector where the 'x' component corresponds to the width343* of the viewport in pixels and the 'y' component corresponds to the height344* of the viewport in pixels.345*346* See also @{method:getScroll}, used to determine the position of the347* viewport, and @{method:getDocument}, used to determine the size of the348* entire document.349*350* @return @{class:JX.Vector} New vector with the viewport dimensions.351* @task query352*/353getViewport : function() {354var c = JX.Vector._viewport;355return new JX.Vector(356window.innerWidth || c.clientWidth || 0,357window.innerHeight || c.clientHeight || 0358);359},360361/**362* Determine the size of the document, including any area outside the363* current viewport which the user would need to scroll in order to see, by364* building a new vector where the 'x' component corresponds to the document365* width in pixels and the 'y' component corresponds to the document height366* in pixels.367*368* @return @{class:JX.Vector} New vector with the document dimensions.369* @task query370*/371getDocument : function() {372var c = JX.Vector._viewport;373return new JX.Vector(c.scrollWidth || 0, c.scrollHeight || 0);374}375},376377/**378* On initialization, the browser-dependent viewport root is determined and379* stored.380*381* In ##__DEV__##, @{class:JX.Vector} installs a toString() method so382* vectors print in a debuggable way:383*384* <23, 92>385*386* This string representation of vectors is not available in a production387* context.388*389* @return void390*/391initialize : function() {392JX.Vector._viewport = document.documentElement || document.body;393394if (__DEV__) {395JX.Vector.prototype.toString = function() {396return '<' + this.x + ', ' + this.y + '>';397};398}399}400401});402403404