PageRenderTime 58ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/hudson-war/src/main/webapp/scripts/yui/connection/connection-debug.js

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