Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/phabricator
Path: blob/master/webroot/rsrc/externals/javelin/lib/Vector.js
12242 views
1
/**
2
* @requires javelin-install
3
* javelin-event
4
* @provides javelin-vector
5
*
6
* @javelin-installs JX.$V
7
*
8
* @javelin
9
*/
10
11
12
/**
13
* Convenience function that returns a @{class:JX.Vector} instance. This allows
14
* you to concisely write things like:
15
*
16
* JX.$V(x, y).add(10, 10); // Explicit coordinates.
17
* JX.$V(node).add(50, 50).setDim(node); // Position of a node.
18
*
19
* @param number|Node If a node, returns the node's position vector.
20
* If numeric, the x-coordinate for the new vector.
21
* @param number? The y-coordinate for the new vector.
22
* @return @{class:JX.Vector} New vector.
23
*/
24
JX.$V = function(x, y) {
25
return new JX.Vector(x, y);
26
};
27
28
29
/**
30
* Query and update positions and dimensions of nodes (and other things) within
31
* within a document. Each vector has two elements, 'x' and 'y', which usually
32
* represent width/height ('dimension vector') or left/top ('position vector').
33
*
34
* Vectors are used to manage the sizes and positions of elements, events,
35
* the document, and the viewport (the visible section of the document, i.e.
36
* how much of the page the user can actually see in their browser window).
37
* Unlike most Javelin classes, @{class:JX.Vector} exposes two bare properties,
38
* 'x' and 'y'. You can read and manipulate these directly:
39
*
40
* // Give the user information about elements when they click on them.
41
* JX.Stratcom.listen(
42
* 'click',
43
* null,
44
* function(e) {
45
* var p = new JX.Vector(e);
46
* var d = JX.Vector.getDim(e.getTarget());
47
*
48
* alert('You clicked at <' + p.x + ',' + p.y + '> and the element ' +
49
* 'you clicked is ' + d.x + 'px wide and ' + d.y + 'px high.');
50
* });
51
*
52
* You can also update positions and dimensions using vectors:
53
*
54
* // When the user clicks on something, make it 10px wider and 10px taller.
55
* JX.Stratcom.listen(
56
* 'click',
57
* null,
58
* function(e) {
59
* var target = e.getTarget();
60
* JX.$V(target).add(10, 10).setDim(target);
61
* });
62
*
63
* Additionally, vectors can be used to query document and viewport information:
64
*
65
* var v = JX.Vector.getViewport(); // Viewport (window) width and height.
66
* var d = JX.Vector.getDocument(); // Document width and height.
67
* var visible_area = parseInt(100 * (v.x * v.y) / (d.x * d.y), 10);
68
* alert('You can currently see ' + visible_area + ' % of the document.');
69
*
70
* The function @{function:JX.$V} provides convenience construction of common
71
* vectors.
72
*
73
* @task query Querying Positions and Dimensions
74
* @task update Changing Positions and Dimensions
75
* @task manip Manipulating Vectors
76
*/
77
JX.install('Vector', {
78
79
/**
80
* Construct a vector, either from explicit coordinates or from a node
81
* or event. You can pass two Numbers to construct an explicit vector:
82
*
83
* var p = new JX.Vector(35, 42);
84
*
85
* Otherwise, you can pass a @{class:JX.Event} or a Node to implicitly
86
* construct a vector:
87
*
88
* var q = new JX.Vector(some_event);
89
* var r = new JX.Vector(some_node);
90
*
91
* These are just like calling JX.Vector.getPos() on the @{class:JX.Event} or
92
* Node.
93
*
94
* For convenience, @{function:JX.$V} constructs a new vector so you don't
95
* need to use the 'new' keyword. That is, these are equivalent:
96
*
97
* var s = new JX.Vector(x, y);
98
* var t = JX.$V(x, y);
99
*
100
* Methods like @{method:getScroll}, @{method:getViewport} and
101
* @{method:getDocument} also create new vectors.
102
*
103
* Once you have a vector, you can manipulate it with add():
104
*
105
* var u = JX.$V(35, 42);
106
* var v = u.add(5, -12); // v = <40, 30>
107
*
108
* @param wild 'x' component of the vector, or a @{class:JX.Event}, or a
109
* Node.
110
* @param Number? If providing an 'x' component, the 'y' component of the
111
* vector.
112
* @return @{class:JX.Vector} Specified vector.
113
* @task query
114
*/
115
construct : function(x, y) {
116
if (typeof y == 'undefined') {
117
return JX.Vector.getPos(x);
118
}
119
120
this.x = (x === null) ? null : parseFloat(x);
121
this.y = (y === null) ? null : parseFloat(y);
122
},
123
124
members : {
125
x : null,
126
y : null,
127
128
/**
129
* Move a node around by setting the position of a Node to the vector's
130
* coordinates. For instance, if you want to move an element to the top left
131
* corner of the document, you could do this (assuming it has 'position:
132
* absolute'):
133
*
134
* JX.$V(0, 0).setPos(node);
135
*
136
* @param Node Node to move.
137
* @return this
138
* @task update
139
*/
140
setPos : function(node) {
141
node.style.left = (this.x === null) ? '' : (parseInt(this.x, 10) + 'px');
142
node.style.top = (this.y === null) ? '' : (parseInt(this.y, 10) + 'px');
143
return this;
144
},
145
146
/**
147
* Change the size of a node by setting its dimensions to the vector's
148
* coordinates. For instance, if you want to change an element to be 100px
149
* by 100px:
150
*
151
* JX.$V(100, 100).setDim(node);
152
*
153
* Or if you want to expand a node's dimensions by 50px:
154
*
155
* JX.$V(node).add(50, 50).setDim(node);
156
*
157
* @param Node Node to resize.
158
* @return this
159
* @task update
160
*/
161
setDim : function(node) {
162
node.style.width =
163
(this.x === null) ? '' : (parseInt(this.x, 10) + 'px');
164
node.style.height =
165
(this.y === null) ? '' : (parseInt(this.y, 10) + 'px');
166
return this;
167
},
168
169
/**
170
* Change a vector's x and y coordinates by adding numbers to them, or
171
* adding the coordinates of another vector. For example:
172
*
173
* var u = JX.$V(3, 4).add(100, 200); // u = <103, 204>
174
*
175
* You can also add another vector:
176
*
177
* var q = JX.$V(777, 999);
178
* var r = JX.$V(1000, 2000);
179
* var s = q.add(r); // s = <1777, 2999>
180
*
181
* Note that this method returns a new vector. It does not modify the
182
* 'this' vector.
183
*
184
* @param wild Value to add to the vector's x component, or another
185
* vector.
186
* @param Number? Value to add to the vector's y component.
187
* @return @{class:JX.Vector} New vector, with summed components.
188
* @task manip
189
*/
190
add : function(x, y) {
191
if (x instanceof JX.Vector) {
192
y = x.y;
193
x = x.x;
194
}
195
return new JX.Vector(this.x + parseFloat(x), this.y + parseFloat(y));
196
}
197
},
198
199
statics : {
200
_viewport: null,
201
202
/**
203
* Determine where in a document an element is (or where an event, like
204
* a click, occurred) by building a new vector containing the position of a
205
* Node or @{class:JX.Event}. The 'x' component of the vector will
206
* correspond to the pixel offset of the argument relative to the left edge
207
* of the document, and the 'y' component will correspond to the pixel
208
* offset of the argument relative to the top edge of the document. Note
209
* that all vectors are generated in document coordinates, so the scroll
210
* position does not affect them.
211
*
212
* See also @{method:getDim}, used to determine an element's dimensions.
213
*
214
* @param Node|@{class:JX.Event} Node or event to determine the position
215
* of.
216
* @return @{class:JX.Vector} New vector with the argument's position.
217
* @task query
218
*/
219
getPos : function(node) {
220
JX.Event && (node instanceof JX.Event) && (node = node.getRawEvent());
221
222
if (node.getBoundingClientRect) {
223
var rect;
224
try {
225
rect = node.getBoundingClientRect();
226
} catch (e) {
227
rect = { top : 0, left : 0 };
228
}
229
return new JX.Vector(
230
rect.left + window.pageXOffset,
231
rect.top + window.pageYOffset);
232
}
233
234
if (('pageX' in node) || ('clientX' in node)) {
235
var c = JX.Vector._viewport;
236
return new JX.Vector(
237
node.pageX || (node.clientX + c.scrollLeft),
238
node.pageY || (node.clientY + c.scrollTop)
239
);
240
}
241
242
var x = 0;
243
var y = 0;
244
do {
245
var offsetParent = node.offsetParent;
246
var scrollLeft = 0;
247
var scrollTop = 0;
248
if (offsetParent && offsetParent != document.body) {
249
scrollLeft = offsetParent.scrollLeft;
250
scrollTop = offsetParent.scrollTop;
251
}
252
x += (node.offsetLeft - scrollLeft);
253
y += (node.offsetTop - scrollTop);
254
node = offsetParent;
255
} while (node && node != document.body);
256
257
return new JX.Vector(x, y);
258
},
259
260
/**
261
* Determine the width and height of a node by building a new vector with
262
* dimension information. The 'x' component of the vector will correspond
263
* to the element's width in pixels, and the 'y' component will correspond
264
* to its height in pixels.
265
*
266
* See also @{method:getPos}, used to determine an element's position.
267
*
268
* @param Node Node to determine the display size of.
269
* @return @{JX.$V} New vector with the node's dimensions.
270
* @task query
271
*/
272
getDim : function(node) {
273
return new JX.Vector(node.offsetWidth, node.offsetHeight);
274
},
275
276
/**
277
* Determine the current scroll position by building a new vector where
278
* the 'x' component corresponds to how many pixels the user has scrolled
279
* from the left edge of the document, and the 'y' component corresponds to
280
* how many pixels the user has scrolled from the top edge of the document.
281
*
282
* See also @{method:getViewport}, used to determine the size of the
283
* viewport.
284
*
285
* @return @{JX.$V} New vector with the document scroll position.
286
* @task query
287
*/
288
getScroll : function() {
289
// We can't use JX.Vector._viewport here because there's diversity between
290
// browsers with respect to where position/dimension and scroll position
291
// information is stored.
292
var b = document.body;
293
var e = document.documentElement;
294
return new JX.Vector(
295
window.pageXOffset || b.scrollLeft || e.scrollLeft,
296
window.pageYOffset || b.scrollTop || e.scrollTop
297
);
298
},
299
300
301
/**
302
* Get the aggregate scroll offsets for a node and all of its parents.
303
*
304
* Note that this excludes scroll at the document level, because it does
305
* not normally impact operations in document coordinates, which everything
306
* on this class returns. Use @{method:getScroll} to get the document scroll
307
* position.
308
*
309
* @param Node Node to determine offsets for.
310
* @return JX.Vector New vector with aggregate scroll offsets.
311
*/
312
getAggregateScrollForNode: function(node) {
313
var x = 0;
314
var y = 0;
315
316
do {
317
if (node == document.body || node == document.documentElement) {
318
break;
319
}
320
321
x += node.scrollLeft || 0;
322
y += node.scrollTop || 0;
323
node = node.parentNode;
324
} while (node);
325
326
return new JX.$V(x, y);
327
},
328
329
330
/**
331
* Get the sum of a node's position and its parent scroll offsets.
332
*
333
* @param Node Node to determine aggregate position for.
334
* @return JX.Vector New vector with aggregate position.
335
*/
336
getPosWithScroll: function(node) {
337
return JX.$V(node).add(JX.Vector.getAggregateScrollForNode(node));
338
},
339
340
341
/**
342
* Determine the size of the viewport (basically, the browser window) by
343
* building a new vector where the 'x' component corresponds to the width
344
* of the viewport in pixels and the 'y' component corresponds to the height
345
* of the viewport in pixels.
346
*
347
* See also @{method:getScroll}, used to determine the position of the
348
* viewport, and @{method:getDocument}, used to determine the size of the
349
* entire document.
350
*
351
* @return @{class:JX.Vector} New vector with the viewport dimensions.
352
* @task query
353
*/
354
getViewport : function() {
355
var c = JX.Vector._viewport;
356
return new JX.Vector(
357
window.innerWidth || c.clientWidth || 0,
358
window.innerHeight || c.clientHeight || 0
359
);
360
},
361
362
/**
363
* Determine the size of the document, including any area outside the
364
* current viewport which the user would need to scroll in order to see, by
365
* building a new vector where the 'x' component corresponds to the document
366
* width in pixels and the 'y' component corresponds to the document height
367
* in pixels.
368
*
369
* @return @{class:JX.Vector} New vector with the document dimensions.
370
* @task query
371
*/
372
getDocument : function() {
373
var c = JX.Vector._viewport;
374
return new JX.Vector(c.scrollWidth || 0, c.scrollHeight || 0);
375
}
376
},
377
378
/**
379
* On initialization, the browser-dependent viewport root is determined and
380
* stored.
381
*
382
* In ##__DEV__##, @{class:JX.Vector} installs a toString() method so
383
* vectors print in a debuggable way:
384
*
385
* <23, 92>
386
*
387
* This string representation of vectors is not available in a production
388
* context.
389
*
390
* @return void
391
*/
392
initialize : function() {
393
JX.Vector._viewport = document.documentElement || document.body;
394
395
if (__DEV__) {
396
JX.Vector.prototype.toString = function() {
397
return '<' + this.x + ', ' + this.y + '>';
398
};
399
}
400
}
401
402
});
403
404