PageRenderTime 30ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/cfidescripts/ajax/yui/connection/connection.js

https://gitlab.com/marioalvarez/ferrominio
JavaScript | 1359 lines | 652 code | 147 blank | 560 comment | 136 complexity | aa936ac4dd6af135d55e8558b1f2ed0d MD5 | raw file
  1. /*
  2. Copyright (c) 2007, Yahoo! Inc. All rights reserved.
  3. Code licensed under the BSD License:
  4. http://developer.yahoo.net/yui/license.txt
  5. version: 2.3.0
  6. */
  7. /**
  8. * The Connection Manager provides a simplified interface to the XMLHttpRequest
  9. * object. It handles cross-browser instantiantion of XMLHttpRequest, negotiates the
  10. * interactive states and server response, returning the results to a pre-defined
  11. * callback you create.
  12. *
  13. * @namespace YAHOO.util
  14. * @module connection
  15. * @requires yahoo
  16. * @requires event
  17. */
  18. /**
  19. * The Connection Manager singleton provides methods for creating and managing
  20. * asynchronous transactions.
  21. *
  22. * @class Connect
  23. */
  24. YAHOO.util.Connect =
  25. {
  26. /**
  27. * @description Array of MSFT ActiveX ids for XMLHttpRequest.
  28. * @property _msxml_progid
  29. * @private
  30. * @static
  31. * @type array
  32. */
  33. _msxml_progid:[
  34. 'MSXML2.XMLHTTP.3.0',
  35. 'MSXML2.XMLHTTP',
  36. 'Microsoft.XMLHTTP'
  37. ],
  38. /**
  39. * @description Object literal of HTTP header(s)
  40. * @property _http_header
  41. * @private
  42. * @static
  43. * @type object
  44. */
  45. _http_headers:{},
  46. /**
  47. * @description Determines if HTTP headers are set.
  48. * @property _has_http_headers
  49. * @private
  50. * @static
  51. * @type boolean
  52. */
  53. _has_http_headers:false,
  54. /**
  55. * @description Determines if a default header of
  56. * Content-Type of 'application/x-www-form-urlencoded'
  57. * will be added to any client HTTP headers sent for POST
  58. * transactions.
  59. * @property _use_default_post_header
  60. * @private
  61. * @static
  62. * @type boolean
  63. */
  64. _use_default_post_header:true,
  65. /**
  66. * @description Determines if a default header of
  67. * Content-Type of 'application/x-www-form-urlencoded'
  68. * will be added to client HTTP headers sent for POST
  69. * transactions.
  70. * @property _default_post_header
  71. * @private
  72. * @static
  73. * @type boolean
  74. */
  75. _default_post_header:'application/x-www-form-urlencoded; charset=UTF-8',
  76. /**
  77. * @description Determines if a default header of
  78. * 'X-Requested-With: XMLHttpRequest'
  79. * will be added to each transaction.
  80. * @property _use_default_xhr_header
  81. * @private
  82. * @static
  83. * @type boolean
  84. */
  85. _use_default_xhr_header:true,
  86. /**
  87. * @description The default header value for the label
  88. * "X-Requested-With". This is sent with each
  89. * transaction, by default, to identify the
  90. * request as being made by YUI Connection Manager.
  91. * @property _default_xhr_header
  92. * @private
  93. * @static
  94. * @type boolean
  95. */
  96. _default_xhr_header:'XMLHttpRequest',
  97. /**
  98. * @description Determines if custom, default headers
  99. * are set for each transaction.
  100. * @property _has_default_header
  101. * @private
  102. * @static
  103. * @type boolean
  104. */
  105. _has_default_headers:true,
  106. /**
  107. * @description Determines if custom, default headers
  108. * are set for each transaction.
  109. * @property _has_default_header
  110. * @private
  111. * @static
  112. * @type boolean
  113. */
  114. _default_headers:{},
  115. /**
  116. * @description Property modified by setForm() to determine if the data
  117. * should be submitted as an HTML form.
  118. * @property _isFormSubmit
  119. * @private
  120. * @static
  121. * @type boolean
  122. */
  123. _isFormSubmit:false,
  124. /**
  125. * @description Property modified by setForm() to determine if a file(s)
  126. * upload is expected.
  127. * @property _isFileUpload
  128. * @private
  129. * @static
  130. * @type boolean
  131. */
  132. _isFileUpload:false,
  133. /**
  134. * @description Property modified by setForm() to set a reference to the HTML
  135. * form node if the desired action is file upload.
  136. * @property _formNode
  137. * @private
  138. * @static
  139. * @type object
  140. */
  141. _formNode:null,
  142. /**
  143. * @description Property modified by setForm() to set the HTML form data
  144. * for each transaction.
  145. * @property _sFormData
  146. * @private
  147. * @static
  148. * @type string
  149. */
  150. _sFormData:null,
  151. /**
  152. * @description Collection of polling references to the polling mechanism in handleReadyState.
  153. * @property _poll
  154. * @private
  155. * @static
  156. * @type object
  157. */
  158. _poll:{},
  159. /**
  160. * @description Queue of timeout values for each transaction callback with a defined timeout value.
  161. * @property _timeOut
  162. * @private
  163. * @static
  164. * @type object
  165. */
  166. _timeOut:{},
  167. /**
  168. * @description The polling frequency, in milliseconds, for HandleReadyState.
  169. * when attempting to determine a transaction's XHR readyState.
  170. * The default is 50 milliseconds.
  171. * @property _polling_interval
  172. * @private
  173. * @static
  174. * @type int
  175. */
  176. _polling_interval:50,
  177. /**
  178. * @description A transaction counter that increments the transaction id for each transaction.
  179. * @property _transaction_id
  180. * @private
  181. * @static
  182. * @type int
  183. */
  184. _transaction_id:0,
  185. /**
  186. * @description Tracks the name-value pair of the "clicked" submit button if multiple submit
  187. * buttons are present in an HTML form; and, if YAHOO.util.Event is available.
  188. * @property _submitElementValue
  189. * @private
  190. * @static
  191. * @type string
  192. */
  193. _submitElementValue:null,
  194. /**
  195. * @description Determines whether YAHOO.util.Event is available and returns true or false.
  196. * If true, an event listener is bound at the document level to trap click events that
  197. * resolve to a target type of "Submit". This listener will enable setForm() to determine
  198. * the clicked "Submit" value in a multi-Submit button, HTML form.
  199. * @property _hasSubmitListener
  200. * @private
  201. * @static
  202. */
  203. _hasSubmitListener:(function()
  204. {
  205. if(YAHOO.util.Event){
  206. YAHOO.util.Event.addListener(
  207. document,
  208. 'click',
  209. function(e){
  210. var obj = YAHOO.util.Event.getTarget(e);
  211. if(obj.type == 'submit'){
  212. YAHOO.util.Connect._submitElementValue = encodeURIComponent(obj.name) + "=" + encodeURIComponent(obj.value);
  213. }
  214. });
  215. return true;
  216. }
  217. return false;
  218. })(),
  219. /**
  220. * @description Custom event that fires at the start of a transaction
  221. * @property startEvent
  222. * @private
  223. * @static
  224. * @type CustomEvent
  225. */
  226. startEvent: new YAHOO.util.CustomEvent('start'),
  227. /**
  228. * @description Custom event that fires when a transaction response has completed.
  229. * @property completeEvent
  230. * @private
  231. * @static
  232. * @type CustomEvent
  233. */
  234. completeEvent: new YAHOO.util.CustomEvent('complete'),
  235. /**
  236. * @description Custom event that fires when handleTransactionResponse() determines a
  237. * response in the HTTP 2xx range.
  238. * @property successEvent
  239. * @private
  240. * @static
  241. * @type CustomEvent
  242. */
  243. successEvent: new YAHOO.util.CustomEvent('success'),
  244. /**
  245. * @description Custom event that fires when handleTransactionResponse() determines a
  246. * response in the HTTP 4xx/5xx range.
  247. * @property failureEvent
  248. * @private
  249. * @static
  250. * @type CustomEvent
  251. */
  252. failureEvent: new YAHOO.util.CustomEvent('failure'),
  253. /**
  254. * @description Custom event that fires when handleTransactionResponse() determines a
  255. * response in the HTTP 4xx/5xx range.
  256. * @property failureEvent
  257. * @private
  258. * @static
  259. * @type CustomEvent
  260. */
  261. uploadEvent: new YAHOO.util.CustomEvent('upload'),
  262. /**
  263. * @description Custom event that fires when a transaction is successfully aborted.
  264. * @property abortEvent
  265. * @private
  266. * @static
  267. * @type CustomEvent
  268. */
  269. abortEvent: new YAHOO.util.CustomEvent('abort'),
  270. /**
  271. * @description A reference table that maps callback custom events members to its specific
  272. * event name.
  273. * @property _customEvents
  274. * @private
  275. * @static
  276. * @type object
  277. */
  278. _customEvents:
  279. {
  280. onStart:['startEvent', 'start'],
  281. onComplete:['completeEvent', 'complete'],
  282. onSuccess:['successEvent', 'success'],
  283. onFailure:['failureEvent', 'failure'],
  284. onUpload:['uploadEvent', 'upload'],
  285. onAbort:['abortEvent', 'abort']
  286. },
  287. /**
  288. * @description Member to add an ActiveX id to the existing xml_progid array.
  289. * In the event(unlikely) a new ActiveX id is introduced, it can be added
  290. * without internal code modifications.
  291. * @method setProgId
  292. * @public
  293. * @static
  294. * @param {string} id The ActiveX id to be added to initialize the XHR object.
  295. * @return void
  296. */
  297. setProgId:function(id)
  298. {
  299. this._msxml_progid.unshift(id);
  300. },
  301. /**
  302. * @description Member to enable or disable the default POST header.
  303. * @method setDefaultPostHeader
  304. * @public
  305. * @static
  306. * @param {boolean} b Set and use default header - true or false .
  307. * @return void
  308. */
  309. setDefaultPostHeader:function(b)
  310. {
  311. this._use_default_post_header = b;
  312. },
  313. /**
  314. * @description Member to enable or disable the default POST header.
  315. * @method setDefaultXhrHeader
  316. * @public
  317. * @static
  318. * @param {boolean} b Set and use default header - true or false .
  319. * @return void
  320. */
  321. setDefaultXhrHeader:function(b)
  322. {
  323. this._use_default_xhr_header = b;
  324. },
  325. /**
  326. * @description Member to modify the default polling interval.
  327. * @method setPollingInterval
  328. * @public
  329. * @static
  330. * @param {int} i The polling interval in milliseconds.
  331. * @return void
  332. */
  333. setPollingInterval:function(i)
  334. {
  335. if(typeof i == 'number' && isFinite(i)){
  336. this._polling_interval = i;
  337. }
  338. },
  339. /**
  340. * @description Instantiates a XMLHttpRequest object and returns an object with two properties:
  341. * the XMLHttpRequest instance and the transaction id.
  342. * @method createXhrObject
  343. * @private
  344. * @static
  345. * @param {int} transactionId Property containing the transaction id for this transaction.
  346. * @return object
  347. */
  348. createXhrObject:function(transactionId)
  349. {
  350. var obj,http;
  351. try
  352. {
  353. // Instantiates XMLHttpRequest in non-IE browsers and assigns to http.
  354. http = new XMLHttpRequest();
  355. // Object literal with http and tId properties
  356. obj = { conn:http, tId:transactionId };
  357. }
  358. catch(e)
  359. {
  360. for(var i=0; i<this._msxml_progid.length; ++i){
  361. try
  362. {
  363. // Instantiates XMLHttpRequest for IE and assign to http.
  364. http = new ActiveXObject(this._msxml_progid[i]);
  365. // Object literal with conn and tId properties
  366. obj = { conn:http, tId:transactionId };
  367. break;
  368. }
  369. catch(e){}
  370. }
  371. }
  372. finally
  373. {
  374. return obj;
  375. }
  376. },
  377. /**
  378. * @description This method is called by asyncRequest to create a
  379. * valid connection object for the transaction. It also passes a
  380. * transaction id and increments the transaction id counter.
  381. * @method getConnectionObject
  382. * @private
  383. * @static
  384. * @return {object}
  385. */
  386. getConnectionObject:function(isFileUpload)
  387. {
  388. var o;
  389. var tId = this._transaction_id;
  390. try
  391. {
  392. if(!isFileUpload){
  393. o = this.createXhrObject(tId);
  394. }
  395. else{
  396. o = {};
  397. o.tId = tId;
  398. o.isUpload = true;
  399. }
  400. if(o){
  401. this._transaction_id++;
  402. }
  403. }
  404. catch(e){}
  405. finally
  406. {
  407. return o;
  408. }
  409. },
  410. /**
  411. * @description Method for initiating an asynchronous request via the XHR object.
  412. * @method asyncRequest
  413. * @public
  414. * @static
  415. * @param {string} method HTTP transaction method
  416. * @param {string} uri Fully qualified path of resource
  417. * @param {callback} callback User-defined callback function or object
  418. * @param {string} postData POST body
  419. * @return {object} Returns the connection object
  420. */
  421. asyncRequest:function(method, uri, callback, postData)
  422. {
  423. var o = (this._isFileUpload)?this.getConnectionObject(true):this.getConnectionObject();
  424. if(!o){
  425. return null;
  426. }
  427. else{
  428. // Intialize any transaction-specific custom events, if provided.
  429. if(callback && callback.customevents){
  430. this.initCustomEvents(o, callback);
  431. }
  432. if(this._isFormSubmit){
  433. if(this._isFileUpload){
  434. this.uploadFile(o, callback, uri, postData);
  435. return o;
  436. }
  437. // If the specified HTTP method is GET, setForm() will return an
  438. // encoded string that is concatenated to the uri to
  439. // create a querystring.
  440. if(method.toUpperCase() == 'GET'){
  441. if(this._sFormData.length !== 0){
  442. // If the URI already contains a querystring, append an ampersand
  443. // and then concatenate _sFormData to the URI.
  444. uri += ((uri.indexOf('?') == -1)?'?':'&') + this._sFormData;
  445. }
  446. else{
  447. uri += "?" + this._sFormData;
  448. }
  449. }
  450. else if(method.toUpperCase() == 'POST'){
  451. // If POST data exist in addition to the HTML form data,
  452. // it will be concatenated to the form data.
  453. postData = postData?this._sFormData + "&" + postData:this._sFormData;
  454. }
  455. }
  456. o.conn.open(method, uri, true);
  457. //this.processTransactionHeaders(o);
  458. // Each transaction will automatically include a custom header of
  459. // "X-Requested-With: XMLHttpRequest" to identify the request as
  460. // having originated from Connection Manager.
  461. if(this._use_default_xhr_header){
  462. if(!this._default_headers['X-Requested-With']){
  463. this.initHeader('X-Requested-With', this._default_xhr_header, true);
  464. }
  465. }
  466. if(this._isFormSubmit || (postData && this._use_default_post_header)){
  467. this.initHeader('Content-Type', this._default_post_header);
  468. if(this._isFormSubmit){
  469. this.resetFormState();
  470. }
  471. }
  472. if(this._has_default_headers || this._has_http_headers){
  473. this.setHeader(o);
  474. }
  475. this.handleReadyState(o, callback);
  476. o.conn.send(postData || null);
  477. // Fire global custom event -- startEvent
  478. this.startEvent.fire(o);
  479. if(o.startEvent){
  480. // Fire transaction custom event -- startEvent
  481. o.startEvent.fire(o);
  482. }
  483. return o;
  484. }
  485. },
  486. /**
  487. * @description This method creates and subscribes custom events,
  488. * specific to each transaction
  489. * @method initCustomEvents
  490. * @private
  491. * @static
  492. * @param {object} o The connection object
  493. * @param {callback} callback The user-defined callback object
  494. * @return {void}
  495. */
  496. initCustomEvents:function(o, callback)
  497. {
  498. // Enumerate through callback.customevents members and bind/subscribe
  499. // events that match in the _customEvents table.
  500. for(var prop in callback.customevents){
  501. if(this._customEvents[prop][0]){
  502. // Create the custom event
  503. o[this._customEvents[prop][0]] = new YAHOO.util.CustomEvent(this._customEvents[prop][1], (callback.scope)?callback.scope:null);
  504. // Subscribe the custom event
  505. o[this._customEvents[prop][0]].subscribe(callback.customevents[prop]);
  506. }
  507. }
  508. },
  509. /**
  510. * @description This method serves as a timer that polls the XHR object's readyState
  511. * property during a transaction, instead of binding a callback to the
  512. * onreadystatechange event. Upon readyState 4, handleTransactionResponse
  513. * will process the response, and the timer will be cleared.
  514. * @method handleReadyState
  515. * @private
  516. * @static
  517. * @param {object} o The connection object
  518. * @param {callback} callback The user-defined callback object
  519. * @return {void}
  520. */
  521. handleReadyState:function(o, callback)
  522. {
  523. var oConn = this;
  524. if(callback && callback.timeout){
  525. this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
  526. }
  527. this._poll[o.tId] = window.setInterval(
  528. function(){
  529. if(o.conn && o.conn.readyState === 4){
  530. // Clear the polling interval for the transaction
  531. // and remove the reference from _poll.
  532. window.clearInterval(oConn._poll[o.tId]);
  533. delete oConn._poll[o.tId];
  534. if(callback && callback.timeout){
  535. window.clearTimeout(oConn._timeOut[o.tId]);
  536. delete oConn._timeOut[o.tId];
  537. }
  538. // Fire global custom event -- completeEvent
  539. oConn.completeEvent.fire(o);
  540. if(o.completeEvent){
  541. // Fire transaction custom event -- completeEvent
  542. o.completeEvent.fire(o);
  543. }
  544. oConn.handleTransactionResponse(o, callback);
  545. }
  546. }
  547. ,this._polling_interval);
  548. },
  549. /**
  550. * @description This method attempts to interpret the server response and
  551. * determine whether the transaction was successful, or if an error or
  552. * exception was encountered.
  553. * @method handleTransactionResponse
  554. * @private
  555. * @static
  556. * @param {object} o The connection object
  557. * @param {object} callback The user-defined callback object
  558. * @param {boolean} isAbort Determines if the transaction was terminated via abort().
  559. * @return {void}
  560. */
  561. handleTransactionResponse:function(o, callback, isAbort)
  562. {
  563. // If no valid callback is provided, then do not process any callback handling.
  564. if(!callback){
  565. this.releaseObject(o);
  566. return;
  567. }
  568. var httpStatus, responseObject;
  569. try
  570. {
  571. if(o.conn.status !== undefined && o.conn.status !== 0){
  572. httpStatus = o.conn.status;
  573. }
  574. else{
  575. httpStatus = 13030;
  576. }
  577. }
  578. catch(e){
  579. // 13030 is the custom code to indicate the condition -- in Mozilla/FF --
  580. // when the o object's status and statusText properties are
  581. // unavailable, and a query attempt throws an exception.
  582. httpStatus = 13030;
  583. }
  584. if(httpStatus >= 200 && httpStatus < 300 || httpStatus === 1223){
  585. responseObject = this.createResponseObject(o, callback.argument);
  586. if(callback.success){
  587. if(!callback.scope){
  588. callback.success(responseObject);
  589. }
  590. else{
  591. // If a scope property is defined, the callback will be fired from
  592. // the context of the object.
  593. callback.success.apply(callback.scope, [responseObject]);
  594. }
  595. }
  596. // Fire global custom event -- successEvent
  597. this.successEvent.fire(responseObject);
  598. if(o.successEvent){
  599. // Fire transaction custom event -- successEvent
  600. o.successEvent.fire(responseObject);
  601. }
  602. }
  603. else{
  604. switch(httpStatus){
  605. // The following cases are wininet.dll error codes that may be encountered.
  606. case 12002: // Server timeout
  607. case 12029: // 12029 to 12031 correspond to dropped connections.
  608. case 12030:
  609. case 12031:
  610. case 12152: // Connection closed by server.
  611. case 13030: // See above comments for variable status.
  612. responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort?isAbort:false));
  613. if(callback.failure){
  614. if(!callback.scope){
  615. callback.failure(responseObject);
  616. }
  617. else{
  618. callback.failure.apply(callback.scope, [responseObject]);
  619. }
  620. }
  621. break;
  622. default:
  623. responseObject = this.createResponseObject(o, callback.argument);
  624. if(callback.failure){
  625. if(!callback.scope){
  626. callback.failure(responseObject);
  627. }
  628. else{
  629. callback.failure.apply(callback.scope, [responseObject]);
  630. }
  631. }
  632. }
  633. // Fire global custom event -- failureEvent
  634. this.failureEvent.fire(responseObject);
  635. if(o.failureEvent){
  636. // Fire transaction custom event -- failureEvent
  637. o.failureEvent.fire(responseObject);
  638. }
  639. }
  640. this.releaseObject(o);
  641. responseObject = null;
  642. },
  643. /**
  644. * @description This method evaluates the server response, creates and returns the results via
  645. * its properties. Success and failure cases will differ in the response
  646. * object's property values.
  647. * @method createResponseObject
  648. * @private
  649. * @static
  650. * @param {object} o The connection object
  651. * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
  652. * @return {object}
  653. */
  654. createResponseObject:function(o, callbackArg)
  655. {
  656. var obj = {};
  657. var headerObj = {};
  658. try
  659. {
  660. var headerStr = o.conn.getAllResponseHeaders();
  661. var header = headerStr.split('\n');
  662. for(var i=0; i<header.length; i++){
  663. var delimitPos = header[i].indexOf(':');
  664. if(delimitPos != -1){
  665. headerObj[header[i].substring(0,delimitPos)] = header[i].substring(delimitPos+2);
  666. }
  667. }
  668. }
  669. catch(e){}
  670. obj.tId = o.tId;
  671. // Normalize IE's response to HTTP 204 when Win error 1223.
  672. obj.status = (o.conn.status == 1223)?204:o.conn.status;
  673. // Normalize IE's statusText to "No Content" instead of "Unknown".
  674. obj.statusText = (o.conn.status == 1223)?"No Content":o.conn.statusText;
  675. obj.getResponseHeader = headerObj;
  676. obj.getAllResponseHeaders = headerStr;
  677. obj.responseText = o.conn.responseText;
  678. obj.responseXML = o.conn.responseXML;
  679. if(typeof callbackArg !== undefined){
  680. obj.argument = callbackArg;
  681. }
  682. return obj;
  683. },
  684. /**
  685. * @description If a transaction cannot be completed due to dropped or closed connections,
  686. * there may be not be enough information to build a full response object.
  687. * The failure callback will be fired and this specific condition can be identified
  688. * by a status property value of 0.
  689. *
  690. * If an abort was successful, the status property will report a value of -1.
  691. *
  692. * @method createExceptionObject
  693. * @private
  694. * @static
  695. * @param {int} tId The Transaction Id
  696. * @param {callbackArg} callbackArg The user-defined argument or arguments to be passed to the callback
  697. * @param {boolean} isAbort Determines if the exception case is caused by a transaction abort
  698. * @return {object}
  699. */
  700. createExceptionObject:function(tId, callbackArg, isAbort)
  701. {
  702. var COMM_CODE = 0;
  703. var COMM_ERROR = 'communication failure';
  704. var ABORT_CODE = -1;
  705. var ABORT_ERROR = 'transaction aborted';
  706. var obj = {};
  707. obj.tId = tId;
  708. if(isAbort){
  709. obj.status = ABORT_CODE;
  710. obj.statusText = ABORT_ERROR;
  711. }
  712. else{
  713. obj.status = COMM_CODE;
  714. obj.statusText = COMM_ERROR;
  715. }
  716. if(callbackArg){
  717. obj.argument = callbackArg;
  718. }
  719. return obj;
  720. },
  721. /**
  722. * @description Method that initializes the custom HTTP headers for the each transaction.
  723. * @method initHeader
  724. * @public
  725. * @static
  726. * @param {string} label The HTTP header label
  727. * @param {string} value The HTTP header value
  728. * @param {string} isDefault Determines if the specific header is a default header
  729. * automatically sent with each transaction.
  730. * @return {void}
  731. */
  732. initHeader:function(label,value,isDefault)
  733. {
  734. var headerObj = (isDefault)?this._default_headers:this._http_headers;
  735. if(headerObj[label] === undefined){
  736. headerObj[label] = value;
  737. }
  738. else{
  739. // Concatenate multiple values, comma-delimited,
  740. // for the same header label,
  741. headerObj[label] = value + "," + headerObj[label];
  742. }
  743. if(isDefault){
  744. this._has_default_headers = true;
  745. }
  746. else{
  747. this._has_http_headers = true;
  748. }
  749. },
  750. /**
  751. * @description Accessor that sets the HTTP headers for each transaction.
  752. * @method setHeader
  753. * @private
  754. * @static
  755. * @param {object} o The connection object for the transaction.
  756. * @return {void}
  757. */
  758. setHeader:function(o)
  759. {
  760. if(this._has_default_headers){
  761. for(var prop in this._default_headers){
  762. if(YAHOO.lang.hasOwnProperty(this._default_headers, prop)){
  763. o.conn.setRequestHeader(prop, this._default_headers[prop]);
  764. }
  765. }
  766. }
  767. if(this._has_http_headers){
  768. for(var prop in this._http_headers){
  769. if(YAHOO.lang.hasOwnProperty(this._http_headers, prop)){
  770. o.conn.setRequestHeader(prop, this._http_headers[prop]);
  771. }
  772. }
  773. delete this._http_headers;
  774. this._http_headers = {};
  775. this._has_http_headers = false;
  776. }
  777. },
  778. /**
  779. * @description Resets the default HTTP headers object
  780. * @method resetDefaultHeaders
  781. * @public
  782. * @static
  783. * @return {void}
  784. */
  785. resetDefaultHeaders:function(){
  786. delete this._default_headers;
  787. this._default_headers = {};
  788. this._has_default_headers = false;
  789. },
  790. /**
  791. * @description This method assembles the form label and value pairs and
  792. * constructs an encoded string.
  793. * asyncRequest() will automatically initialize the transaction with a
  794. * a HTTP header Content-Type of application/x-www-form-urlencoded.
  795. * @method setForm
  796. * @public
  797. * @static
  798. * @param {string || object} form id or name attribute, or form object.
  799. * @param {boolean} optional enable file upload.
  800. * @param {boolean} optional enable file upload over SSL in IE only.
  801. * @return {string} string of the HTML form field name and value pairs..
  802. */
  803. setForm:function(formId, isUpload, secureUri)
  804. {
  805. this.resetFormState();
  806. var oForm;
  807. if(typeof formId == 'string'){
  808. // Determine if the argument is a form id or a form name.
  809. // Note form name usage is deprecated by supported
  810. // here for legacy reasons.
  811. oForm = (document.getElementById(formId) || document.forms[formId]);
  812. }
  813. else if(typeof formId == 'object'){
  814. // Treat argument as an HTML form object.
  815. oForm = formId;
  816. }
  817. else{
  818. return;
  819. }
  820. // If the isUpload argument is true, setForm will call createFrame to initialize
  821. // an iframe as the form target.
  822. //
  823. // The argument secureURI is also required by IE in SSL environments
  824. // where the secureURI string is a fully qualified HTTP path, used to set the source
  825. // of the iframe, to a stub resource in the same domain.
  826. if(isUpload){
  827. // Create iframe in preparation for file upload.
  828. var io = this.createFrame(secureUri?secureUri:null);
  829. // Set form reference and file upload properties to true.
  830. this._isFormSubmit = true;
  831. this._isFileUpload = true;
  832. this._formNode = oForm;
  833. return;
  834. }
  835. var oElement, oName, oValue, oDisabled;
  836. var hasSubmit = false;
  837. // Iterate over the form elements collection to construct the
  838. // label-value pairs.
  839. for (var i=0; i<oForm.elements.length; i++){
  840. oElement = oForm.elements[i];
  841. oDisabled = oForm.elements[i].disabled;
  842. oName = oForm.elements[i].name;
  843. oValue = oForm.elements[i].value;
  844. // Do not submit fields that are disabled or
  845. // do not have a name attribute value.
  846. if(!oDisabled && oName)
  847. {
  848. switch(oElement.type)
  849. {
  850. case 'select-one':
  851. case 'select-multiple':
  852. for(var j=0; j<oElement.options.length; j++){
  853. if(oElement.options[j].selected){
  854. if(window.ActiveXObject){
  855. this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].attributes['value'].specified?oElement.options[j].value:oElement.options[j].text) + '&';
  856. }
  857. else{
  858. this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oElement.options[j].hasAttribute('value')?oElement.options[j].value:oElement.options[j].text) + '&';
  859. }
  860. }
  861. }
  862. break;
  863. case 'radio':
  864. case 'checkbox':
  865. if(oElement.checked){
  866. this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
  867. }
  868. break;
  869. case 'file':
  870. // stub case as XMLHttpRequest will only send the file path as a string.
  871. case undefined:
  872. // stub case for fieldset element which returns undefined.
  873. case 'reset':
  874. // stub case for input type reset button.
  875. case 'button':
  876. // stub case for input type button elements.
  877. break;
  878. case 'submit':
  879. if(hasSubmit === false){
  880. if(this._hasSubmitListener && this._submitElementValue){
  881. this._sFormData += this._submitElementValue + '&';
  882. }
  883. else{
  884. this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
  885. }
  886. hasSubmit = true;
  887. }
  888. break;
  889. default:
  890. this._sFormData += encodeURIComponent(oName) + '=' + encodeURIComponent(oValue) + '&';
  891. }
  892. }
  893. }
  894. this._isFormSubmit = true;
  895. this._sFormData = this._sFormData.substr(0, this._sFormData.length - 1);
  896. return this._sFormData;
  897. },
  898. /**
  899. * @description Resets HTML form properties when an HTML form or HTML form
  900. * with file upload transaction is sent.
  901. * @method resetFormState
  902. * @private
  903. * @static
  904. * @return {void}
  905. */
  906. resetFormState:function(){
  907. this._isFormSubmit = false;
  908. this._isFileUpload = false;
  909. this._formNode = null;
  910. this._sFormData = "";
  911. },
  912. /**
  913. * @description Creates an iframe to be used for form file uploads. It is remove from the
  914. * document upon completion of the upload transaction.
  915. * @method createFrame
  916. * @private
  917. * @static
  918. * @param {string} optional qualified path of iframe resource for SSL in IE.
  919. * @return {void}
  920. */
  921. createFrame:function(secureUri){
  922. // IE does not allow the setting of id and name attributes as object
  923. // properties via createElement(). A different iframe creation
  924. // pattern is required for IE.
  925. var frameId = 'yuiIO' + this._transaction_id;
  926. var io;
  927. if(window.ActiveXObject){
  928. io = document.createElement('<iframe id="' + frameId + '" name="' + frameId + '" />');
  929. // IE will throw a security exception in an SSL environment if the
  930. // iframe source is undefined.
  931. if(typeof secureUri == 'boolean'){
  932. io.src = 'javascript:false';
  933. }
  934. else if(typeof secureURI == 'string'){
  935. // Deprecated
  936. io.src = secureUri;
  937. }
  938. }
  939. else{
  940. io = document.createElement('iframe');
  941. io.id = frameId;
  942. io.name = frameId;
  943. }
  944. io.style.position = 'absolute';
  945. io.style.top = '-1000px';
  946. io.style.left = '-1000px';
  947. document.body.appendChild(io);
  948. },
  949. /**
  950. * @description Parses the POST data and creates hidden form elements
  951. * for each key-value, and appends them to the HTML form object.
  952. * @method appendPostData
  953. * @private
  954. * @static
  955. * @param {string} postData The HTTP POST data
  956. * @return {array} formElements Collection of hidden fields.
  957. */
  958. appendPostData:function(postData)
  959. {
  960. var formElements = [];
  961. var postMessage = postData.split('&');
  962. for(var i=0; i < postMessage.length; i++){
  963. var delimitPos = postMessage[i].indexOf('=');
  964. if(delimitPos != -1){
  965. formElements[i] = document.createElement('input');
  966. formElements[i].type = 'hidden';
  967. formElements[i].name = postMessage[i].substring(0,delimitPos);
  968. formElements[i].value = postMessage[i].substring(delimitPos+1);
  969. this._formNode.appendChild(formElements[i]);
  970. }
  971. }
  972. return formElements;
  973. },
  974. /**
  975. * @description Uploads HTML form, inclusive of files/attachments, using the
  976. * iframe created in createFrame to facilitate the transaction.
  977. * @method uploadFile
  978. * @private
  979. * @static
  980. * @param {int} id The transaction id.
  981. * @param {object} callback User-defined callback object.
  982. * @param {string} uri Fully qualified path of resource.
  983. * @param {string} postData POST data to be submitted in addition to HTML form.
  984. * @return {void}
  985. */
  986. uploadFile:function(o, callback, uri, postData){
  987. // Each iframe has an id prefix of "yuiIO" followed
  988. // by the unique transaction id.
  989. var frameId = 'yuiIO' + o.tId;
  990. var uploadEncoding = 'multipart/form-data';
  991. var io = document.getElementById(frameId);
  992. var oConn = this;
  993. // Track original HTML form attribute values.
  994. var rawFormAttributes =
  995. {
  996. action:this._formNode.getAttribute('action'),
  997. method:this._formNode.getAttribute('method'),
  998. target:this._formNode.getAttribute('target')
  999. };
  1000. // Initialize the HTML form properties in case they are
  1001. // not defined in the HTML form.
  1002. this._formNode.setAttribute('action', uri);
  1003. this._formNode.setAttribute('method', 'POST');
  1004. this._formNode.setAttribute('target', frameId);
  1005. if(this._formNode.encoding){
  1006. // IE does not respect property enctype for HTML forms.
  1007. // Instead it uses the property - "encoding".
  1008. this._formNode.setAttribute('encoding', uploadEncoding);
  1009. }
  1010. else{
  1011. this._formNode.setAttribute('enctype', uploadEncoding);
  1012. }
  1013. if(postData){
  1014. var oElements = this.appendPostData(postData);
  1015. }
  1016. // Start file upload.
  1017. this._formNode.submit();
  1018. // Fire global custom event -- startEvent
  1019. this.startEvent.fire(o);
  1020. if(o.startEvent){
  1021. // Fire transaction custom event -- startEvent
  1022. o.startEvent.fire(o);
  1023. }
  1024. // Start polling if a callback is present and the timeout
  1025. // property has been defined.
  1026. if(callback && callback.timeout){
  1027. this._timeOut[o.tId] = window.setTimeout(function(){ oConn.abort(o, callback, true); }, callback.timeout);
  1028. }
  1029. // Remove HTML elements created by appendPostData
  1030. if(oElements && oElements.length > 0){
  1031. for(var i=0; i < oElements.length; i++){
  1032. this._formNode.removeChild(oElements[i]);
  1033. }
  1034. }
  1035. // Restore HTML form attributes to their original
  1036. // values prior to file upload.
  1037. for(var prop in rawFormAttributes){
  1038. if(YAHOO.lang.hasOwnProperty(rawFormAttributes, prop)){
  1039. if(rawFormAttributes[prop]){
  1040. this._formNode.setAttribute(prop, rawFormAttributes[prop]);
  1041. }
  1042. else{
  1043. this._formNode.removeAttribute(prop);
  1044. }
  1045. }
  1046. }
  1047. // Reset HTML form state properties.
  1048. this.resetFormState();
  1049. // Create the upload callback handler that fires when the iframe
  1050. // receives the load event. Subsequently, the event handler is detached
  1051. // and the iframe removed from the document.
  1052. var uploadCallback = function()
  1053. {
  1054. if(callback && callback.timeout){
  1055. window.clearTimeout(oConn._timeOut[o.tId]);
  1056. delete oConn._timeOut[o.tId];
  1057. }
  1058. // Fire global custom event -- completeEvent
  1059. oConn.completeEvent.fire(o);
  1060. if(o.completeEvent){
  1061. // Fire transaction custom event -- completeEvent
  1062. o.completeEvent.fire(o);
  1063. }
  1064. var obj = {};
  1065. obj.tId = o.tId;
  1066. obj.argument = callback.argument;
  1067. try
  1068. {
  1069. // responseText and responseXML will be populated with the same data from the iframe.
  1070. // Since the HTTP headers cannot be read from the iframe
  1071. obj.responseText = io.contentWindow.document.body?io.contentWindow.document.body.innerHTML:io.contentWindow.document.documentElement.textContent;
  1072. obj.responseXML = io.contentWindow.document.XMLDocument?io.contentWindow.document.XMLDocument:io.contentWindow.document;
  1073. }
  1074. catch(e){}
  1075. if(callback && callback.upload){
  1076. if(!callback.scope){
  1077. callback.upload(obj);
  1078. }
  1079. else{
  1080. callback.upload.apply(callback.scope, [obj]);
  1081. }
  1082. }
  1083. // Fire global custom event -- completeEvent
  1084. oConn.uploadEvent.fire(obj);
  1085. if(o.uploadEvent){
  1086. // Fire transaction custom event -- completeEvent
  1087. o.uploadEvent.fire(obj);
  1088. }
  1089. if(YAHOO.util.Event){
  1090. YAHOO.util.Event.removeListener(io, "load", uploadCallback);
  1091. }
  1092. else if(window.detachEvent){
  1093. io.detachEvent('onload', uploadCallback);
  1094. }
  1095. else{
  1096. io.removeEventListener('load', uploadCallback, false);
  1097. }
  1098. setTimeout(
  1099. function(){
  1100. document.body.removeChild(io);
  1101. oConn.releaseObject(o);
  1102. }, 100);
  1103. };
  1104. // Bind the onload handler to the iframe to detect the file upload response.
  1105. if(YAHOO.util.Event){
  1106. YAHOO.util.Event.addListener(io, "load", uploadCallback);
  1107. }
  1108. else if(window.attachEvent){
  1109. io.attachEvent('onload', uploadCallback);
  1110. }
  1111. else{
  1112. io.addEventListener('load', uploadCallback, false);
  1113. }
  1114. },
  1115. /**
  1116. * @description Method to terminate a transaction, if it has not reached readyState 4.
  1117. * @method abort
  1118. * @public
  1119. * @static
  1120. * @param {object} o The connection object returned by asyncRequest.
  1121. * @param {object} callback User-defined callback object.
  1122. * @param {string} isTimeout boolean to indicate if abort resulted from a callback timeout.
  1123. * @return {boolean}
  1124. */
  1125. abort:function(o, callback, isTimeout)
  1126. {
  1127. var abortStatus;
  1128. if(o.conn){
  1129. if(this.isCallInProgress(o)){
  1130. // Issue abort request
  1131. o.conn.abort();
  1132. window.clearInterval(this._poll[o.tId]);
  1133. delete this._poll[o.tId];
  1134. if(isTimeout){
  1135. window.clearTimeout(this._timeOut[o.tId]);
  1136. delete this._timeOut[o.tId];
  1137. }
  1138. abortStatus = true;
  1139. }
  1140. }
  1141. else if(o.isUpload === true){
  1142. var frameId = 'yuiIO' + o.tId;
  1143. var io = document.getElementById(frameId);
  1144. if(io){
  1145. // Destroy the iframe facilitating the transaction.
  1146. document.body.removeChild(io);
  1147. if(isTimeout){
  1148. window.clearTimeout(this._timeOut[o.tId]);
  1149. delete this._timeOut[o.tId];
  1150. }
  1151. abortStatus = true;
  1152. }
  1153. }
  1154. else{
  1155. abortStatus = false;
  1156. }
  1157. if(abortStatus === true){
  1158. // Fire global custom event -- abortEvent
  1159. this.abortEvent.fire(o);
  1160. if(o.abortEvent){
  1161. // Fire transaction custom event -- abortEvent
  1162. o.abortEvent.fire(o);
  1163. }
  1164. this.handleTransactionResponse(o, callback, true);
  1165. }
  1166. else{
  1167. }
  1168. return abortStatus;
  1169. },
  1170. /**
  1171. * Public method to check if the transaction is still being processed.
  1172. *
  1173. * @method isCallInProgress
  1174. * @public
  1175. * @static
  1176. * @param {object} o The connection object returned by asyncRequest
  1177. * @return {boolean}
  1178. */
  1179. isCallInProgress:function(o)
  1180. {
  1181. // if the XHR object assigned to the transaction has not been dereferenced,
  1182. // then check its readyState status. Otherwise, return false.
  1183. if(o && o.conn){
  1184. return o.conn.readyState !== 4 && o.conn.readyState !== 0;
  1185. }
  1186. else if(o && o.isUpload === true){
  1187. var frameId = 'yuiIO' + o.tId;
  1188. return document.getElementById(frameId)?true:false;
  1189. }
  1190. else{
  1191. return false;
  1192. }
  1193. },
  1194. /**
  1195. * @description Dereference the XHR instance and the connection object after the transaction is completed.
  1196. * @method releaseObject
  1197. * @private
  1198. * @static
  1199. * @param {object} o The connection object
  1200. * @return {void}
  1201. */
  1202. releaseObject:function(o)
  1203. {
  1204. //dereference the XHR instance.
  1205. if(o.conn){
  1206. o.conn = null;
  1207. }
  1208. //dereference the connection object.
  1209. o = null;
  1210. }
  1211. };
  1212. YAHOO.register("connection", YAHOO.util.Connect, {version: "2.3.0", build: "442"});