jsxmlhttpdatasource.js 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. // Copyright 2006 The Closure Library Authors. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS-IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. /**
  15. * @fileoverview
  16. * DataSource implementation that uses XMLHttpRequest as transport, with
  17. * response as valid JSON.
  18. *
  19. * Response can have unexecutable starting/ending text to prevent inclusion
  20. * using <script src="...">
  21. *
  22. */
  23. goog.provide('goog.ds.JsXmlHttpDataSource');
  24. goog.require('goog.Uri');
  25. goog.require('goog.ds.DataManager');
  26. goog.require('goog.ds.FastDataNode');
  27. goog.require('goog.ds.LoadState');
  28. goog.require('goog.ds.logger');
  29. goog.require('goog.events');
  30. goog.require('goog.json');
  31. goog.require('goog.log');
  32. goog.require('goog.net.EventType');
  33. goog.require('goog.net.XhrIo');
  34. /**
  35. * Similar to JsonDataSource, with using XMLHttpRequest for transport
  36. * Currently requires the result be a valid JSON.
  37. *
  38. * @param {(string|goog.Uri)} uri URI for the request.
  39. * @param {string} name Name of the datasource.
  40. * @param {string=} opt_startText Text to expect/strip before JS response.
  41. * @param {string=} opt_endText Text to expect/strip after JS response.
  42. * @param {boolean=} opt_usePost If true, use POST. Defaults to false (GET).
  43. *
  44. * @extends {goog.ds.FastDataNode}
  45. * @constructor
  46. * @final
  47. */
  48. goog.ds.JsXmlHttpDataSource = function(
  49. uri, name, opt_startText, opt_endText, opt_usePost) {
  50. goog.ds.FastDataNode.call(this, {}, name, null);
  51. if (uri) {
  52. this.uri_ = new goog.Uri(uri);
  53. this.xhr_ = new goog.net.XhrIo();
  54. this.usePost_ = !!opt_usePost;
  55. goog.events.listen(
  56. this.xhr_, goog.net.EventType.COMPLETE, this.completed_, false, this);
  57. } else {
  58. this.uri_ = null;
  59. }
  60. this.startText_ = opt_startText;
  61. this.endText_ = opt_endText;
  62. };
  63. goog.inherits(goog.ds.JsXmlHttpDataSource, goog.ds.FastDataNode);
  64. /**
  65. * Delimiter for start of JSON data in response.
  66. * null = starts at first character of response
  67. * @type {string|undefined}
  68. * @private
  69. */
  70. goog.ds.JsXmlHttpDataSource.prototype.startText_;
  71. /**
  72. * Delimiter for end of JSON data in response.
  73. * null = ends at last character of response
  74. * @type {string|undefined}
  75. * @private
  76. */
  77. goog.ds.JsXmlHttpDataSource.prototype.endText_;
  78. /**
  79. * Gets the state of the backing data for this node
  80. * @return {goog.ds.LoadState} The state.
  81. * @override
  82. */
  83. goog.ds.JsXmlHttpDataSource.prototype.getLoadState = function() {
  84. return this.loadState_;
  85. };
  86. /**
  87. * Sets the request data. This can be used if it is required to
  88. * send a specific body rather than build the body from the query
  89. * parameters. Only used in POST requests.
  90. * @param {string} data The data to send in the request body.
  91. */
  92. goog.ds.JsXmlHttpDataSource.prototype.setQueryData = function(data) {
  93. this.queryData_ = data;
  94. };
  95. /**
  96. * Load or reload the backing data for this node.
  97. * Fires the JsonDataSource
  98. * @override
  99. */
  100. goog.ds.JsXmlHttpDataSource.prototype.load = function() {
  101. goog.log.info(
  102. goog.ds.logger, 'Sending JS request for DataSource ' +
  103. this.getDataName() + ' to ' + this.uri_);
  104. if (this.uri_) {
  105. if (this.usePost_) {
  106. var queryData;
  107. if (!this.queryData_) {
  108. queryData = this.uri_.getQueryData().toString();
  109. } else {
  110. queryData = this.queryData_;
  111. }
  112. var uriNoQuery = this.uri_.clone();
  113. uriNoQuery.setQueryData(null);
  114. this.xhr_.send(String(uriNoQuery), 'POST', queryData);
  115. } else {
  116. this.xhr_.send(String(this.uri_));
  117. }
  118. } else {
  119. this.loadState_ = goog.ds.LoadState.NOT_LOADED;
  120. }
  121. };
  122. /**
  123. * Called on successful request.
  124. * @private
  125. */
  126. goog.ds.JsXmlHttpDataSource.prototype.success_ = function() {
  127. goog.ds.DataManager.getInstance().fireDataChange(this.getDataName());
  128. };
  129. /**
  130. * Completed callback. Loads data if successful, otherwise sets
  131. * state to FAILED
  132. * @param {goog.events.Event} e Event object, Xhr is target.
  133. * @private
  134. */
  135. goog.ds.JsXmlHttpDataSource.prototype.completed_ = function(e) {
  136. if (this.xhr_.isSuccess()) {
  137. goog.log.info(
  138. goog.ds.logger, 'Got data for DataSource ' + this.getDataName());
  139. var text = this.xhr_.getResponseText();
  140. // Look for start and end token and trim text
  141. if (this.startText_) {
  142. var startpos = text.indexOf(this.startText_);
  143. text = text.substring(startpos + this.startText_.length);
  144. }
  145. if (this.endText_) {
  146. var endpos = text.lastIndexOf(this.endText_);
  147. text = text.substring(0, endpos);
  148. }
  149. // Parse result.
  150. try {
  151. var jsonObj = goog.json.parse(text);
  152. this.extendWith(jsonObj);
  153. this.loadState_ = goog.ds.LoadState.LOADED;
  154. } catch (ex) {
  155. // Invalid JSON.
  156. this.loadState_ = goog.ds.LoadState.FAILED;
  157. goog.log.error(goog.ds.logger, 'Failed to parse data: ' + ex.message);
  158. }
  159. // Call on a timer to avoid threading issues on IE.
  160. goog.global.setTimeout(goog.bind(this.success_, this), 0);
  161. } else {
  162. goog.log.info(
  163. goog.ds.logger,
  164. 'Data retrieve failed for DataSource ' + this.getDataName());
  165. this.loadState_ = goog.ds.LoadState.FAILED;
  166. }
  167. };