Source: StringWriter.js

  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one
  3. * or more contributor license agreements. See the NOTICE file
  4. * distributed with this work for additional information
  5. * regarding copyright ownership. The ASF licenses this file
  6. * to you under the Apache License, Version 2.0 (the
  7. * "License"); you may not use this file except in compliance
  8. * with the License. You may obtain a copy of the License at
  9. *
  10. * http://www.apache.org/licenses/LICENSE-2.0
  11. *
  12. * Unless required by applicable law or agreed to in writing,
  13. * software distributed under the License is distributed on an
  14. * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  15. * KIND, either express or implied. See the License for the
  16. * specific language governing permissions and limitations
  17. * under the License.
  18. */
  19. var Guacamole = Guacamole || {};
  20. /**
  21. * A writer which automatically writes to the given output stream with text
  22. * data.
  23. *
  24. * @constructor
  25. * @param {Guacamole.OutputStream} stream The stream that data will be written
  26. * to.
  27. */
  28. Guacamole.StringWriter = function(stream) {
  29. /**
  30. * Reference to this Guacamole.StringWriter.
  31. * @private
  32. */
  33. var guac_writer = this;
  34. /**
  35. * Wrapped Guacamole.ArrayBufferWriter.
  36. * @private
  37. * @type {Guacamole.ArrayBufferWriter}
  38. */
  39. var array_writer = new Guacamole.ArrayBufferWriter(stream);
  40. /**
  41. * Internal buffer for UTF-8 output.
  42. * @private
  43. */
  44. var buffer = new Uint8Array(8192);
  45. /**
  46. * The number of bytes currently in the buffer.
  47. * @private
  48. */
  49. var length = 0;
  50. // Simply call onack for acknowledgements
  51. array_writer.onack = function(status) {
  52. if (guac_writer.onack)
  53. guac_writer.onack(status);
  54. };
  55. /**
  56. * Expands the size of the underlying buffer by the given number of bytes,
  57. * updating the length appropriately.
  58. *
  59. * @private
  60. * @param {Number} bytes The number of bytes to add to the underlying
  61. * buffer.
  62. */
  63. function __expand(bytes) {
  64. // Resize buffer if more space needed
  65. if (length+bytes >= buffer.length) {
  66. var new_buffer = new Uint8Array((length+bytes)*2);
  67. new_buffer.set(buffer);
  68. buffer = new_buffer;
  69. }
  70. length += bytes;
  71. }
  72. /**
  73. * Appends a single Unicode character to the current buffer, resizing the
  74. * buffer if necessary. The character will be encoded as UTF-8.
  75. *
  76. * @private
  77. * @param {Number} codepoint The codepoint of the Unicode character to
  78. * append.
  79. */
  80. function __append_utf8(codepoint) {
  81. var mask;
  82. var bytes;
  83. // 1 byte
  84. if (codepoint <= 0x7F) {
  85. mask = 0x00;
  86. bytes = 1;
  87. }
  88. // 2 byte
  89. else if (codepoint <= 0x7FF) {
  90. mask = 0xC0;
  91. bytes = 2;
  92. }
  93. // 3 byte
  94. else if (codepoint <= 0xFFFF) {
  95. mask = 0xE0;
  96. bytes = 3;
  97. }
  98. // 4 byte
  99. else if (codepoint <= 0x1FFFFF) {
  100. mask = 0xF0;
  101. bytes = 4;
  102. }
  103. // If invalid codepoint, append replacement character
  104. else {
  105. __append_utf8(0xFFFD);
  106. return;
  107. }
  108. // Offset buffer by size
  109. __expand(bytes);
  110. var offset = length - 1;
  111. // Add trailing bytes, if any
  112. for (var i=1; i<bytes; i++) {
  113. buffer[offset--] = 0x80 | (codepoint & 0x3F);
  114. codepoint >>= 6;
  115. }
  116. // Set initial byte
  117. buffer[offset] = mask | codepoint;
  118. }
  119. /**
  120. * Encodes the given string as UTF-8, returning an ArrayBuffer containing
  121. * the resulting bytes.
  122. *
  123. * @private
  124. * @param {String} text The string to encode as UTF-8.
  125. * @return {Uint8Array} The encoded UTF-8 data.
  126. */
  127. function __encode_utf8(text) {
  128. // Fill buffer with UTF-8
  129. for (var i=0; i<text.length; i++) {
  130. var codepoint = text.charCodeAt(i);
  131. __append_utf8(codepoint);
  132. }
  133. // Flush buffer
  134. if (length > 0) {
  135. var out_buffer = buffer.subarray(0, length);
  136. length = 0;
  137. return out_buffer;
  138. }
  139. }
  140. /**
  141. * Sends the given text.
  142. *
  143. * @param {String} text The text to send.
  144. */
  145. this.sendText = function(text) {
  146. if (text.length)
  147. array_writer.sendData(__encode_utf8(text));
  148. };
  149. /**
  150. * Signals that no further text will be sent, effectively closing the
  151. * stream.
  152. */
  153. this.sendEnd = function() {
  154. array_writer.sendEnd();
  155. };
  156. /**
  157. * Fired for received data, if acknowledged by the server.
  158. * @event
  159. * @param {Guacamole.Status} status The status of the operation.
  160. */
  161. this.onack = null;
  162. };