PageRenderTime 72ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/framework/source/class/qx/io/remote/transport/Iframe.js

http://github.com/qooxdoo/qooxdoo
JavaScript | 651 lines | 300 code | 112 blank | 239 comment | 44 complexity | 8372db87d542871a682e42bc69f09c45 MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-3.0, MIT
  1. /* ************************************************************************
  2. qooxdoo - the new era of web development
  3. http://qooxdoo.org
  4. Copyright:
  5. 2004-2008 1&1 Internet AG, Germany, http://www.1und1.de
  6. 2006 Derrell Lipman
  7. 2006 STZ-IDA, Germany, http://www.stz-ida.de
  8. License:
  9. MIT: https://opensource.org/licenses/MIT
  10. See the LICENSE file in the project's top-level directory for details.
  11. Authors:
  12. * Sebastian Werner (wpbasti)
  13. * Andreas Ecker (ecker)
  14. * Derrell Lipman (derrell)
  15. * Andreas Junghans (lucidcake)
  16. ************************************************************************ */
  17. /* ************************************************************************
  18. ************************************************************************ */
  19. /**
  20. * Transports requests to a server using an IFRAME.
  21. *
  22. * This class should not be used directly by client programmers.
  23. *
  24. * NOTE: Instances of this class must be disposed of after use
  25. *
  26. * @asset(qx/static/blank.gif)
  27. */
  28. qx.Class.define("qx.io.remote.transport.Iframe",
  29. {
  30. extend : qx.io.remote.transport.Abstract,
  31. implement: [ qx.core.IDisposable ],
  32. /*
  33. *****************************************************************************
  34. CONSTRUCTOR
  35. *****************************************************************************
  36. */
  37. construct : function()
  38. {
  39. this.base(arguments);
  40. // Unique identifiers for iframe and form
  41. var vUniqueId = (new Date).valueOf();
  42. var vFrameName = "frame_" + vUniqueId;
  43. var vFormName = "form_" + vUniqueId;
  44. // This is to prevent the "mixed secure and insecure content" warning in IE with https
  45. var vFrameSource;
  46. if ((qx.core.Environment.get("engine.name") == "mshtml")) {
  47. vFrameSource = "javascript:void(0)";
  48. }
  49. // Create a hidden iframe.
  50. // The purpose of the iframe is to receive data coming back from the server (see below).
  51. this.__frame = qx.bom.Iframe.create({id: vFrameName, name: vFrameName, src: vFrameSource});
  52. qx.bom.element.Style.set(this.__frame, "display", "none");
  53. // Create form element with textarea as conduit for request data.
  54. // The target of the form is the hidden iframe, which means the response
  55. // coming back from the server is written into the iframe.
  56. this.__form = qx.dom.Element.create("form", {id: vFormName, name: vFormName, target: vFrameName});
  57. qx.bom.element.Style.set(this.__form, "display", "none");
  58. qx.dom.Element.insertEnd(this.__form, qx.dom.Node.getBodyElement(document));
  59. this.__data = qx.dom.Element.create("textarea", {id: "_data_", name: "_data_"});
  60. qx.dom.Element.insertEnd(this.__data, this.__form);
  61. // Finally, attach iframe to DOM and add listeners
  62. qx.dom.Element.insertEnd(this.__frame, qx.dom.Node.getBodyElement(document));
  63. qx.event.Registration.addListener(this.__frame, "load", this._onload, this);
  64. // qx.event.handler.Iframe does not yet support the readystatechange event
  65. this.__onreadystatechangeWrapper = qx.lang.Function.listener(this._onreadystatechange, this);
  66. qx.bom.Event.addNativeListener(this.__frame, "readystatechange", this.__onreadystatechangeWrapper);
  67. },
  68. /*
  69. *****************************************************************************
  70. STATICS
  71. *****************************************************************************
  72. */
  73. statics :
  74. {
  75. /**
  76. * Capabilities of this transport type.
  77. *
  78. * @internal
  79. */
  80. handles :
  81. {
  82. synchronous : false,
  83. asynchronous : true,
  84. crossDomain : false,
  85. fileUpload : true,
  86. programmaticFormFields : true,
  87. responseTypes : [ "text/plain", "text/javascript", "application/json", "application/xml", "text/html" ]
  88. },
  89. /**
  90. * Returns always true, because iframe transport is supported by all browsers.
  91. *
  92. * @return {Boolean}
  93. */
  94. isSupported : function() {
  95. return true;
  96. },
  97. /*
  98. ---------------------------------------------------------------------------
  99. EVENT LISTENER
  100. ---------------------------------------------------------------------------
  101. */
  102. /**
  103. * For reference:
  104. * http://msdn.microsoft.com/en-us/library/ie/ms534359%28v=vs.85%29.aspx
  105. *
  106. * @internal
  107. */
  108. _numericMap :
  109. {
  110. "uninitialized" : 1,
  111. "loading" : 2,
  112. "loaded" : 2,
  113. "interactive" : 3,
  114. "complete" : 4
  115. }
  116. },
  117. /*
  118. *****************************************************************************
  119. MEMBERS
  120. *****************************************************************************
  121. */
  122. members :
  123. {
  124. __data : null,
  125. __lastReadyState : 0,
  126. __form : null,
  127. __frame : null,
  128. __onreadystatechangeWrapper : null,
  129. /*
  130. ---------------------------------------------------------------------------
  131. USER METHODS
  132. ---------------------------------------------------------------------------
  133. */
  134. /**
  135. * Sends a request with the use of a form.
  136. *
  137. */
  138. send : function()
  139. {
  140. var vMethod = this.getMethod();
  141. var vUrl = this.getUrl();
  142. // --------------------------------------
  143. // Adding parameters
  144. // --------------------------------------
  145. var vParameters = this.getParameters(false);
  146. var vParametersList = [];
  147. for (var vId in vParameters)
  148. {
  149. var value = vParameters[vId];
  150. if (value instanceof Array)
  151. {
  152. for (var i=0; i<value.length; i++) {
  153. vParametersList.push(encodeURIComponent(vId) + "=" + encodeURIComponent(value[i]));
  154. }
  155. }
  156. else
  157. {
  158. vParametersList.push(encodeURIComponent(vId) + "=" + encodeURIComponent(value));
  159. }
  160. }
  161. if (vParametersList.length > 0) {
  162. vUrl += (vUrl.indexOf("?") >= 0 ? "&" : "?") + vParametersList.join("&");
  163. }
  164. // --------------------------------------------------------
  165. // Adding data parameters (if no data is already present)
  166. // --------------------------------------------------------
  167. if (this.getData() === null)
  168. {
  169. var vParameters = this.getParameters(true);
  170. var vParametersList = [];
  171. for (var vId in vParameters)
  172. {
  173. var value = vParameters[vId];
  174. if (value instanceof Array)
  175. {
  176. for (var i=0; i<value.length; i++)
  177. {
  178. vParametersList.push(encodeURIComponent(vId) +
  179. "=" +
  180. encodeURIComponent(value[i]));
  181. }
  182. }
  183. else
  184. {
  185. vParametersList.push(encodeURIComponent(vId) +
  186. "=" +
  187. encodeURIComponent(value));
  188. }
  189. }
  190. if (vParametersList.length > 0)
  191. {
  192. this.setData(vParametersList.join("&"));
  193. }
  194. }
  195. // --------------------------------------
  196. // Adding form fields
  197. // --------------------------------------
  198. var vFormFields = this.getFormFields();
  199. for (var vId in vFormFields)
  200. {
  201. var vField = document.createElement("textarea");
  202. vField.name = vId;
  203. vField.appendChild(document.createTextNode(vFormFields[vId]));
  204. this.__form.appendChild(vField);
  205. }
  206. // --------------------------------------
  207. // Preparing form
  208. // --------------------------------------
  209. this.__form.action = vUrl;
  210. this.__form.method = vMethod;
  211. // --------------------------------------
  212. // Sending data
  213. // --------------------------------------
  214. this.__data.appendChild(document.createTextNode(this.getData()));
  215. this.__form.submit();
  216. this.setState("sending");
  217. },
  218. /**
  219. * Converting complete state to numeric value and update state property
  220. *
  221. * @signature function(e)
  222. * @param e {qx.event.type.Event} event object
  223. */
  224. _onload : qx.event.GlobalError.observeMethod(function(e)
  225. {
  226. // Timing-issue in Opera
  227. // Do not switch state to complete in case load event fires before content
  228. // of iframe was updated
  229. if (qx.core.Environment.get("engine.name") == "opera" && this.getIframeHtmlContent() == "") {
  230. return;
  231. }
  232. if (this.__form.src) {
  233. return;
  234. }
  235. this._switchReadyState(qx.io.remote.transport.Iframe._numericMap.complete);
  236. }),
  237. /**
  238. * Converting named readyState to numeric value and update state property
  239. *
  240. * @signature function(e)
  241. * @param e {qx.event.type.Event} event object
  242. */
  243. _onreadystatechange : qx.event.GlobalError.observeMethod(function(e) {
  244. this._switchReadyState(qx.io.remote.transport.Iframe._numericMap[this.__frame.readyState]);
  245. }),
  246. /**
  247. * Switches the readystate by setting the internal state.
  248. *
  249. * @param vReadyState {String} readystate value
  250. */
  251. _switchReadyState : function(vReadyState)
  252. {
  253. // Ignoring already stopped requests
  254. switch(this.getState())
  255. {
  256. case "completed":
  257. case "aborted":
  258. case "failed":
  259. case "timeout":
  260. this.warn("Ignore Ready State Change");
  261. return;
  262. }
  263. // Updating internal state
  264. while (this.__lastReadyState < vReadyState) {
  265. this.setState(qx.io.remote.Exchange._nativeMap[++this.__lastReadyState]);
  266. }
  267. },
  268. /*
  269. ---------------------------------------------------------------------------
  270. REQUEST HEADER SUPPORT
  271. ---------------------------------------------------------------------------
  272. */
  273. /**
  274. * Sets a request header with the given value.
  275. *
  276. * This method is not implemented at the moment.
  277. *
  278. * @param vLabel {String} request header name
  279. * @param vValue {var} request header value
  280. */
  281. setRequestHeader : function(vLabel, vValue) {},
  282. /*
  283. ---------------------------------------------------------------------------
  284. RESPONSE HEADER SUPPORT
  285. ---------------------------------------------------------------------------
  286. */
  287. /**
  288. * Returns the value of the given response header.
  289. *
  290. * This method is not implemented at the moment and returns always "null".
  291. *
  292. * @param vLabel {String} Response header name
  293. * @return {null} Returns null
  294. */
  295. getResponseHeader : function(vLabel) {
  296. return null;
  297. },
  298. /**
  299. * Provides an hash of all response headers.
  300. *
  301. * This method is not implemented at the moment and returns an empty map.
  302. *
  303. * @return {Map} empty map
  304. */
  305. getResponseHeaders : function() {
  306. return {};
  307. },
  308. /*
  309. ---------------------------------------------------------------------------
  310. STATUS SUPPORT
  311. ---------------------------------------------------------------------------
  312. */
  313. /**
  314. * Returns the current status code of the request if available or -1 if not.
  315. * This method needs implementation (returns always 200).
  316. *
  317. * @return {Integer} status code
  318. */
  319. getStatusCode : function() {
  320. return 200;
  321. },
  322. /**
  323. * Provides the status text for the current request if available and null otherwise.
  324. * This method needs implementation (returns always an empty string)
  325. *
  326. * @return {String} status code text
  327. */
  328. getStatusText : function() {
  329. return "";
  330. },
  331. /*
  332. ---------------------------------------------------------------------------
  333. FRAME UTILITIES
  334. ---------------------------------------------------------------------------
  335. */
  336. /**
  337. * Returns the DOM window object of the used iframe.
  338. *
  339. * @return {Object} DOM window object
  340. */
  341. getIframeWindow : function() {
  342. return qx.bom.Iframe.getWindow(this.__frame);
  343. },
  344. /**
  345. * Returns the document node of the used iframe.
  346. *
  347. * @return {Object} document node
  348. */
  349. getIframeDocument : function() {
  350. return qx.bom.Iframe.getDocument(this.__frame);
  351. },
  352. /**
  353. * Returns the body node of the used iframe.
  354. *
  355. * @return {Object} body node
  356. */
  357. getIframeBody : function() {
  358. return qx.bom.Iframe.getBody(this.__frame);
  359. },
  360. /*
  361. ---------------------------------------------------------------------------
  362. RESPONSE DATA SUPPORT
  363. ---------------------------------------------------------------------------
  364. */
  365. /**
  366. * Returns the iframe content (innerHTML) as text.
  367. *
  368. * @return {String} iframe content as text
  369. */
  370. getIframeTextContent : function()
  371. {
  372. var vBody = this.getIframeBody();
  373. if (!vBody) {
  374. return null;
  375. }
  376. if (!vBody.firstChild) {
  377. return "";
  378. }
  379. // Mshtml returns the content inside a PRE
  380. // element if we use plain text
  381. if (vBody.firstChild.tagName &&
  382. vBody.firstChild.tagName.toLowerCase() == "pre") {
  383. return vBody.firstChild.innerHTML;
  384. } else {
  385. return vBody.innerHTML;
  386. }
  387. },
  388. /**
  389. * Returns the iframe content as HTML.
  390. *
  391. * @return {String} iframe content as HTML
  392. */
  393. getIframeHtmlContent : function()
  394. {
  395. var vBody = this.getIframeBody();
  396. return vBody ? vBody.innerHTML : null;
  397. },
  398. /**
  399. * Returns the length of the content as fetched thus far.
  400. * This method needs implementation (returns always 0).
  401. *
  402. * @return {Integer} Returns 0
  403. */
  404. getFetchedLength : function() {
  405. return 0;
  406. },
  407. /**
  408. * Returns the content of the response
  409. *
  410. * @return {null | String} null or text of the response (=iframe content).
  411. */
  412. getResponseContent : function()
  413. {
  414. if (this.getState() !== "completed")
  415. {
  416. if (qx.core.Environment.get("qx.debug"))
  417. {
  418. if (qx.core.Environment.get("qx.debug.io.remote")) {
  419. this.warn("Transfer not complete, ignoring content!");
  420. }
  421. }
  422. return null;
  423. }
  424. if (qx.core.Environment.get("qx.debug"))
  425. {
  426. if (qx.core.Environment.get("qx.debug.io.remote")) {
  427. this.debug("Returning content for responseType: " + this.getResponseType());
  428. }
  429. }
  430. var vText = this.getIframeTextContent();
  431. switch(this.getResponseType())
  432. {
  433. case "text/plain":
  434. if (qx.core.Environment.get("qx.debug"))
  435. {
  436. if (qx.core.Environment.get("qx.debug.io.remote.data"))
  437. {
  438. this.debug("Response: " + this._responseContent);
  439. }
  440. }
  441. return vText;
  442. case "text/html":
  443. vText = this.getIframeHtmlContent();
  444. if (qx.core.Environment.get("qx.debug"))
  445. {
  446. if (qx.core.Environment.get("qx.debug.io.remote.data"))
  447. {
  448. this.debug("Response: " + this._responseContent);
  449. }
  450. }
  451. return vText;
  452. case "application/json":
  453. vText = this.getIframeHtmlContent();
  454. if (qx.core.Environment.get("qx.debug"))
  455. {
  456. if (qx.core.Environment.get("qx.debug.io.remote.data"))
  457. {
  458. this.debug("Response: " + this._responseContent);
  459. }
  460. }
  461. try {
  462. return vText && vText.length > 0 ? qx.lang.Json.parse(vText) : null;
  463. } catch(ex) {
  464. return this.error("Could not execute json: (" + vText + ")", ex);
  465. }
  466. case "text/javascript":
  467. vText = this.getIframeHtmlContent();
  468. if (qx.core.Environment.get("qx.debug"))
  469. {
  470. if (qx.core.Environment.get("qx.debug.io.remote.data"))
  471. {
  472. this.debug("Response: " + this._responseContent);
  473. }
  474. }
  475. try {
  476. return vText && vText.length > 0 ? window.eval(vText) : null;
  477. } catch(ex) {
  478. return this.error("Could not execute javascript: (" + vText + ")", ex);
  479. }
  480. case "application/xml":
  481. vText = this.getIframeDocument();
  482. if (qx.core.Environment.get("qx.debug"))
  483. {
  484. if (qx.core.Environment.get("qx.debug.io.remote.data"))
  485. {
  486. this.debug("Response: " + this._responseContent);
  487. }
  488. }
  489. return vText;
  490. default:
  491. this.warn("No valid responseType specified (" + this.getResponseType() + ")!");
  492. return null;
  493. }
  494. }
  495. },
  496. /*
  497. *****************************************************************************
  498. DEFER
  499. *****************************************************************************
  500. */
  501. defer : function()
  502. {
  503. // basic registration to qx.io.remote.Exchange
  504. // the real availability check (activeX stuff and so on) follows at the first real request
  505. qx.io.remote.Exchange.registerType(qx.io.remote.transport.Iframe, "qx.io.remote.transport.Iframe");
  506. },
  507. /*
  508. *****************************************************************************
  509. DESTRUCTOR
  510. *****************************************************************************
  511. */
  512. destruct : function()
  513. {
  514. if (this.__frame)
  515. {
  516. qx.event.Registration.removeListener(this.__frame, "load", this._onload, this);
  517. qx.bom.Event.removeNativeListener(this.__frame, "readystatechange", this.__onreadystatechangeWrapper);
  518. // Reset source to a blank image for gecko
  519. // Otherwise it will switch into a load-without-end behaviour
  520. if ((qx.core.Environment.get("engine.name") == "gecko")) {
  521. this.__frame.src = qx.util.ResourceManager.getInstance().toUri("qx/static/blank.gif");
  522. }
  523. // Finally, remove element node
  524. qx.dom.Element.remove(this.__frame);
  525. }
  526. if (this.__form) {
  527. qx.dom.Element.remove(this.__form);
  528. }
  529. this.__frame = this.__form = this.__data = null;
  530. }
  531. });