-
Notifications
You must be signed in to change notification settings - Fork 88
/
robot.js
170 lines (156 loc) · 6.5 KB
/
robot.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
define([
"./_base/array",
"./dom",
"./dom-geometry",
"./_base/kernel",
"./_base/lang",
"./_base/window",
"doh/_browserRunner",
"doh/robot",
"./window"
], function(array, dom, geom, kernel, lang, win, doh, robot, winUtils){
kernel.experimental("dojo.robot");
// users who use doh+dojo get the added convenience of robot.mouseMoveAt(),
// instead of computing the absolute coordinates of their elements themselves
lang.mixin(robot, {
_resolveNode: function(/*String||DOMNode||Function*/ n){
if(typeof n == "function"){
// if the user passed a function returning a node, evaluate it
n = n();
}
return n ? dom.byId(n) : null;
},
_scrollIntoView: function(/*Node*/ n){
// scrolls the passed node into view, scrolling all ancestor frames/windows as well.
// Assumes parent iframes can be made fully visible given the current browser window size
var p = null;
array.forEach(robot._getWindowChain(n), function(w){
// get the position of the node wrt its parent window
// if it is a parent frame, its padding and border extents will get added in
var p2 = geom.position(n, false),
b = geom.getPadBorderExtents(n),
oldp = null;
// if p2 is the position of the original passed node, store the position away as p
// otherwise, node is actually an iframe. in this case, add the iframe's position wrt its parent window and also the iframe's padding and border extents
if(!p){
p = p2;
}else{
oldp = p;
p = {x: p.x+p2.x+b.l,
y: p.y+p2.y+b.t,
w: p.w,
h: p.h};
}
// scroll the parent window so that the node translated into the parent window's coordinate space is in view
winUtils.scrollIntoView(n,p);
// adjust position for the new scroll offsets
p2 = geom.position(n, false);
if(!oldp){
p = p2;
}else{
p = {x: oldp.x+p2.x+b.l,
y: oldp.y+p2.y+b.t,
w: p.w,
h: p.h};
}
// get the parent iframe so it can be scrolled too
n = w.frameElement;
});
},
_position: function(/*Node*/ n){
// Returns the geom.position of the passed node wrt the passed window's viewport,
// following any parent iframes containing the node and clipping the node to each iframe.
// precondition: _scrollIntoView already called
var p = null, max = Math.max, min = Math.min;
// p: the returned position of the node
array.forEach(robot._getWindowChain(n), function(w){
// get the position of the node wrt its parent window
// if it is a parent frame, its padding and border extents will get added in
var p2 = geom.position(n, false), b = geom.getPadBorderExtents(n);
// if p2 is the position of the original passed node, store the position away as p
// otherwise, node is actually an iframe. in this case, add the iframe's position wrt its parent window and also the iframe's padding and border extents
if(!p){
p = p2;
}else{
var view = winUtils.getBox(n.contentWindow.document);
p2.r = p2.x+view.w;
p2.b = p2.y+view.h;
p = {x: max(p.x+p2.x,p2.x)+b.l, // clip left edge of node wrt the iframe
y: max(p.y+p2.y,p2.y)+b.t, // top edge
r: min(p.x+p2.x+p.w,p2.r)+b.l, // right edge (to compute width)
b: min(p.y+p2.y+p.h,p2.b)+b.t}; // bottom edge (to compute height)
// save a few bytes by computing width and height from r and b
p.w = p.r-p.x;
p.h = p.b-p.y;
}
// the new node is now the old node's parent iframe
n=w.frameElement;
});
return p;
},
_getWindowChain : function(/*Node*/ n){
// Returns an array of windows starting from the passed node's parent window and ending at dojo's window
var cW = winUtils.get(n.ownerDocument);
var arr = [cW];
var f = cW.frameElement;
return (cW == win.global || !f) ? arr : arr.concat(robot._getWindowChain(f));
},
scrollIntoView : function(/*String||DOMNode||Function*/ node, /*Number, optional*/ delay){
// summary:
// Scroll the passed node into view, if it is not.
// node:
// The id of the node, or the node itself, to move the mouse to.
// If you pass an id or a function that returns a node, the node will not be evaluated until the movement executes.
// This is useful if you need to move the mouse to an node that is not yet present.
// delay:
// Delay, in milliseconds, to wait before firing.
// The delay is a delta with respect to the previous automation call.
robot.sequence(function(){
robot._scrollIntoView(robot._resolveNode(node));
}, delay);
},
mouseMoveAt : function(/*String||DOMNode||Function*/ node, /*Integer, optional*/ delay, /*Integer, optional*/ duration, /*Number, optional*/ offsetX, /*Number, optional*/ offsetY){
// summary:
// Moves the mouse over the specified node at the specified relative x,y offset.
// description:
// Moves the mouse over the specified node at the specified relative x,y offset.
// If you do not specify an offset, mouseMove will default to move to the middle of the node.
// Example: to move the mouse over a ComboBox's down arrow node, call doh.mouseMoveAt(dijit.byId('setvaluetest').downArrowNode);
// node:
// The id of the node, or the node itself, to move the mouse to.
// If you pass an id or a function that returns a node, the node will not be evaluated until the movement executes.
// This is useful if you need to move the mouse to an node that is not yet present.
// delay:
// Delay, in milliseconds, to wait before firing.
// The delay is a delta with respect to the previous automation call.
// For example, the following code ends after 600ms:
// | robot.mouseClick({left:true}, 100) // first call; wait 100ms
// | robot.typeKeys("dij", 500) // 500ms AFTER previous call; 600ms in all
// duration:
// Approximate time Robot will spend moving the mouse
// The default is 100ms.
// offsetX:
// x offset relative to the node, in pixels, to move the mouse. The default is half the node's width.
// offsetY:
// y offset relative to the node, in pixels, to move the mouse. The default is half the node's height.
robot._assertRobot();
// Schedule an action to scroll the node into view, then calculate it's center point
var point = {};
this.sequence(function(){
node = robot._resolveNode(node);
robot._scrollIntoView(node);
var pos = robot._position(node);
if(offsetY === undefined){
offsetX = pos.w/2;
offsetY = pos.h/2;
}
point.x = pos.x+offsetX;
point.y = pos.y+offsetY;
}, delay);
// Schedule a bunch of actions to move the mouse from the current position to point.
// These actions won't run until after the above callback.
this.mouseMoveTo(point, 0, duration, false);
}
});
return robot;
});