PageRenderTime 54ms CodeModel.GetById 17ms RepoModel.GetById 1ms app.codeStats 0ms

/yabife/yabife/static/javascript/yui-2.4.1/connection/connection.js

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