PageRenderTime 27ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/build/gallery-io-multiresponse/gallery-io-multiresponse-debug.js

https://github.com/nonano/yui3-gallery
JavaScript | 372 lines | 185 code | 42 blank | 145 comment | 34 complexity | c8fcc3c449d42843d1934f16a9e9ebf8 MD5 | raw file
  1. YUI.add('gallery-io-multiresponse', function(Y) {
  2. /**
  3. * <p>Extends the IO base class to enable multiple responses using an
  4. * iframe as the transport medium.</p>
  5. *
  6. * @module io
  7. * @submodule io-multiresponse
  8. */
  9. /**
  10. * <p>Extends the IO base class to enable multiple responses using an
  11. * iframe as the transport medium. Each response fires the response event.
  12. * The only events that are fired are the start and end events.</p>
  13. *
  14. * <p>All the YUI 3 IO features are supported, so the request can be sent
  15. * as either GET (for simple query args) or POST (for anything, even file
  16. * uploads). The module uses an iframe to send the request and includes a
  17. * callback parameter. (So you cannot include a parameter named
  18. * <q>callback</q>.) For each response, the server must send a script
  19. * block invoking the callback, similar to JSONP. Unlike JSONP, however,
  20. * requests can only be made to your own server because the callback will
  21. * reference <code>window.parent</code>. In order to trigger script
  22. * parsing in all browsers, the first chunk of data that the server writes
  23. * to the connection must be at least 1024 bytes, and it must be part of
  24. * the body, so you will need to explicitly send an empty head.</p>
  25. *
  26. * <p>Due to the way that the request data is parsed, it is not safe to
  27. * send JSON-encoded data using the standard YUI 3 IO methods. However, if
  28. * you define <code>json</code> in the configuration passed to
  29. * <code>Y.io()</code>, then the data will be passed to the server under
  30. * the <code>json</code> parameter. (If you pass an object, it will be
  31. * serialized with <code>Y.JSON.stringify()</code>.)
  32. *
  33. * <p>To keep the iframe after it has finished loading, set
  34. * <code>debug:true</code> in the configuration passed to
  35. * <code>Y.io()</code>.</p>
  36. *
  37. * @class io~multiresponse
  38. */
  39. var L = Y.Lang,
  40. w = Y.config.win,
  41. d = Y.config.doc,
  42. _std = (d.documentMode && d.documentMode >= 8),
  43. _d = decodeURIComponent;
  44. /**
  45. * Creates the iframe used in file upload transactions, and binds the
  46. * response event handler.
  47. *
  48. * @method _iframe
  49. * @private
  50. * @static
  51. * @param {object} o Transaction object generated by _iframe().
  52. * @param {object} c Configuration object passed to YUI.io().
  53. * @return {void}
  54. */
  55. function _iframe(o, c, io) {
  56. var i = Y.Node.create('<iframe id="io-multi-response-' + o.id + '" name="io-multi-response-' + o.id + '" />');
  57. i._node.style.position = 'absolute';
  58. i._node.style.top = '-1000px';
  59. i._node.style.left = '-1000px';
  60. Y.one('body').appendChild(i);
  61. // Bind the onload handler to the iframe to detect the file upload response.
  62. Y.on("load", function() { io._multi_complete(o, c); }, '#io-multi-response-' + o.id);
  63. }
  64. /**
  65. * Creates a temporary form, if the caller doesn't provide one.
  66. *
  67. * @method _form
  68. * @private
  69. * @static
  70. * @param {object} c Configuration object passed to YUI.io().
  71. * @return {string} form id
  72. */
  73. function _form(c) {
  74. var id = Y.guid('io-multi-response-form-'),
  75. f = Y.Node.create('<form id="' + id + '" name="' + id + '" />');
  76. f._node.style.position = 'absolute';
  77. f._node.style.top = '-1000px';
  78. f._node.style.left = '-1000px';
  79. Y.one('body').appendChild(f);
  80. return id;
  81. }
  82. Y.mix(Y.IO.prototype, {
  83. /**
  84. * Adds JSON encoded data to the form.
  85. *
  86. * @method _addJSON
  87. * @private
  88. * @static
  89. * @param {object} f HTML form object.
  90. * @param {string|object} s The JSON data or object to serialize.
  91. * @return {array} created fields.
  92. */
  93. _addJSON: function(f, s) {
  94. if (!Y.Lang.isString(s)) {
  95. s = Y.JSON.stringify(s);
  96. }
  97. var el = d.createElement('input');
  98. el.type = 'hidden';
  99. el.name = 'json';
  100. el.value = s;
  101. f.appendChild(el);
  102. Y.log('key: json and value: ' + s + ' added as form data.', 'info', 'io');
  103. return el;
  104. },
  105. /**
  106. * Sets the appropriate attributes and values to the HTML form, in
  107. * preparation of a file upload transaction.
  108. *
  109. * @method _multi_setAttrs
  110. * @private
  111. * @static
  112. * @param {object} f HTML form object.
  113. * @param {object} id The Transaction ID.
  114. * @param {object} uri Qualified path to transaction resource.
  115. * @param {string} method POST or GET.
  116. * @return {void}
  117. */
  118. _multi_setAttrs: function(f, id, uri, method) {
  119. f.setAttribute('action', uri);
  120. f.setAttribute('method', method || 'POST');
  121. f.setAttribute('target', 'io-multi-response-' + id );
  122. f.setAttribute(Y.UA.ie && !_std ? 'encoding' : 'enctype', 'multipart/form-data');
  123. },
  124. /**
  125. * Starts timeout count if the configuration object has a defined
  126. * timeout property.
  127. *
  128. * @method _multi_startTimeout
  129. * @private
  130. * @static
  131. * @param {object} o Transaction object generated by _iframe().
  132. * @param {object} c Configuration object passed to YUI.io().
  133. * @return {void}
  134. */
  135. _multi_startTimeout: function(o, c) {
  136. var io = this;
  137. io._timeout[o.id] = w.setTimeout(
  138. function() {
  139. o.status = 0;
  140. o.statusText = 'timeout';
  141. io.end(o, c);
  142. Y.log('Transaction ' + o.id + ' timeout.', 'info', 'io');
  143. }, c.timeout);
  144. },
  145. // reuse _clearTimeout()
  146. /**
  147. * Destroy the iframe and temp form, if any.
  148. *
  149. * @method _multi_destroy
  150. * @private
  151. * @static
  152. * @param {number} id Transaction id.
  153. * @param {object} c Configuration object for the transaction.
  154. * @return {void}
  155. */
  156. _multi_destroy: function(id, c) {
  157. if (!c.debug) {
  158. Y.Event.purgeElement('#io-multi-response-' + id, false);
  159. Y.one('body').removeChild(Y.one('#io-multi-response-' + id));
  160. Y.log('The iframe for transaction ' + id + ' has been destroyed.', 'info', 'io');
  161. }
  162. if (c.form.id.indexOf('io-multi-response-form-') === 0) {
  163. Y.one('body').removeChild(Y.one('#' + c.form.id));
  164. Y.log('The temporary form for transaction ' + id + ' has been destroyed.', 'info', 'io');
  165. }
  166. },
  167. /**
  168. * Bound to the iframe's Load event and processes the response data.
  169. *
  170. * @method _multi_complete
  171. * @private
  172. * @static
  173. * @param {o} o The transaction object
  174. * @param {object} c Configuration object for the transaction.
  175. * @return {void}
  176. */
  177. _multi_complete: function(o, c) {
  178. var io = this;
  179. if (c.timeout) {
  180. io._clearTimeout(o.id);
  181. }
  182. io.end(o, c);
  183. // The transaction is complete, so call _multi_destroy to remove
  184. // the event listener bound to the iframe, and then
  185. // destroy the iframe.
  186. w.setTimeout( function() { io._multi_destroy(o.id, c); }, 0);
  187. },
  188. /**
  189. * Uploads HTML form data, inclusive of files/attachments, using the
  190. * iframe created in _iframe to facilitate the transaction.
  191. *
  192. * @method _multi_send
  193. * @private
  194. * @static
  195. * @param {o} o The transaction object
  196. * @param {object} uri Qualified path to transaction resource.
  197. * @param {object} c Configuration object for the transaction.
  198. * @return {void}
  199. */
  200. _multi_send: function(o, uri, c) {
  201. var io = this,
  202. f = (typeof c.form.id === 'string') ? d.getElementById(c.form.id) : c.form.id,
  203. // Track original HTML form attribute values.
  204. attr = {
  205. method: f.getAttribute('method'),
  206. action: f.getAttribute('action'),
  207. target: f.getAttribute('target')
  208. },
  209. fields = [];
  210. // Initialize the HTML form properties in case they are
  211. // not defined in the HTML form.
  212. io._multi_setAttrs(f, o.id, uri, c.method);
  213. if (c.data) {
  214. var data = c.data;
  215. if (L.isObject(data)) { // bug in 3.4.0: does not auto-stringify for file upload
  216. data = Y.QueryString.stringify(data);
  217. }
  218. fields = io._addData(f, data);
  219. }
  220. if (c.json) {
  221. fields.push(io._addJSON(f, c.json));
  222. }
  223. // Start polling if a callback is present and the timeout
  224. // property has been defined.
  225. if (c.timeout) {
  226. io._multi_startTimeout(o, c);
  227. Y.log('Transaction timeout started for transaction ' + o.id + '.', 'info', 'io');
  228. }
  229. // Start file upload.
  230. f.submit();
  231. io.start(o, c);
  232. if (c.data) {
  233. io._removeData(f, fields);
  234. }
  235. // Restore HTML form attributes to their original values.
  236. io._resetAttrs(f, attr);
  237. return {
  238. id: o.id,
  239. abort: function() {
  240. o.status = 0;
  241. o.statusText = 'abort';
  242. if (Y.one('#io-multi-response-' + o.id)) {
  243. io._multi_destroy(o.id, c);
  244. io.end(o, c);
  245. Y.log('Transaction ' + o.id + ' aborted.', 'info', 'io');
  246. }
  247. else {
  248. Y.log('Attempted to abort transaction ' + o.id + ' but transaction has completed.', 'warn', 'io');
  249. return false;
  250. }
  251. },
  252. isInProgress: function() {
  253. return Y.one('#io-multi-response-' + o.id) ? true : false;
  254. },
  255. io: io
  256. };
  257. }
  258. });
  259. var orig_init = Y.IO.prototype._init;
  260. Y.IO.prototype._init = function(c) {
  261. orig_init.apply(this, arguments);
  262. this.publish('io:response', Y.merge({ broadcast: 1 }, c));
  263. this.publish('io-trn:response', c);
  264. };
  265. var orig_upload = Y.IO.prototype.upload;
  266. /* @param {object} o - transaction object.
  267. * @param {string} uri - qualified path to transaction resource.
  268. * @param {object} c - configuration object for the transaction.
  269. * @return object
  270. */
  271. Y.IO.prototype.upload = function(o, uri, c) {
  272. if (!c.multiresponse) {
  273. return orig_upload.apply(this, arguments);
  274. }
  275. var io = this;
  276. YUI.Env.io_multi_response_callback[ o.id ] = function(data) {
  277. if (!data) {
  278. Y.error('Callback ' + o.id + ' invoked without data.', null, 'io');
  279. return;
  280. }
  281. // reset timeout
  282. if (c.timeout) {
  283. io._clearTimeout(o.id);
  284. io._multi_startTimeout(o, c);
  285. }
  286. if (c.on && c.on.response) {
  287. o.c = data;
  288. io._evt('response', o, c);
  289. }
  290. };
  291. var callback_value = encodeURIComponent('window.parent.YUI.Env.io_multi_response_callback[' + o.id + ']');
  292. var callback_arg = 'callback=' + callback_value;
  293. if (!c.data) {
  294. c.data = callback_arg;
  295. }
  296. else if (L.isObject(c.data)) {
  297. c.data.callback = callback_value;
  298. }
  299. else {
  300. c.data = c.data + '&' + callback_arg;
  301. }
  302. if (c.form && !c.form.id) {
  303. delete c.form;
  304. }
  305. _iframe(o, c, io);
  306. return io._multi_send(o, uri, c);
  307. };
  308. if (!YUI.Env.io_multi_response_callback)
  309. {
  310. YUI.Env.io_multi_response_callback = [];
  311. }
  312. var orig_io = Y.io;
  313. /* @param {string} uri - qualified path to transaction resource.
  314. * @param {object} c - configuration object for the transaction.
  315. * @return object
  316. */
  317. Y.io = function(uri, c) {
  318. if (c.multiresponse && !c.form) {
  319. c.form = {
  320. id: _form(c),
  321. upload: true
  322. };
  323. }
  324. else if (c.multiresponse && !c.form.upload) {
  325. c.form.upload = true;
  326. }
  327. orig_io.call(this, uri, c);
  328. };
  329. Y.mix(Y.io, orig_io);
  330. }, 'gallery-2011.08.24-23-44' ,{requires:['io-upload-iframe'], optional:['json-stringify']});