summaryrefslogtreecommitdiff
path: root/lib/dojo/robot.js
blob: 6cbe0bebc48793a81a5138e32d2f5ff1ff1e6b32 (plain)
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
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
/*
	Copyright (c) 2004-2010, The Dojo Foundation All Rights Reserved.
	Available via Academic Free License >= 2.1 OR the modified BSD license.
	see: http://dojotoolkit.org/license for details
*/


if(!dojo._hasResource["dojo.robot"]){ //_hasResource checks added by build. Do not use _hasResource directly in your code.
dojo._hasResource["dojo.robot"] = true;
dojo.provide("dojo.robot");
dojo.experimental("dojo.robot");
dojo.require("doh.robot");
dojo.require("dojo.window");

(function(){
// users who use doh+dojo get the added convenience of dojo.mouseMoveAt,
// instead of computing the absolute coordinates of their elements themselves
dojo.mixin(doh.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? dojo.byId(n) : null;
	},

	_scrollIntoView: function(/*Node*/ n){
		// scrolls the passed node into view, scrolling all ancester frames/windows as well.
		// Assumes parent iframes can be made fully visible given the current browser window size
		var d = dojo,
			dr = doh.robot,
			p = null;
		d.forEach(dr._getWindowChain(n), function(w){
			d.withGlobal(w, function(){
				// 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 = d.position(n, false),
					b = d._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
				dojo.window.scrollIntoView(n,p);
				// adjust position for the new scroll offsets
				p2 = d.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 dojo.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 d = dojo, p = null, M = Math.max, m = Math.min;
		// p: the returned position of the node
		d.forEach(doh.robot._getWindowChain(n), function(w){
			d.withGlobal(w, function(){
				// 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 = d.position(n, false), b = d._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;
					d.withGlobal(n.contentWindow,function(){
						view=dojo.window.getBox();
					});
					p2.r = p2.x+view.w;
					p2.b = p2.y+view.h;
					p = {x: M(p.x+p2.x,p2.x)+b.l, // clip left edge of node wrt the iframe
						y: M(p.y+p2.y,p2.y)+b.t,	// top edge
						r: m(p.x+p2.x+p.w,p2.r)+b.l,	// right edge (to compute width)
						b: m(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 = dojo.window.get(n.ownerDocument);
		var arr=[cW];
		var f = cW.frameElement;
		return (cW == dojo.global || f == null)? arr : arr.concat(doh.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.
		//
		doh.robot.sequence(function(){
			doh.robot._scrollIntoView(doh.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:
		//			doh.robot.mouseClick({left:true}, 100) // first call; wait 100ms
		//			doh.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.
		//

		doh.robot._assertRobot();
		duration = duration||100;
		this.sequence(function(){
			node=doh.robot._resolveNode(node);
			doh.robot._scrollIntoView(node);
			var pos = doh.robot._position(node);
			if(offsetY === undefined){
				offsetX=pos.w/2;
				offsetY=pos.h/2;
			}
			var x = pos.x+offsetX;
			var y = pos.y+offsetY;
			doh.robot._mouseMove(x, y, false, duration);
		}, delay, duration);
	}
});

})();

}