1 | define(["dojo/_base/kernel", "dojo/_base/window", "dojo/_base/xhr", "dojo/_base/sniff", "dojo/_base/url", "dojo/domReady!"], function(dojo){ |
---|
2 | dojo.getObject("io.windowName", true, dojox); |
---|
3 | // Implements the window.name transport |
---|
4 | |
---|
5 | dojox.io.windowName = { |
---|
6 | send: function(/*String*/ method, /*dojo.__IoArgs*/ args){ |
---|
7 | // summary: |
---|
8 | // Provides secure cross-domain request capability. |
---|
9 | // Sends a request using an iframe (POST or GET) and reads the response through the |
---|
10 | // frame's window.name. |
---|
11 | // |
---|
12 | // method: |
---|
13 | // The method to use to send the request, GET or POST |
---|
14 | // |
---|
15 | // args: |
---|
16 | // See dojo.xhr |
---|
17 | // |
---|
18 | // ####args.authElement: DOMNode? |
---|
19 | // |
---|
20 | // By providing an authElement, this indicates that windowName should use the |
---|
21 | // authorized window.name protocol, relying on |
---|
22 | // the loaded XD resource to return to the provided return URL on completion |
---|
23 | // of authorization/authentication. The provided authElement will be used to place |
---|
24 | // the iframe in, so the user can interact with the server resource for authentication |
---|
25 | // and/or authorization to access the resource. |
---|
26 | // |
---|
27 | // ####args.onAuthLoad: Function? |
---|
28 | // |
---|
29 | // When using authorized access to resources, this function will be called when the |
---|
30 | // authorization page has been loaded. (When authorization is actually completed, |
---|
31 | // the deferred callback function is called with the result). The primary use for this |
---|
32 | // is to make the authElement visible to the user once the resource has loaded |
---|
33 | // (this can be preferable to showing the iframe while the resource is loading |
---|
34 | // since it may not require authorization, it may simply return the resource). |
---|
35 | // |
---|
36 | // description: |
---|
37 | // In order to provide a windowname transport accessible resources/web services, a server |
---|
38 | // should check for the presence of a parameter window.name=true and if a request includes |
---|
39 | // such a parameter, it should respond to the request with an HTML |
---|
40 | // document that sets it's window.name to the string that is to be |
---|
41 | // delivered to the client. For example, if a client makes a window.name request like: |
---|
42 | // | http://othersite.com/greeting?windowname=true |
---|
43 | // And server wants to respond to the client with "Hello", it should return an html page: |
---|
44 | // | <html><script type="text/javascript"> |
---|
45 | // | window.name="Hello"; |
---|
46 | // | </script></html> |
---|
47 | // One can provide XML or JSON data by simply quoting the data as a string, and parsing the data |
---|
48 | // on the client. |
---|
49 | // If you use the authorization window.name protocol, the requester should include an |
---|
50 | // authElement element in the args, and a request will be created like: |
---|
51 | // | http://othersite.com/greeting?windowname=auth |
---|
52 | // And the server can respond like this: |
---|
53 | // | <html><script type="text/javascript"> |
---|
54 | // | var loc = window.name; |
---|
55 | // | authorizationButton.onclick = function(){ |
---|
56 | // | window.name="Hello"; |
---|
57 | // | location = loc; |
---|
58 | // | }; |
---|
59 | // | </script></html> |
---|
60 | // When using windowName from a XD Dojo build, make sure to set the |
---|
61 | // dojo.dojoBlankHtmlUrl property to a local URL. |
---|
62 | args.url += (args.url.match(/\?/) ? '&' : '?') + "windowname=" + (args.authElement ? "auth" : true); // indicate our desire for window.name communication |
---|
63 | var authElement = args.authElement; |
---|
64 | var cleanup = function(result){ |
---|
65 | try{ |
---|
66 | // we have to do this to stop the wait cursor in FF |
---|
67 | var innerDoc = dfd.ioArgs.frame.contentWindow.document; |
---|
68 | innerDoc.write(" "); |
---|
69 | innerDoc.close(); |
---|
70 | }catch(e){} |
---|
71 | (authElement || dojo.body()).removeChild(dfd.ioArgs.outerFrame); // clean up |
---|
72 | return result; |
---|
73 | }; |
---|
74 | var dfd = dojo._ioSetArgs(args,cleanup,cleanup,cleanup); |
---|
75 | if(args.timeout){ |
---|
76 | setTimeout(function(){ |
---|
77 | if(dfd.fired == -1){ |
---|
78 | dfd.callback(new Error("Timeout")); |
---|
79 | } |
---|
80 | }, |
---|
81 | args.timeout |
---|
82 | ); |
---|
83 | } |
---|
84 | dojox.io.windowName._send(dfd, method, authElement, args.onAuthLoad); |
---|
85 | return dfd; |
---|
86 | }, |
---|
87 | _send: function(dfd, method, authTarget, onAuthLoad){ |
---|
88 | |
---|
89 | var ioArgs = dfd.ioArgs; |
---|
90 | var frameNum = dojox.io.windowName._frameNum++; |
---|
91 | var sameDomainUrl = (dojo.config.dojoBlankHtmlUrl||dojo.config.dojoCallbackUrl||dojo.moduleUrl("dojo", "resources/blank.html")) + "#" + frameNum; |
---|
92 | var frameName = new dojo._Url(window.location, sameDomainUrl); |
---|
93 | var doc = dojo.doc; |
---|
94 | var frameContainer = authTarget || dojo.body(); |
---|
95 | function styleFrame(frame){ |
---|
96 | frame.style.width="100%"; |
---|
97 | frame.style.height="100%"; |
---|
98 | frame.style.border="0px"; |
---|
99 | } |
---|
100 | if(dojo.isMoz && ![].reduce){ |
---|
101 | // FF2 allows unsafe sibling frame modification, |
---|
102 | // the fix for this is to create nested frames with getters and setters to protect access |
---|
103 | var outerFrame = doc.createElement("iframe"); |
---|
104 | styleFrame(outerFrame); |
---|
105 | if(!authTarget){ |
---|
106 | outerFrame.style.display='none'; |
---|
107 | } |
---|
108 | frameContainer.appendChild(outerFrame); |
---|
109 | |
---|
110 | var firstWindow = outerFrame.contentWindow; |
---|
111 | doc = firstWindow.document; |
---|
112 | doc.write("<html><body margin='0px'><iframe style='width:100%;height:100%;border:0px' name='protectedFrame'></iframe></body></html>"); |
---|
113 | doc.close(); |
---|
114 | var secondWindow = firstWindow[0]; |
---|
115 | firstWindow.__defineGetter__(0,function(){}); |
---|
116 | firstWindow.__defineGetter__("protectedFrame",function(){}); |
---|
117 | doc = secondWindow.document; |
---|
118 | doc.write("<html><body margin='0px'></body></html>"); |
---|
119 | doc.close(); |
---|
120 | frameContainer = doc.body; |
---|
121 | } |
---|
122 | var frame; |
---|
123 | if(dojo.isIE){ |
---|
124 | var div = doc.createElement("div"); |
---|
125 | div.innerHTML = '<iframe name="' + frameName + '" onload="dojox.io.windowName['+frameNum+']()">'; |
---|
126 | frame = div.firstChild; |
---|
127 | }else{ |
---|
128 | frame = doc.createElement('iframe'); |
---|
129 | } |
---|
130 | ioArgs.frame = frame; |
---|
131 | styleFrame(frame); |
---|
132 | ioArgs.outerFrame = outerFrame = outerFrame || frame; |
---|
133 | if(!authTarget){ |
---|
134 | outerFrame.style.display='none'; |
---|
135 | } |
---|
136 | var state = 0; |
---|
137 | function getData(){ |
---|
138 | var data = frame.contentWindow.name; |
---|
139 | if(typeof data == 'string'){ |
---|
140 | if(data != frameName){ |
---|
141 | state = 2; // we are done now |
---|
142 | dfd.ioArgs.hash = frame.contentWindow.location.hash; |
---|
143 | dfd.callback(data); |
---|
144 | } |
---|
145 | } |
---|
146 | } |
---|
147 | dojox.io.windowName[frameNum] = frame.onload = function(){ |
---|
148 | try{ |
---|
149 | if(!dojo.isMoz && frame.contentWindow.location =='about:blank'){ |
---|
150 | // opera and safari will do an onload for about:blank first, we can ignore this first onload |
---|
151 | return; |
---|
152 | } |
---|
153 | }catch(e){ |
---|
154 | // if we are in the target domain, frame.contentWindow.location will throw an ignorable error |
---|
155 | } |
---|
156 | if(!state){ |
---|
157 | // we have loaded the target resource, now time to navigate back to our domain so we can read the frame name |
---|
158 | state=1; |
---|
159 | if(authTarget){ |
---|
160 | // call the callback so it can make it visible |
---|
161 | if(onAuthLoad){ |
---|
162 | onAuthLoad(); |
---|
163 | } |
---|
164 | }else{ |
---|
165 | // we are doing a synchronous capture, go directly to our same domain URL and retrieve the resource |
---|
166 | frame.contentWindow.location = sameDomainUrl; |
---|
167 | } |
---|
168 | } |
---|
169 | // back to our domain, we should be able to access the frame name now |
---|
170 | try{ |
---|
171 | if(state<2){ |
---|
172 | getData(); |
---|
173 | } |
---|
174 | } |
---|
175 | catch(e){ |
---|
176 | } |
---|
177 | |
---|
178 | }; |
---|
179 | frame.name = frameName; |
---|
180 | if(method.match(/GET/i)){ |
---|
181 | // if it is a GET we can just the iframe our src url |
---|
182 | dojo._ioAddQueryToUrl(ioArgs); |
---|
183 | frame.src = ioArgs.url; |
---|
184 | frameContainer.appendChild(frame); |
---|
185 | if(frame.contentWindow){ |
---|
186 | frame.contentWindow.location.replace(ioArgs.url); |
---|
187 | } |
---|
188 | }else if(method.match(/POST/i)){ |
---|
189 | // if it is a POST we will build a form to post it |
---|
190 | frameContainer.appendChild(frame); |
---|
191 | var form = dojo.doc.createElement("form"); |
---|
192 | dojo.body().appendChild(form); |
---|
193 | var query = dojo.queryToObject(ioArgs.query); |
---|
194 | for(var i in query){ |
---|
195 | var values = query[i]; |
---|
196 | values = values instanceof Array ? values : [values]; |
---|
197 | for(var j = 0; j < values.length; j++){ |
---|
198 | // create hidden inputs for all the parameters |
---|
199 | var input = doc.createElement("input"); |
---|
200 | input.type = 'hidden'; |
---|
201 | input.name = i; |
---|
202 | input.value = values[j]; |
---|
203 | form.appendChild(input); |
---|
204 | } |
---|
205 | } |
---|
206 | form.method = 'POST'; |
---|
207 | form.action = ioArgs.url; |
---|
208 | form.target = frameName;// connect the form to the iframe |
---|
209 | |
---|
210 | form.submit(); |
---|
211 | form.parentNode.removeChild(form); |
---|
212 | }else{ |
---|
213 | throw new Error("Method " + method + " not supported with the windowName transport"); |
---|
214 | } |
---|
215 | if(frame.contentWindow){ |
---|
216 | frame.contentWindow.name = frameName; // IE likes it afterwards |
---|
217 | } |
---|
218 | }, |
---|
219 | _frameNum: 0 |
---|
220 | |
---|
221 | }; |
---|
222 | |
---|
223 | return dojox.io.windowName; |
---|
224 | |
---|
225 | }); |
---|