API Docs for: 3.10.3
Show:

File: io/js/io-upload-iframe.js

  1. /**
  2. Extends the IO to enable file uploads, with HTML forms
  3. using an iframe as the transport medium.
  4. @module io
  5. @submodule io-upload-iframe
  6. @for IO
  7. **/
  8.  
  9. var w = Y.config.win,
  10. d = Y.config.doc,
  11. _std = (d.documentMode && d.documentMode >= 8),
  12. _d = decodeURIComponent,
  13. _end = Y.IO.prototype.end;
  14.  
  15. /**
  16. * Creates the iframe transported used in file upload
  17. * transactions, and binds the response event handler.
  18. *
  19. * @method _cFrame
  20. * @private
  21. * @param {Object} o Transaction object generated by _create().
  22. * @param {Object} c Configuration object passed to YUI.io().
  23. * @param {Object} io
  24. */
  25. function _cFrame(o, c, io) {
  26. var i = Y.Node.create('<iframe src="#" id="io_iframe' + o.id + '" name="io_iframe' + o.id + '" />');
  27. i._node.style.position = 'absolute';
  28. i._node.style.top = '-1000px';
  29. i._node.style.left = '-1000px';
  30. Y.one('body').appendChild(i);
  31. // Bind the onload handler to the iframe to detect the file upload response.
  32. Y.on("load", function() { io._uploadComplete(o, c); }, '#io_iframe' + o.id);
  33. }
  34.  
  35. /**
  36. * Removes the iframe transport used in the file upload
  37. * transaction.
  38. *
  39. * @method _dFrame
  40. * @private
  41. * @param {Number} id The transaction ID used in the iframe's creation.
  42. */
  43. function _dFrame(id) {
  44. Y.Event.purgeElement('#io_iframe' + id, false);
  45. Y.one('body').removeChild(Y.one('#io_iframe' + id));
  46. Y.log('The iframe transport for transaction ' + id + ' has been destroyed.', 'info', 'io');
  47. }
  48.  
  49. Y.mix(Y.IO.prototype, {
  50. /**
  51. * Parses the POST data object and creates hidden form elements
  52. * for each key-value, and appends them to the HTML form object.
  53. * @method appendData
  54. * @private
  55. * @static
  56. * @param {Object} f HTML form object.
  57. * @param {String} s The key-value POST data.
  58. * @return {Array} o Array of created fields.
  59. */
  60. _addData: function(f, s) {
  61. // Serialize an object into a key-value string using
  62. // querystring-stringify-simple.
  63. if (Y.Lang.isObject(s)) {
  64. s = Y.QueryString.stringify(s);
  65. }
  66.  
  67. var o = [],
  68. m = s.split('='),
  69. i, l;
  70.  
  71. for (i = 0, l = m.length - 1; i < l; i++) {
  72. o[i] = d.createElement('input');
  73. o[i].type = 'hidden';
  74. o[i].name = _d(m[i].substring(m[i].lastIndexOf('&') + 1));
  75. o[i].value = (i + 1 === l) ? _d(m[i + 1]) : _d(m[i + 1].substring(0, (m[i + 1].lastIndexOf('&'))));
  76. f.appendChild(o[i]);
  77. Y.log('key: ' + o[i].name + ' and value: ' + o[i].value + ' added as form data.', 'info', 'io');
  78. }
  79.  
  80. return o;
  81. },
  82.  
  83. /**
  84. * Removes the custom fields created to pass additional POST
  85. * data, along with the HTML form fields.
  86. * @method _removeData
  87. * @private
  88. * @static
  89. * @param {Object} f HTML form object.
  90. * @param {Object} o HTML form fields created from configuration.data.
  91. */
  92. _removeData: function(f, o) {
  93. var i, l;
  94.  
  95. for (i = 0, l = o.length; i < l; i++) {
  96. f.removeChild(o[i]);
  97. }
  98. },
  99.  
  100. /**
  101. * Sets the appropriate attributes and values to the HTML
  102. * form, in preparation of a file upload transaction.
  103. * @method _setAttrs
  104. * @private
  105. * @static
  106. * @param {Object} f HTML form object.
  107. * @param {Object} id The Transaction ID.
  108. * @param {Object} uri Qualified path to transaction resource.
  109. */
  110. _setAttrs: function(f, id, uri) {
  111. f.setAttribute('action', uri);
  112. f.setAttribute('method', 'POST');
  113. f.setAttribute('target', 'io_iframe' + id );
  114. f.setAttribute(Y.UA.ie && !_std ? 'encoding' : 'enctype', 'multipart/form-data');
  115. },
  116.  
  117. /**
  118. * Reset the HTML form attributes to their original values.
  119. * @method _resetAttrs
  120. * @private
  121. * @static
  122. * @param {Object} f HTML form object.
  123. * @param {Object} a Object of original attributes.
  124. */
  125. _resetAttrs: function(f, a) {
  126. Y.Object.each(a, function(v, p) {
  127. if (v) {
  128. f.setAttribute(p, v);
  129. }
  130. else {
  131. f.removeAttribute(p);
  132. }
  133. });
  134. },
  135.  
  136. /**
  137. * Starts timeout count if the configuration object
  138. * has a defined timeout property.
  139. *
  140. * @method _startUploadTimeout
  141. * @private
  142. * @static
  143. * @param {Object} o Transaction object generated by _create().
  144. * @param {Object} c Configuration object passed to YUI.io().
  145. */
  146. _startUploadTimeout: function(o, c) {
  147. var io = this;
  148.  
  149. io._timeout[o.id] = w.setTimeout(
  150. function() {
  151. o.status = 0;
  152. o.statusText = 'timeout';
  153. io.complete(o, c);
  154. io.end(o, c);
  155. Y.log('Transaction ' + o.id + ' timeout.', 'info', 'io');
  156. }, c.timeout);
  157. },
  158.  
  159. /**
  160. * Clears the timeout interval started by _startUploadTimeout().
  161. * @method _clearUploadTimeout
  162. * @private
  163. * @static
  164. * @param {Number} id - Transaction ID.
  165. */
  166. _clearUploadTimeout: function(id) {
  167. var io = this;
  168.  
  169. w.clearTimeout(io._timeout[id]);
  170. delete io._timeout[id];
  171. },
  172.  
  173. /**
  174. * Bound to the iframe's Load event and processes
  175. * the response data.
  176. * @method _uploadComplete
  177. * @private
  178. * @static
  179. * @param {Object} o The transaction object
  180. * @param {Object} c Configuration object for the transaction.
  181. */
  182. _uploadComplete: function(o, c) {
  183. var io = this,
  184. d = Y.one('#io_iframe' + o.id).get('contentWindow.document'),
  185. b = d.one('body'),
  186. p;
  187.  
  188. if (c.timeout) {
  189. io._clearUploadTimeout(o.id);
  190. }
  191.  
  192. try {
  193. if (b) {
  194. // When a response Content-Type of "text/plain" is used, Firefox and Safari
  195. // will wrap the response string with <pre></pre>.
  196. p = b.one('pre:first-child');
  197. o.c.responseText = p ? p.get('text') : b.get('text');
  198. Y.log('The responseText value for transaction ' + o.id + ' is: ' + o.c.responseText + '.', 'info', 'io');
  199. }
  200. else {
  201. o.c.responseXML = d._node;
  202. Y.log('The response for transaction ' + o.id + ' is an XML document.', 'info', 'io');
  203. }
  204. }
  205. catch (e) {
  206. o.e = "upload failure";
  207. }
  208.  
  209. io.complete(o, c);
  210. io.end(o, c);
  211. // The transaction is complete, so call _dFrame to remove
  212. // the event listener bound to the iframe transport, and then
  213. // destroy the iframe.
  214. w.setTimeout( function() { _dFrame(o.id); }, 0);
  215. },
  216.  
  217. /**
  218. * Uploads HTML form data, inclusive of files/attachments,
  219. * using the iframe created in _create to facilitate the transaction.
  220. * @method _upload
  221. * @private
  222. * @static
  223. * @param {Object} o The transaction object
  224. * @param {Object} uri Qualified path to transaction resource.
  225. * @param {Object} c Configuration object for the transaction.
  226. */
  227. _upload: function(o, uri, c) {
  228. var io = this,
  229. f = (typeof c.form.id === 'string') ? d.getElementById(c.form.id) : c.form.id,
  230. // Track original HTML form attribute values.
  231. attr = {
  232. action: f.getAttribute('action'),
  233. target: f.getAttribute('target')
  234. },
  235. fields;
  236.  
  237. // Initialize the HTML form properties in case they are
  238. // not defined in the HTML form.
  239. io._setAttrs(f, o.id, uri);
  240. if (c.data) {
  241. fields = io._addData(f, c.data);
  242. }
  243.  
  244. // Start polling if a callback is present and the timeout
  245. // property has been defined.
  246. if (c.timeout) {
  247. io._startUploadTimeout(o, c);
  248. Y.log('Transaction timeout started for transaction ' + o.id + '.', 'info', 'io');
  249. }
  250.  
  251. // Start file upload.
  252. f.submit();
  253. io.start(o, c);
  254. if (c.data) {
  255. io._removeData(f, fields);
  256. }
  257.  
  258. return {
  259. id: o.id,
  260. abort: function() {
  261. o.status = 0;
  262. o.statusText = 'abort';
  263. if (Y.one('#io_iframe' + o.id)) {
  264. _dFrame(o.id);
  265. io.complete(o, c);
  266. io.end(o, c, attr);
  267. Y.log('Transaction ' + o.id + ' aborted.', 'info', 'io');
  268. }
  269. else {
  270. Y.log('Attempted to abort transaction ' + o.id + ' but transaction has completed.', 'warn', 'io');
  271. return false;
  272. }
  273. },
  274. isInProgress: function() {
  275. return Y.one('#io_iframe' + o.id) ? true : false;
  276. },
  277. io: io
  278. };
  279. },
  280.  
  281. upload: function(o, uri, c) {
  282. _cFrame(o, c, this);
  283. return this._upload(o, uri, c);
  284. },
  285.  
  286. end: function(transaction, config, attr) {
  287. if (config && config.form && config.form.upload) {
  288. var io = this;
  289. // Restore HTML form attributes to their original values.
  290. io._resetAttrs(f, attr);
  291. }
  292.  
  293. return _end.call(this, transaction, config);
  294. }
  295. });
  296.