/hippo/src/main/webapp/yui/connection/connection.js

http://hdbc.googlecode.com/ · JavaScript · 1376 lines · 650 code · 156 blank · 570 comment · 154 complexity · 6000c27aea72e3c980ce882a2b66e0bb MD5 · raw file

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