remotenamefetcher.js 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289
  1. // Copyright 2012 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 Object which fetches Unicode codepoint names from a remote data
  16. * source. This data source should accept two parameters:
  17. * <ol>
  18. * <li>c - the list of codepoints in hexadecimal format
  19. * <li>p - the name property
  20. * </ol>
  21. * and return a JSON object representation of the result.
  22. * For example, calling this data source with the following URL:
  23. * http://datasource?c=50,ff,102bd&p=name
  24. * Should return a JSON object which looks like this:
  25. * <pre>
  26. * {"50":{"name":"LATIN CAPITAL LETTER P"},
  27. * "ff":{"name":"LATIN SMALL LETTER Y WITH DIAERESIS"},
  28. * "102bd":{"name":"CARIAN LETTER K2"}}
  29. * </pre>.
  30. */
  31. goog.provide('goog.i18n.uChar.RemoteNameFetcher');
  32. goog.require('goog.Disposable');
  33. goog.require('goog.Uri');
  34. goog.require('goog.events');
  35. goog.require('goog.i18n.uChar');
  36. goog.require('goog.i18n.uChar.NameFetcher');
  37. goog.require('goog.log');
  38. goog.require('goog.net.EventType');
  39. goog.require('goog.net.XhrIo');
  40. goog.require('goog.structs.Map');
  41. /**
  42. * Builds the RemoteNameFetcher object. This object retrieves codepoint names
  43. * from a remote data source.
  44. *
  45. * @param {string} dataSourceUri URI to the data source.
  46. * @constructor
  47. * @implements {goog.i18n.uChar.NameFetcher}
  48. * @extends {goog.Disposable}
  49. * @final
  50. */
  51. goog.i18n.uChar.RemoteNameFetcher = function(dataSourceUri) {
  52. goog.i18n.uChar.RemoteNameFetcher.base(this, 'constructor');
  53. /**
  54. * XHRIo object for prefetch() asynchronous calls.
  55. *
  56. * @type {!goog.net.XhrIo}
  57. * @private
  58. */
  59. this.prefetchXhrIo_ = new goog.net.XhrIo();
  60. /**
  61. * XHRIo object for getName() asynchronous calls.
  62. *
  63. * @type {!goog.net.XhrIo}
  64. * @private
  65. */
  66. this.getNameXhrIo_ = new goog.net.XhrIo();
  67. /**
  68. * URI to the data.
  69. *
  70. * @type {string}
  71. * @private
  72. */
  73. this.dataSourceUri_ = dataSourceUri;
  74. /**
  75. * A cache of all the collected names from the server.
  76. *
  77. * @type {!goog.structs.Map}
  78. * @private
  79. */
  80. this.charNames_ = new goog.structs.Map();
  81. };
  82. goog.inherits(goog.i18n.uChar.RemoteNameFetcher, goog.Disposable);
  83. /**
  84. * Key to the listener on XHR for prefetch(). Used to clear previous listeners.
  85. *
  86. * @type {goog.events.Key}
  87. * @private
  88. */
  89. goog.i18n.uChar.RemoteNameFetcher.prototype.prefetchLastListenerKey_;
  90. /**
  91. * Key to the listener on XHR for getName(). Used to clear previous listeners.
  92. *
  93. * @type {goog.events.Key}
  94. * @private
  95. */
  96. goog.i18n.uChar.RemoteNameFetcher.prototype.getNameLastListenerKey_;
  97. /**
  98. * A reference to the RemoteNameFetcher logger.
  99. *
  100. * @type {goog.log.Logger}
  101. * @private
  102. */
  103. goog.i18n.uChar.RemoteNameFetcher.logger_ =
  104. goog.log.getLogger('goog.i18n.uChar.RemoteNameFetcher');
  105. /** @override */
  106. goog.i18n.uChar.RemoteNameFetcher.prototype.disposeInternal = function() {
  107. goog.i18n.uChar.RemoteNameFetcher.base(this, 'disposeInternal');
  108. this.prefetchXhrIo_.dispose();
  109. this.getNameXhrIo_.dispose();
  110. };
  111. /** @override */
  112. goog.i18n.uChar.RemoteNameFetcher.prototype.prefetch = function(characters) {
  113. // Abort the current request if there is one
  114. if (this.prefetchXhrIo_.isActive()) {
  115. goog.log.info(
  116. goog.i18n.uChar.RemoteNameFetcher.logger_,
  117. 'Aborted previous prefetch() call for new incoming request');
  118. this.prefetchXhrIo_.abort();
  119. }
  120. if (this.prefetchLastListenerKey_) {
  121. goog.events.unlistenByKey(this.prefetchLastListenerKey_);
  122. }
  123. // Set up new listener
  124. var preFetchCallback = goog.bind(this.prefetchCallback_, this);
  125. this.prefetchLastListenerKey_ = goog.events.listenOnce(
  126. this.prefetchXhrIo_, goog.net.EventType.COMPLETE, preFetchCallback);
  127. this.fetch_(
  128. goog.i18n.uChar.RemoteNameFetcher.RequestType_.BASE_88, characters,
  129. this.prefetchXhrIo_);
  130. };
  131. /**
  132. * Callback on completion of the prefetch operation.
  133. *
  134. * @private
  135. */
  136. goog.i18n.uChar.RemoteNameFetcher.prototype.prefetchCallback_ = function() {
  137. this.processResponse_(this.prefetchXhrIo_);
  138. };
  139. /** @override */
  140. goog.i18n.uChar.RemoteNameFetcher.prototype.getName = function(
  141. character, callback) {
  142. var codepoint = goog.i18n.uChar.toCharCode(character).toString(16);
  143. if (this.charNames_.containsKey(codepoint)) {
  144. var name = /** @type {string} */ (this.charNames_.get(codepoint));
  145. callback(name);
  146. return;
  147. }
  148. // Abort the current request if there is one
  149. if (this.getNameXhrIo_.isActive()) {
  150. goog.log.info(
  151. goog.i18n.uChar.RemoteNameFetcher.logger_,
  152. 'Aborted previous getName() call for new incoming request');
  153. this.getNameXhrIo_.abort();
  154. }
  155. if (this.getNameLastListenerKey_) {
  156. goog.events.unlistenByKey(this.getNameLastListenerKey_);
  157. }
  158. // Set up new listener
  159. var getNameCallback =
  160. goog.bind(this.getNameCallback_, this, codepoint, callback);
  161. this.getNameLastListenerKey_ = goog.events.listenOnce(
  162. this.getNameXhrIo_, goog.net.EventType.COMPLETE, getNameCallback);
  163. this.fetch_(
  164. goog.i18n.uChar.RemoteNameFetcher.RequestType_.CODEPOINT, codepoint,
  165. this.getNameXhrIo_);
  166. };
  167. /**
  168. * Callback on completion of the getName operation.
  169. *
  170. * @param {string} codepoint The codepoint in hexadecimal format.
  171. * @param {function(?string)} callback The callback function called when the
  172. * name retrieval is complete, contains a single string parameter with the
  173. * codepoint name, this parameter will be null if the character name is not
  174. * defined.
  175. * @private
  176. */
  177. goog.i18n.uChar.RemoteNameFetcher.prototype.getNameCallback_ = function(
  178. codepoint, callback) {
  179. this.processResponse_(this.getNameXhrIo_);
  180. var name = /** @type {?string} */ (this.charNames_.get(codepoint, null));
  181. callback(name);
  182. };
  183. /**
  184. * Process the response received from the server and store results in the cache.
  185. *
  186. * @param {!goog.net.XhrIo} xhrIo The XhrIo object used to make the request.
  187. * @private
  188. */
  189. goog.i18n.uChar.RemoteNameFetcher.prototype.processResponse_ = function(xhrIo) {
  190. if (!xhrIo.isSuccess()) {
  191. goog.log.error(
  192. goog.i18n.uChar.RemoteNameFetcher.logger_,
  193. 'Problem with data source: ' + xhrIo.getLastError());
  194. return;
  195. }
  196. var result = xhrIo.getResponseJson();
  197. for (var codepoint in result) {
  198. if (result[codepoint].hasOwnProperty('name')) {
  199. this.charNames_.set(codepoint, result[codepoint]['name']);
  200. }
  201. }
  202. };
  203. /**
  204. * Enum for the different request types.
  205. *
  206. * @enum {string}
  207. * @private
  208. */
  209. goog.i18n.uChar.RemoteNameFetcher.RequestType_ = {
  210. /**
  211. * Request type that uses a base 88 string containing a set of codepoints to
  212. * be fetched from the server (see goog.i18n.charpickerdata for more
  213. * information on b88).
  214. */
  215. BASE_88: 'b88',
  216. /**
  217. * Request type that uses a a string of comma separated codepoint values.
  218. */
  219. CODEPOINT: 'c'
  220. };
  221. /**
  222. * Fetches a set of codepoint names from the data source.
  223. *
  224. * @param {!goog.i18n.uChar.RemoteNameFetcher.RequestType_} requestType The
  225. * request type of the operation. This parameter specifies how the server is
  226. * called to fetch a particular set of codepoints.
  227. * @param {string} requestInput The input to the request, this is the value that
  228. * is passed onto the server to complete the request.
  229. * @param {!goog.net.XhrIo} xhrIo The XHRIo object to execute the server call.
  230. * @private
  231. */
  232. goog.i18n.uChar.RemoteNameFetcher.prototype.fetch_ = function(
  233. requestType, requestInput, xhrIo) {
  234. var url = new goog.Uri(this.dataSourceUri_);
  235. url.setParameterValue(requestType, requestInput);
  236. url.setParameterValue('p', 'name');
  237. goog.log.info(
  238. goog.i18n.uChar.RemoteNameFetcher.logger_, 'Request: ' + url.toString());
  239. xhrIo.send(url);
  240. };
  241. /** @override */
  242. goog.i18n.uChar.RemoteNameFetcher.prototype.isNameAvailable = function(
  243. character) {
  244. return true;
  245. };