API Docs for: 3.17.2
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 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 _addData
  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. // Track original HTML form attribute values.
  112. this._originalFormAttrs = {
  113. action: f.getAttribute('action'),
  114. target: f.getAttribute('target')
  115. };
  116.  
  117. f.setAttribute('action', uri);
  118. f.setAttribute('method', 'POST');
  119. f.setAttribute('target', 'io_iframe' + id );
  120. f.setAttribute(Y.UA.ie && !_std ? 'encoding' : 'enctype', 'multipart/form-data');
  121. },
  122.  
  123. /**
  124. * Reset the HTML form attributes to their original values.
  125. * @method _resetAttrs
  126. * @private
  127. * @static
  128. * @param {Object} f HTML form object.
  129. * @param {Object} a Object of original attributes.
  130. */
  131. _resetAttrs: function(f, a) {
  132. Y.Object.each(a, function(v, p) {
  133. if (v) {
  134. f.setAttribute(p, v);
  135. }
  136. else {
  137. f.removeAttribute(p);
  138. }
  139. });
  140. },
  141.  
  142. /**
  143. * Starts timeout count if the configuration object
  144. * has a defined timeout property.
  145. *
  146. * @method _startUploadTimeout
  147. * @private
  148. * @static
  149. * @param {Object} o Transaction object generated by _create().
  150. * @param {Object} c Configuration object passed to YUI.io().
  151. */
  152. _startUploadTimeout: function(o, c) {
  153. var io = this;
  154.  
  155. io._timeout[o.id] = w.setTimeout(
  156. function() {
  157. o.status = 0;
  158. o.statusText = 'timeout';
  159. io.complete(o, c);
  160. io.end(o, c);
  161. Y.log('Transaction ' + o.id + ' timeout.', 'info', 'io');
  162. }, c.timeout);
  163. },
  164.  
  165. /**
  166. * Clears the timeout interval started by _startUploadTimeout().
  167. * @method _clearUploadTimeout
  168. * @private
  169. * @static
  170. * @param {Number} id - Transaction ID.
  171. */
  172. _clearUploadTimeout: function(id) {
  173. var io = this;
  174.  
  175. w.clearTimeout(io._timeout[id]);
  176. delete io._timeout[id];
  177. },
  178.  
  179. /**
  180. * Bound to the iframe's Load event and processes
  181. * the response data.
  182. * @method _uploadComplete
  183. * @private
  184. * @static
  185. * @param {Object} o The transaction object
  186. * @param {Object} c Configuration object for the transaction.
  187. */
  188. _uploadComplete: function(o, c) {
  189. var io = this,
  190. d = Y.one('#io_iframe' + o.id).get('contentWindow.document'),
  191. b = d.one('body'),
  192. p;
  193.  
  194. if (c.timeout) {
  195. io._clearUploadTimeout(o.id);
  196. }
  197.  
  198. try {
  199. if (b) {
  200. // When a response Content-Type of "text/plain" is used, Firefox and Safari
  201. // will wrap the response string with <pre></pre>.
  202. p = b.one('pre:first-child');
  203. o.c.responseText = p ? p.get('text') : b.get('text');
  204. Y.log('The responseText value for transaction ' + o.id + ' is: ' + o.c.responseText + '.', 'info', 'io');
  205. }
  206. else {
  207. o.c.responseXML = d._node;
  208. Y.log('The response for transaction ' + o.id + ' is an XML document.', 'info', 'io');
  209. }
  210. }
  211. catch (e) {
  212. o.e = "upload failure";
  213. }
  214.  
  215. io.complete(o, c);
  216. io.end(o, c);
  217. // The transaction is complete, so call _dFrame to remove
  218. // the event listener bound to the iframe transport, and then
  219. // destroy the iframe.
  220. w.setTimeout( function() { _dFrame(o.id); }, 0);
  221. },
  222.  
  223. /**
  224. * Uploads HTML form data, inclusive of files/attachments,
  225. * using the iframe created in _create to facilitate the transaction.
  226. * @method _upload
  227. * @private
  228. * @static
  229. * @param {Object} o The transaction object
  230. * @param {Object} uri Qualified path to transaction resource.
  231. * @param {Object} c Configuration object for the transaction.
  232. */
  233. _upload: function(o, uri, c) {
  234. var io = this,
  235. f = (typeof c.form.id === 'string') ? d.getElementById(c.form.id) : c.form.id,
  236. fields;
  237.  
  238. // Initialize the HTML form properties in case they are
  239. // not defined in the HTML form.
  240. io._setAttrs(f, o.id, uri);
  241. if (c.data) {
  242. fields = io._addData(f, c.data);
  243. }
  244.  
  245. // Start polling if a callback is present and the timeout
  246. // property has been defined.
  247. if (c.timeout) {
  248. io._startUploadTimeout(o, c);
  249. Y.log('Transaction timeout started for transaction ' + o.id + '.', 'info', 'io');
  250. }
  251.  
  252. // Start file upload.
  253. f.submit();
  254. io.start(o, c);
  255. if (c.data) {
  256. io._removeData(f, fields);
  257. }
  258.  
  259. return {
  260. id: o.id,
  261. abort: function() {
  262. o.status = 0;
  263. o.statusText = 'abort';
  264. if (Y.one('#io_iframe' + o.id)) {
  265. _dFrame(o.id);
  266. io.complete(o, c);
  267. io.end(o, c);
  268. Y.log('Transaction ' + o.id + ' aborted.', 'info', 'io');
  269. }
  270. else {
  271. Y.log('Attempted to abort transaction ' + o.id + ' but transaction has completed.', 'warn', 'io');
  272. return false;
  273. }
  274. },
  275. isInProgress: function() {
  276. return Y.one('#io_iframe' + o.id) ? true : false;
  277. },
  278. io: io
  279. };
  280. },
  281.  
  282. upload: function(o, uri, c) {
  283. _cFrame(o, c, this);
  284. return this._upload(o, uri, c);
  285. },
  286.  
  287. end: function(transaction, config) {
  288. var form, io;
  289.  
  290. if (config) {
  291. form = config.form;
  292.  
  293. if (form && form.upload) {
  294. io = this;
  295.  
  296. // Restore HTML form attributes to their original values.
  297. form = (typeof form.id === 'string') ? d.getElementById(form.id) : form.id;
  298.  
  299. // Check whether the form still exists before resetting it.
  300. if (form) {
  301. io._resetAttrs(form, this._originalFormAttrs);
  302. }
  303. }
  304. }
  305.  
  306. return _end.call(this, transaction, config);
  307. }
  308. }, true);
  309.