closure 1.3.1 → 1.4.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/README.md +142 -9
  2. data/bin/closure-script +19 -0
  3. data/closure-compiler/README +18 -4
  4. data/closure-compiler/compiler.jar +0 -0
  5. data/closure-templates/SoyToJsSrcCompiler.jar +0 -0
  6. data/closure-templates/soydata.js +163 -0
  7. data/closure-templates/soyutils.js +1191 -159
  8. data/closure-templates/soyutils_usegoog.js +1107 -60
  9. data/docs/closure/Closure.html +58 -52
  10. data/docs/closure/Closure/BeanShell.html +6 -3
  11. data/docs/closure/Closure/Compiler.html +18 -15
  12. data/docs/closure/Closure/Compiler/Compilation.html +9 -3
  13. data/docs/closure/Closure/Compiler/Error.html +3 -3
  14. data/docs/closure/Closure/FileResponse.html +13 -7
  15. data/docs/closure/Closure/Goog.html +49 -85
  16. data/docs/closure/Closure/Middleware.html +5 -3
  17. data/docs/closure/Closure/Script.html +14 -5
  18. data/docs/closure/Closure/Script/NotFound.html +3 -3
  19. data/docs/closure/Closure/Script/RenderStackOverflow.html +3 -3
  20. data/docs/closure/Closure/Server.html +6 -3
  21. data/docs/closure/Closure/ShowExceptions.html +5 -3
  22. data/docs/closure/Closure/Sources.html +145 -37
  23. data/docs/closure/Closure/Templates.html +11 -10
  24. data/docs/closure/Closure/Templates/Error.html +3 -3
  25. data/docs/closure/_index.html +4 -4
  26. data/docs/closure/css/full_list.css +2 -0
  27. data/docs/closure/css/style.css +2 -0
  28. data/docs/closure/file.LICENSE.html +3 -3
  29. data/docs/closure/file.README.html +151 -10
  30. data/docs/closure/frames.html +1 -1
  31. data/docs/closure/index.html +151 -10
  32. data/docs/closure/js/full_list.js +23 -6
  33. data/docs/closure/method_list.html +91 -83
  34. data/docs/closure/top-level-namespace.html +3 -3
  35. data/lib/closure.rb +3 -16
  36. data/lib/closure/compiler.rb +135 -53
  37. data/lib/closure/goog.rb +5 -29
  38. data/lib/closure/sources.rb +22 -9
  39. data/lib/closure/version.rb +1 -1
  40. data/scripts/config.ru +0 -1
  41. data/scripts/hello/compiler_build.js +5 -5
  42. data/scripts/hello/compiler_build.map +518 -522
  43. data/scripts/hello/compiler_debug.js +7 -13
  44. data/scripts/hello/legume.js +2 -2
  45. data/scripts/index.erb +0 -3
  46. data/scripts/modules/compiler_build.js +3 -3
  47. data/scripts/modules/compiler_build.map +11569 -11476
  48. data/scripts/modules/compiler_build_api.js +1 -1
  49. data/scripts/modules/compiler_build_app.js +71 -70
  50. data/scripts/modules/compiler_build_settings.js +2 -2
  51. data/scripts/modules/compiler_debug.js +3 -3
  52. data/scripts/modules/compiler_debug_api.js +2 -2
  53. data/scripts/modules/compiler_debug_app.js +926 -1382
  54. data/scripts/modules/compiler_debug_settings.js +21 -24
  55. metadata +8 -18
  56. data/externs/chrome_extensions.externs +0 -968
  57. data/externs/jquery-1.3.2.externs +0 -718
  58. data/externs/jquery-1.4.3.externs +0 -1289
  59. data/externs/jquery-1.4.4.externs +0 -1302
  60. data/externs/jquery-1.5.externs +0 -1697
  61. data/externs/jquery-ui.externs +0 -10
  62. data/externs/jquery.externs +0 -4
  63. data/scripts/jquery/compiler.js.erb +0 -7
  64. data/scripts/jquery/compiler_out.js +0 -1
  65. data/scripts/jquery/index.erb +0 -25
  66. data/scripts/jquery/jquery_1.4.4.js +0 -167
  67. data/scripts/jquery/jquery_test.js +0 -8
data/README.md CHANGED
@@ -1,14 +1,147 @@
1
- Google Closure Compiler, Library, Script, and Templates.
1
+ # Closure Script
2
+
3
+ A development environment for Google Closure Tools.
2
4
 
3
5
  Licensed under the Apache License, Version 2.0 (the "License");
4
6
  <http://www.apache.org/licenses/LICENSE-2.0>
5
7
 
8
+ # Installing
9
+
10
+ Everything you need for advanced Google Closure development is available in a
11
+ single .jar for the Java Virtual Machine. You may also run the tools on any
12
+ Ruby platform (>=1.8.6) including JRuby (JVM), Rubinius (LLVM), and Ruby 1.9 (YARV).
13
+
14
+ It is generally easier to get started with the .jar distribution, especially
15
+ under Windows. Mac OSX and most Linux will have a compatible Ruby by default.
16
+
17
+ ## Java (.jar)
18
+
19
+ ### Step 1: Download to a new folder
20
+
21
+ cd ~/empty-dir
22
+ curl -LO https://github.com/downloads/dturnbull/closure-script/closure-1.4.2.jar
23
+
24
+ ### Step 2: Start server from the new folder
25
+
26
+ java -jar closure-1.4.2.jar
27
+
28
+ ### Step 3: Open a web browser
29
+
30
+ http://localhost:8080/
31
+
32
+
33
+ ## Ruby (.gem)
34
+
35
+ ### Step 1: Install the gem
36
+
37
+ gem install closure
38
+
39
+ ### Step 2: Start server from a new folder
40
+
41
+ cd ~/empty-dir
42
+ closure-script
43
+
44
+ ### Step 3: Open a web browser
45
+
46
+ http://localhost:8080/
47
+
48
+
49
+ # The Closure Script Method
50
+
51
+ When you start the server for the first time in an empty folder, the home page
52
+ will prompt you to install scaffolding. This includes three example projects to
53
+ demonstrate soy, modules, and unobtrusive markup. Dissecting and working with
54
+ these examples is the fast track to understanding The Closure Script Method.
55
+
56
+ ## The Server
57
+
58
+ Closure Script is a high-performance, multi-threaded web application engineered
59
+ exclusively for the needs of Google Closure Javascript development.
60
+
61
+ You will be freed from the command line. All error output from the compiler
62
+ will show on the Javascript console. This avoids lost time from not being
63
+ in the correct log and missing an important error. Javascript compilation
64
+ is done just-in-time and only when source files have changed.
65
+ No need for a separate build step; just refresh the browser. Not working?
66
+ Check your Javascript console. Then back to your editor.
67
+
68
+ ## Easy Configuration
69
+
70
+ You'll need to supply the directories where you have source Javascript and static files.
71
+ Ruby developers will recognize that Closure Script is Rack middleware. This makes it trivial
72
+ to include the Closure Script build tool in a Rails application. If you're not developing a
73
+ Ruby application, your ```config.ru``` will probably never be more complex than the following:
74
+
75
+ require 'closure'
76
+ Closure.add_source '.', '/'
77
+ use Closure::Middleware, 'index'
78
+ run Rack::File.new '.'
79
+
80
+ The add_source command may be duplicated for each source Javascript folder you want to
81
+ serve. The first argument is the local filesystem path, the second is the mount point
82
+ for the http server. Make sure not to accidentally serve more than one copy of
83
+ Closure Library per Closure Script server or you'll get an error.
84
+
85
+ ## Cut-and-Paste Ruby
86
+
87
+ In practice, all you do with Ruby is adjust the arguments to compiler.jar by
88
+ analyzing options on the URL query string. If you can handle conditionally appending
89
+ strings to an array in Ruby, then you're fully qualified to use Closure Script!
90
+ There's enough example code in the scaffolding to cut-and-paste your way to victory.
91
+
92
+ ### Demo Scripts
93
+
94
+ The Closure Script Method is to create various demo pages to drive development. You may
95
+ also choose to use your main application instead of Closure Script for your demo pages.
96
+
97
+ Files ending with .erb are Closure Scripts and will have their embedded Ruby evaluated
98
+ as they are served. Scripts may also render other Scripts and pass variables if you
99
+ need that complexity. Scripts default to a MIME type of text/html so ```demo.erb``` is
100
+ the same as ```demo.html.erb```.
101
+
102
+ <html>
103
+ <head>
104
+ <script src='compiler.js?<%= query_string %>'></script>
105
+ </head>
106
+
107
+ ### Compiler Scripts
108
+
109
+ Compilation is performed by requesting a file that generates Javascript instead of HTML.
110
+ The goog.compile() function of Closure Script handles everything for you.
111
+
112
+ Note that goog.compile() does not simply call the compiler. It will monitor your source
113
+ files and skip calling the compiler if everything is up to date. The Java process will
114
+ remain running on a REPL so subsequent compilations don't pay the Java startup cost.
115
+ The dependency tree for all your sources is known so you can build from namespaces
116
+ (--ns) as well as files (--js). Modules have been automated to find common dependencies,
117
+ like plovr, and work from namespaces so you don't need to use filenames and counts.
118
+ The luxurious goog.compile() can serve up a loader for the raw, uncompiled files,
119
+ even when working with modules.
120
+
121
+ A very simple compiler.js.erb is as follows. Check the scaffold for practical examples
122
+ that use the query string.
123
+
124
+ <%
125
+ args = %w{
126
+ --compilation_level ADVANCED_OPTIMIZATIONS
127
+ --js_output_file compiler_build.js
128
+ --ns myapp.helloWorld
129
+ }
130
+ @response = goog.compile(args).to_response
131
+ %>
132
+
133
+ ### Testing
134
+
135
+ Closure Script helps with testing because it can see your data in ways that
136
+ browsers are not allowed to. The ```alltests.js``` file in Closure Library is
137
+ generated by a program that scans the filesystem. Here's a replacement in
138
+ Closure Script so that a manual build step never has to be executed again:
6
139
 
7
- Step 1: Download to a new folder:
8
- <https://github.com/dturnbull/closure-script/downloads>
9
-
10
- Step 2: Run from the new folder with Java:
11
- ```java -jar closure.jar```
12
-
13
- Step 3: Open a web browser:
14
- ```http://localhost:8080/```
140
+ <% all_test_files = Dir.glob expand_path '**/*_test.html'
141
+ json_strings = all_test_files.map { |x| relative_src(x).dump }
142
+ -%>var _allTests = [<%= json_strings.join(',') %>];
143
+
144
+ Since all of Ruby is at your disposal, you could even pull fixture data from SQL
145
+ or a web service. Perhaps a fixture refresh happens when the developer pushes a
146
+ form button. The svn.erb tool is a complex example that uses threads and a
147
+ background process. You're only limited by your imagination.
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env ruby
2
+ begin
3
+ require 'rubygems'
4
+ rescue LoadError
5
+ end
6
+ require 'rack'
7
+ closure_lib_path = File.expand_path('../lib', File.dirname(__FILE__))
8
+ if File.exist? File.join closure_lib_path, 'closure.rb'
9
+ $LOAD_PATH.unshift(closure_lib_path) if !$LOAD_PATH.include?(closure_lib_path)
10
+ end
11
+ if File.exist? 'config.ru'
12
+ Rack::Server.start
13
+ else
14
+ require 'closure'
15
+ ENV["CLOSURE_SCRIPT_WELCOME"] = 'true'
16
+ Rack::Server.start :config => File.join(Closure.base_path, 'scripts/config.ru')
17
+ end
18
+
19
+
@@ -152,7 +152,7 @@ system have been added.
152
152
 
153
153
  -----
154
154
  Code in:
155
- lib/libtrunk_rhino_parser_jarjared.jar
155
+ lib/rhino
156
156
 
157
157
  Rhino
158
158
  URL: http://www.mozilla.org/rhino
@@ -161,9 +161,8 @@ License: Netscape Public License and MPL / GPL dual license
161
161
 
162
162
  Description: Mozilla Rhino is an implementation of JavaScript for the JVM.
163
163
 
164
- Local Modifications: None. We've used JarJar to renamespace the code
165
- post-compilation. See:
166
- http://code.google.com/p/jarjar/
164
+ Local Modifications: Minor changes to parsing JSDoc that usually get pushed
165
+ up-stream to Rhino trunk.
167
166
 
168
167
 
169
168
  -----
@@ -210,6 +209,21 @@ Description: Annotations for software defect detection.
210
209
  Local Modifications: None.
211
210
 
212
211
 
212
+ -----
213
+ Code in:
214
+ lib/jarjar.jar
215
+
216
+ Jar Jar Links
217
+ URL: http://jarjar.googlecode.com/
218
+ Version: 1.1
219
+ License: Apache License 2.0
220
+
221
+ Description:
222
+ A utility for repackaging Java libraries.
223
+
224
+ Local Modifications: None.
225
+
226
+
213
227
  ----
214
228
  Code in:
215
229
  lib/junit.jar
@@ -0,0 +1,163 @@
1
+ /*
2
+ * Copyright 2010 Google Inc.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+
18
+ /**
19
+ * @fileoverview
20
+ * Defines typed strings, e.g. an HTML string {@code "a<b>c"} is
21
+ * semantically distinct from the plain text string {@code "a<b>c"} and smart
22
+ * templates can take that distinction into account.
23
+ *
24
+ * @author Mike Samuel
25
+ */
26
+
27
+
28
+ goog.provide('soydata');
29
+ goog.provide('soydata.SanitizedHtml');
30
+ goog.provide('soydata.SanitizedHtmlAttribute');
31
+ goog.provide('soydata.SanitizedJsStrChars');
32
+ goog.provide('soydata.SanitizedUri');
33
+
34
+
35
+ /**
36
+ * A type of textual content.
37
+ * @enum
38
+ */
39
+ soydata.SanitizedContentKind = {
40
+
41
+ /**
42
+ * A snippet of HTML that does not start or end inside a tag, comment, entity,
43
+ * or DOCTYPE; and that does not contain any executable code
44
+ * (JS, {@code <object>}s, etc.) from a different trust domain.
45
+ */
46
+ HTML: 0,
47
+
48
+ /**
49
+ * A sequence of code units that can appear between quotes (either kind) in a
50
+ * JS program without causing a parse error, and without causing any side
51
+ * effects.
52
+ * <p>
53
+ * The content should not contain unescaped quotes, newlines, or anything else
54
+ * that would cause parsing to fail or to cause a JS parser to finish the
55
+ * string its parsing inside the content.
56
+ * <p>
57
+ * The content must also not end inside an escape sequence ; no partial octal
58
+ * escape sequences or odd number of '{@code \}'s at the end.
59
+ */
60
+ JS_STR_CHARS: 1,
61
+
62
+ /** A properly encoded portion of a URI. */
63
+ URI: 2,
64
+
65
+ /** An attribute name and value such as {@code dir="ltr"}. */
66
+ HTML_ATTRIBUTE: 3
67
+ };
68
+
69
+
70
+ /**
71
+ * A string-like object that carries a content-type.
72
+ * @constructor
73
+ * @private
74
+ */
75
+ soydata.SanitizedContent = function() {};
76
+
77
+ /** The textual content. @type {string} */
78
+ soydata.SanitizedContent.prototype.content;
79
+
80
+ /** @type {soydata.SanitizedContentKind} */
81
+ soydata.SanitizedContent.prototype.contentKind;
82
+
83
+ /**
84
+ * @return {string}
85
+ */
86
+ soydata.SanitizedContent.prototype.toString = function() {
87
+ return this.content;
88
+ };
89
+
90
+
91
+ /**
92
+ * Content of type {@link soydata.SanitizedContentKind.HTML}.
93
+ * @constructor
94
+ * @param {string!} content A string of HTML that can safely be embedded in
95
+ * a PCDATA context in your app. If you would be surprised to find that an
96
+ * HTML sanitizer produced {@code s} (e.g. it runs code or fetches bad URLs)
97
+ * and you wouldn't write a template that produces {@code s} on security or
98
+ * privacy grounds, then don't pass {@code s} here.
99
+ */
100
+ soydata.SanitizedHtml = function(content) {
101
+ this.content = content;
102
+ };
103
+
104
+ /** @override */
105
+ soydata.SanitizedHtml.prototype = new soydata.SanitizedContent();
106
+
107
+ /** @override */
108
+ soydata.SanitizedHtml.prototype.contentKind = soydata.SanitizedContentKind.HTML;
109
+
110
+
111
+ /**
112
+ * Content of type {@link soydata.SanitizedContentKind.JS_STR_CHARS}.
113
+ * @constructor
114
+ * @param {string!} content A string of JS that when evaled, produces a
115
+ * value that does not depend on any sensitive data and has no side effects
116
+ * <b>OR</b> a string of JS that does not reference any variables or have
117
+ * any side effects not known statically to the app authors.
118
+ */
119
+ soydata.SanitizedJsStrChars = function(content) {
120
+ this.content = content;
121
+ };
122
+
123
+ /** @override */
124
+ soydata.SanitizedJsStrChars.prototype = new soydata.SanitizedContent();
125
+
126
+ /** @override */
127
+ soydata.SanitizedJsStrChars.prototype.contentKind =
128
+ soydata.SanitizedContentKind.JS_STR_CHARS;
129
+
130
+
131
+ /**
132
+ * Content of type {@link soydata.SanitizedContentKind.URI}.
133
+ * @constructor
134
+ * @param {string!} content A chunk of URI that the caller knows is safe to
135
+ * emit in a template.
136
+ */
137
+ soydata.SanitizedUri = function(content) {
138
+ this.content = content;
139
+ };
140
+
141
+ /** @override */
142
+ soydata.SanitizedUri.prototype = new soydata.SanitizedContent();
143
+
144
+ /** @override */
145
+ soydata.SanitizedUri.prototype.contentKind = soydata.SanitizedContentKind.URI;
146
+
147
+
148
+ /**
149
+ * Content of type {@link soydata.SanitizedContentKind.HTML_ATTRIBUTE}.
150
+ * @constructor
151
+ * @param {string!} content An attribute name and value, such as
152
+ * {@code dir="ltr"}..
153
+ */
154
+ soydata.SanitizedHtmlAttribute = function(content) {
155
+ this.content = content;
156
+ };
157
+
158
+ /** @override */
159
+ soydata.SanitizedHtmlAttribute.prototype = new soydata.SanitizedContent();
160
+
161
+ /** @override */
162
+ soydata.SanitizedHtmlAttribute.prototype.contentKind =
163
+ soydata.SanitizedContentKind.HTML_ATTRIBUTE;
@@ -14,16 +14,26 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- // Utility functions and classes for Soy.
18
- //
19
- // The top portion of this file contains utilities for Soy users:
20
- // + soy.StringBuilder: Compatible with the 'stringbuilder' code style.
21
- // + soy.renderElement: Render template and set as innerHTML of an element.
22
- // + soy.renderAsFragment: Render template and return as HTML fragment.
23
- //
24
- // The bottom portion of this file contains utilities that should only be called
25
- // by Soy-generated JS code. Please do not use these functions directly from
26
- // your hand-writen code. Their names all start with '$$'.
17
+ /**
18
+ * @fileoverview
19
+ * Utility functions and classes for Soy.
20
+ *
21
+ * <p>
22
+ * The top portion of this file contains utilities for Soy users:<ul>
23
+ * <li> soy.StringBuilder: Compatible with the 'stringbuilder' code style.
24
+ * <li> soy.renderElement: Render template and set as innerHTML of an element.
25
+ * <li> soy.renderAsFragment: Render template and return as HTML fragment.
26
+ * </ul>
27
+ *
28
+ * <p>
29
+ * The bottom portion of this file contains utilities that should only be called
30
+ * by Soy-generated JS code. Please do not use these functions directly from
31
+ * your hand-writen code. Their names all start with '$$'.
32
+ *
33
+ * @author Mike Samuel
34
+ * @author Kai Huang
35
+ * @author Aharon Lenin
36
+ */
27
37
 
28
38
  /**
29
39
  * Base name for the soy utilities, when used outside of Closure Library.
@@ -32,6 +42,7 @@
32
42
  * @type {Object}
33
43
  */
34
44
  var soy = soy || {};
45
+ soy.esc = soy.esc || {};
35
46
 
36
47
 
37
48
  // Just enough browser detection for this file.
@@ -42,17 +53,17 @@ var soy = soy || {};
42
53
  * @type {boolean}
43
54
  * @private
44
55
  */
45
- soy.IS_OPERA_ = isOpera;
56
+ soy.$$IS_OPERA_ = isOpera;
46
57
  /**
47
58
  * @type {boolean}
48
59
  * @private
49
60
  */
50
- soy.IS_IE_ = !isOpera && ua.indexOf('MSIE') != -1;
61
+ soy.$$IS_IE_ = !isOpera && ua.indexOf('MSIE') != -1;
51
62
  /**
52
63
  * @type {boolean}
53
64
  * @private
54
65
  */
55
- soy.IS_WEBKIT_ = !isOpera && ua.indexOf('WebKit') != -1;
66
+ soy.$$IS_WEBKIT_ = !isOpera && ua.indexOf('WebKit') != -1;
56
67
  })();
57
68
 
58
69
 
@@ -67,7 +78,7 @@ var soy = soy || {};
67
78
  *
68
79
  * @param {Object|number|string|boolean=} opt_a1 Optional first initial item
69
80
  * to append.
70
- * @param {Object|number|string|boolean} var_args Other initial items to
81
+ * @param {...Object|number|string|boolean} var_args Other initial items to
71
82
  * append, e.g., new soy.StringBuilder('foo', 'bar').
72
83
  * @constructor
73
84
  */
@@ -78,7 +89,7 @@ soy.StringBuilder = function(opt_a1, var_args) {
78
89
  * @type {string|Array}
79
90
  * @private
80
91
  */
81
- this.buffer_ = soy.IS_IE_ ? [] : '';
92
+ this.buffer_ = soy.$$IS_IE_ ? [] : '';
82
93
 
83
94
  if (opt_a1 != null) {
84
95
  this.append.apply(this, arguments);
@@ -102,19 +113,20 @@ soy.StringBuilder.prototype.bufferLength_ = 0;
102
113
  *
103
114
  * @param {Object|number|string|boolean} a1 Required first string.
104
115
  * @param {Object|number|string|boolean=} opt_a2 Optional second string.
105
- * @param {Object|number|string|boolean} var_args Other items to append,
116
+ * @param {...Object|number|string|boolean} var_args Other items to append,
106
117
  * e.g., sb.append('foo', 'bar', 'baz').
107
118
  * @return {soy.StringBuilder} This same StringBuilder object.
108
119
  */
109
120
  soy.StringBuilder.prototype.append = function(a1, opt_a2, var_args) {
110
121
 
111
- if (soy.IS_IE_) {
122
+ if (soy.$$IS_IE_) {
112
123
  if (opt_a2 == null) { // no second argument (note: undefined == null)
113
124
  // Array assignment is 2x faster than Array push. Also, use a1
114
125
  // directly to avoid arguments instantiation, another 2x improvement.
115
126
  this.buffer_[this.bufferLength_++] = a1;
116
127
  } else {
117
- this.buffer_.push.apply(this.buffer_, arguments);
128
+ var arr = /**@type {Array.<number|string|boolean>}*/this.buffer_;
129
+ arr.push.apply(arr, arguments);
118
130
  this.bufferLength_ = this.buffer_.length;
119
131
  }
120
132
 
@@ -138,7 +150,7 @@ soy.StringBuilder.prototype.append = function(a1, opt_a2, var_args) {
138
150
  */
139
151
  soy.StringBuilder.prototype.clear = function() {
140
152
 
141
- if (soy.IS_IE_) {
153
+ if (soy.$$IS_IE_) {
142
154
  this.buffer_.length = 0; // reuse array to avoid creating new object
143
155
  this.bufferLength_ = 0;
144
156
 
@@ -155,7 +167,7 @@ soy.StringBuilder.prototype.clear = function() {
155
167
  */
156
168
  soy.StringBuilder.prototype.toString = function() {
157
169
 
158
- if (soy.IS_IE_) {
170
+ if (soy.$$IS_IE_) {
159
171
  var str = this.buffer_.join('');
160
172
  // Given a string with the entire contents, simplify the StringBuilder by
161
173
  // setting its contents to only be this string, rather than many fragments.
@@ -175,6 +187,16 @@ soy.StringBuilder.prototype.toString = function() {
175
187
  // Public utilities.
176
188
 
177
189
 
190
+ /**
191
+ * Immutable object that is passed into templates that are rendered
192
+ * without any data.
193
+ *
194
+ * @type {Object}
195
+ * @private
196
+ */
197
+ soy.$$DEFAULT_TEMPLATE_DATA_ = {};
198
+
199
+
178
200
  /**
179
201
  * Helper function to render a Soy template and then set the output string as
180
202
  * the innerHTML of an element. It is recommended to use this helper function
@@ -184,35 +206,104 @@ soy.StringBuilder.prototype.toString = function() {
184
206
  * @param {Element} element The element whose content we are rendering.
185
207
  * @param {Function} template The Soy template defining the element's content.
186
208
  * @param {Object=} opt_templateData The data for the template.
209
+ * @param {Object=} opt_injectedData The injected data for the template.
187
210
  */
188
- soy.renderElement = function(element, template, opt_templateData) {
189
- element.innerHTML = template(opt_templateData);
211
+ soy.renderElement = function(
212
+ element, template, opt_templateData, opt_injectedData) {
213
+ element.innerHTML = template(
214
+ opt_templateData || soy.$$DEFAULT_TEMPLATE_DATA_, undefined,
215
+ opt_injectedData);
190
216
  };
191
217
 
192
218
 
193
219
  /**
194
220
  * Helper function to render a Soy template into a single node or a document
195
221
  * fragment. If the rendered HTML string represents a single node, then that
196
- * node is returned. Otherwise a document fragment is returned containing the
222
+ * node is returned (note that this is *not* a fragment, despite them name of
223
+ * the method). Otherwise a document fragment is returned containing the
197
224
  * rendered nodes.
198
225
  *
199
226
  * @param {Function} template The Soy template defining the element's content.
200
227
  * @param {Object=} opt_templateData The data for the template.
201
- * @return {Node} The resulting node or document fragment.
228
+ * @param {Document=} opt_document The document used to create DOM nodes. If not
229
+ * specified, global document object is used.
230
+ * @param {Object=} opt_injectedData The injected data for the template.
231
+ * @return {!Node} The resulting node or document fragment.
202
232
  */
203
- soy.renderAsFragment = function(template, opt_templateData) {
233
+ soy.renderAsFragment = function(
234
+ template, opt_templateData, opt_document, opt_injectedData) {
235
+ return soy.$$renderWithWrapper_(
236
+ template, opt_templateData, opt_document, false /* asElement */,
237
+ opt_injectedData);
238
+ };
204
239
 
205
- var tempDiv = document.createElement('div');
206
- tempDiv.innerHTML = template(opt_templateData);
207
- if (tempDiv.childNodes.length == 1) {
208
- return tempDiv.firstChild;
209
- } else {
210
- var fragment = document.createDocumentFragment();
211
- while (tempDiv.firstChild) {
212
- fragment.appendChild(tempDiv.firstChild);
240
+
241
+ /**
242
+ * Helper function to render a Soy template into a single node. If the rendered
243
+ * HTML string represents a single node, then that node is returned. Otherwise,
244
+ * a DIV element is returned containing the rendered nodes.
245
+ *
246
+ * @param {Function} template The Soy template defining the element's content.
247
+ * @param {Object=} opt_templateData The data for the template.
248
+ * @param {Document=} opt_document The document used to create DOM nodes. If not
249
+ * specified, global document object is used.
250
+ * @param {Object=} opt_injectedData The injected data for the template.
251
+ * @return {!Element} Rendered template contents, wrapped in a parent DIV
252
+ * element if necessary.
253
+ */
254
+ soy.renderAsElement = function(
255
+ template, opt_templateData, opt_document, opt_injectedData) {
256
+ return /** @type {!Element} */ (soy.$$renderWithWrapper_(
257
+ template, opt_templateData, opt_document, true /* asElement */,
258
+ opt_injectedData));
259
+ };
260
+
261
+
262
+ /**
263
+ * Helper function to render a Soy template into a single node or a document
264
+ * fragment. If the rendered HTML string represents a single node, then that
265
+ * node is returned. Otherwise a document fragment is created and returned
266
+ * (wrapped in a DIV element if #opt_singleNode is true).
267
+ *
268
+ * @param {Function} template The Soy template defining the element's content.
269
+ * @param {Object=} opt_templateData The data for the template.
270
+ * @param {Document=} opt_document The document used to create DOM nodes. If not
271
+ * specified, global document object is used.
272
+ * @param {boolean=} opt_asElement Whether to wrap the fragment in an
273
+ * element if the template does not render a single element. If true, result
274
+ * is always an Element.
275
+ * @param {Object=} opt_injectedData The injected data for the template.
276
+ * @return {!Node} The resulting node or document fragment.
277
+ * @private
278
+ */
279
+ soy.$$renderWithWrapper_ = function(
280
+ template, opt_templateData, opt_document, opt_asElement, opt_injectedData) {
281
+
282
+ var doc = opt_document || document;
283
+ var wrapper = doc.createElement('div');
284
+ wrapper.innerHTML = template(
285
+ opt_templateData || soy.$$DEFAULT_TEMPLATE_DATA_, undefined,
286
+ opt_injectedData);
287
+
288
+ // If the template renders as a single element, return it.
289
+ if (wrapper.childNodes.length == 1) {
290
+ var firstChild = wrapper.firstChild;
291
+ if (!opt_asElement || firstChild.nodeType == 1 /* Element */) {
292
+ return /** @type {!Node} */ (firstChild);
213
293
  }
214
- return fragment;
215
294
  }
295
+
296
+ // If we're forcing it to be a single element, return the wrapper DIV.
297
+ if (opt_asElement) {
298
+ return wrapper;
299
+ }
300
+
301
+ // Otherwise, create and return a fragment.
302
+ var fragment = doc.createDocumentFragment();
303
+ while (wrapper.firstChild) {
304
+ fragment.appendChild(wrapper.firstChild);
305
+ }
306
+ return fragment;
216
307
  };
217
308
 
218
309
 
@@ -237,9 +328,9 @@ soy.$$augmentData = function(origData, additionalParams) {
237
328
 
238
329
  // Create a new object whose '__proto__' field is set to origData.
239
330
  /** @constructor */
240
- function tempCtor() {};
241
- tempCtor.prototype = origData;
242
- var newData = new tempCtor();
331
+ function TempCtor() {}
332
+ TempCtor.prototype = origData;
333
+ var newData = new TempCtor();
243
334
 
244
335
  // Add the additional params to the new object.
245
336
  for (var key in additionalParams) {
@@ -251,151 +342,530 @@ soy.$$augmentData = function(origData, additionalParams) {
251
342
 
252
343
 
253
344
  /**
254
- * Escapes HTML special characters in a string. Escapes double quote '"' in
345
+ * Gets the keys in a map as an array. There are no guarantees on the order.
346
+ * @param {Object} map The map to get the keys of.
347
+ * @return {Array.<string>} The array of keys in the given map.
348
+ */
349
+ soy.$$getMapKeys = function(map) {
350
+ var mapKeys = [];
351
+ for (var key in map) {
352
+ mapKeys.push(key);
353
+ }
354
+ return mapKeys;
355
+ };
356
+
357
+
358
+ /**
359
+ * Gets a consistent unique id for the given delegate template name. Two calls
360
+ * to this function will return the same id if and only if the input names are
361
+ * the same.
362
+ *
363
+ * <p> Important: This function must always be called with a string constant.
364
+ *
365
+ * <p> If Closure Compiler is not being used, then this is just this identity
366
+ * function. If Closure Compiler is being used, then each call to this function
367
+ * will be replaced with a short string constant, which will be consistent per
368
+ * input name.
369
+ *
370
+ * @param {string} delTemplateName The delegate template name for which to get a
371
+ * consistent unique id.
372
+ * @return {string} A unique id that is consistent per input name.
373
+ *
374
+ * @consistentIdGenerator
375
+ */
376
+ soy.$$getDelegateId = function(delTemplateName) {
377
+ return delTemplateName;
378
+ };
379
+
380
+
381
+ /**
382
+ * Map from registered delegate template id/name to the priority of the
383
+ * implementation.
384
+ * @type {Object}
385
+ * @private
386
+ */
387
+ soy.$$DELEGATE_REGISTRY_PRIORITIES_ = {};
388
+
389
+ /**
390
+ * Map from registered delegate template id/name to the implementation function.
391
+ * @type {Object}
392
+ * @private
393
+ */
394
+ soy.$$DELEGATE_REGISTRY_FUNCTIONS_ = {};
395
+
396
+
397
+ /**
398
+ * Registers a delegate implementation. If the same delegate template id/name
399
+ * has been registered previously, then priority values are compared and only
400
+ * the higher priority implementation is stored (if priorities are equal, an
401
+ * error is thrown).
402
+ *
403
+ * @param {string} delTemplateId The delegate template id/name to register.
404
+ * @param {number} delPriority The implementation's priority value.
405
+ * @param {Function} delFn The implementation function.
406
+ */
407
+ soy.$$registerDelegateFn = function(delTemplateId, delPriority, delFn) {
408
+ var mapKey = 'key_' + delTemplateId;
409
+ var currPriority = soy.$$DELEGATE_REGISTRY_PRIORITIES_[mapKey];
410
+ if (currPriority === undefined || delPriority > currPriority) {
411
+ // Registering new or higher-priority function: replace registry entry.
412
+ soy.$$DELEGATE_REGISTRY_PRIORITIES_[mapKey] = delPriority;
413
+ soy.$$DELEGATE_REGISTRY_FUNCTIONS_[mapKey] = delFn;
414
+ } else if (delPriority == currPriority) {
415
+ // Registering same-priority function: error.
416
+ throw Error(
417
+ 'Encountered two active delegates with same priority (id/name "' +
418
+ delTemplateId + '").');
419
+ } else {
420
+ // Registering lower-priority function: do nothing.
421
+ }
422
+ };
423
+
424
+
425
+ /**
426
+ * Retrieves the (highest-priority) implementation that has been registered for
427
+ * a given delegate template id/name. If no implementation has been registered
428
+ * for the id/name, then returns an implementation that is equivalent to an
429
+ * empty template (i.e. rendered output would be empty string).
430
+ *
431
+ * @param {string} delTemplateId The delegate template id/name to get.
432
+ * @return {Function} The retrieved implementation function.
433
+ */
434
+ soy.$$getDelegateFn = function(delTemplateId) {
435
+ var delFn = soy.$$DELEGATE_REGISTRY_FUNCTIONS_['key_' + delTemplateId];
436
+ return delFn ? delFn : soy.$$EMPTY_TEMPLATE_FN_;
437
+ };
438
+
439
+
440
+ /**
441
+ * Private helper soy.$$getDelegateFn(). This is the empty template function
442
+ * that is returned whenever there's no delegate implementation found.
443
+ *
444
+ * @param {Object.<string, *>=} opt_data
445
+ * @param {soy.StringBuilder=} opt_sb
446
+ * @param {Object.<string, *>=} opt_ijData
447
+ * @return {string}
448
+ * @private
449
+ */
450
+ soy.$$EMPTY_TEMPLATE_FN_ = function(opt_data, opt_sb, opt_ijData) {
451
+ return '';
452
+ };
453
+
454
+
455
+ /**
456
+ * Used for temporary fix. See GenJsCodeVisitor.java.
457
+ * TODO: Remove when i18n plurals team provides a better # processing option.
458
+ * @param {string} str The string to escape.
459
+ * @return {string} The escaped string.
460
+ */
461
+ soy.$$tempHashEscape = function(str) {
462
+ return str.replace(soy.$$HASH_RE_, '__HashLit__');
463
+ };
464
+
465
+ /**
466
+ * Used by soy.$$tempHashEscape().
467
+ * @type {RegExp}
468
+ * @private
469
+ */
470
+ soy.$$HASH_RE_ = /#/g;
471
+
472
+
473
+ /**
474
+ * Used for temporary fix. See GenJsCodeVisitor.java.
475
+ * TODO: Remove when i18n plurals team provides a better # processing option.
476
+ * @param {string} str The string to unescape.
477
+ * @return {string} The unescaped string.
478
+ */
479
+ soy.$$tempHashUnescape = function(str) {
480
+ return str.replace(soy.$$HASH_ESCAPED_RE_, '#');
481
+ };
482
+
483
+ /**
484
+ * Used by soy.$$tempHashUnescape().
485
+ * @type {RegExp}
486
+ * @private
487
+ */
488
+ soy.$$HASH_ESCAPED_RE_ = /__HashLit__/g;
489
+
490
+
491
+ // -----------------------------------------------------------------------------
492
+ // Escape/filter/normalize.
493
+
494
+
495
+ /**
496
+ * Escapes HTML special characters in a string. Escapes double quote '"' in
255
497
  * addition to '&', '<', and '>' so that a string can be included in an HTML
256
498
  * tag attribute value within double quotes.
499
+ * Will emit known safe HTML as-is.
257
500
  *
258
- * @param {*} str The string to be escaped. Can be other types, but the value
259
- * will be coerced to a string.
260
- * @return {string} An escaped copy of the string.
261
- */
262
- soy.$$escapeHtml = function(str) {
501
+ * @param {*} value The string-like value to be escaped. May not be a string,
502
+ * but the value will be coerced to a string.
503
+ * @return {string} An escaped version of value.
504
+ */
505
+ soy.$$escapeHtml = function(value) {
506
+ if (typeof value === 'object' && value &&
507
+ value.contentKind === soydata.SanitizedContentKind.HTML) {
508
+ return value.content;
509
+ }
510
+ return soy.esc.$$escapeHtmlHelper(value);
511
+ };
263
512
 
264
- str = String(str);
265
513
 
266
- // This quick test helps in the case when there are no chars to replace, in
267
- // the worst case this makes barely a difference to the time taken.
268
- if (!soy.$$EscapeHtmlRe_.ALL_SPECIAL_CHARS.test(str)) {
269
- return str;
514
+ /**
515
+ * Escapes HTML special characters in a string so that it can be embedded in
516
+ * RCDATA.
517
+ * <p>
518
+ * Escapes HTML special characters so that the value will not prematurely end
519
+ * the body of a tag like {@code <textarea>} or {@code <title>}. RCDATA tags
520
+ * cannot contain other HTML entities, so it is not strictly necessary to escape
521
+ * HTML special characters except when part of that text looks like an HTML
522
+ * entity or like a close tag : {@code </textarea>}.
523
+ * <p>
524
+ * Will normalize known safe HTML to make sure that sanitized HTML (which could
525
+ * contain an innocuous {@code </textarea>} don't prematurely end an RCDATA
526
+ * element.
527
+ *
528
+ * @param {*} value The string-like value to be escaped. May not be a string,
529
+ * but the value will be coerced to a string.
530
+ * @return {string} An escaped version of value.
531
+ */
532
+ soy.$$escapeHtmlRcdata = function(value) {
533
+ if (typeof value === 'object' && value &&
534
+ value.contentKind === soydata.SanitizedContentKind.HTML) {
535
+ return soy.esc.$$normalizeHtmlHelper(value.content);
270
536
  }
537
+ return soy.esc.$$escapeHtmlHelper(value);
538
+ };
271
539
 
272
- // Since we're only checking one char at a time, we use String.indexOf(),
273
- // which is faster than RegExp.test(). Important: Must replace '&' first!
274
- if (str.indexOf('&') != -1) {
275
- str = str.replace(soy.$$EscapeHtmlRe_.AMP, '&amp;');
276
- }
277
- if (str.indexOf('<') != -1) {
278
- str = str.replace(soy.$$EscapeHtmlRe_.LT, '&lt;');
540
+
541
+ /**
542
+ * Removes HTML tags from a string of known safe HTML so it can be used as an
543
+ * attribute value.
544
+ *
545
+ * @param {*} value The HTML to be escaped. May not be a string, but the
546
+ * value will be coerced to a string.
547
+ * @return {string} A representation of value without tags, HTML comments, or
548
+ * other content.
549
+ */
550
+ soy.$$stripHtmlTags = function(value) {
551
+ return String(value).replace(soy.esc.$$HTML_TAG_REGEX_, '');
552
+ };
553
+
554
+
555
+ /**
556
+ * Escapes HTML special characters in an HTML attribute value.
557
+ *
558
+ * @param {*} value The HTML to be escaped. May not be a string, but the
559
+ * value will be coerced to a string.
560
+ * @return {string} An escaped version of value.
561
+ */
562
+ soy.$$escapeHtmlAttribute = function(value) {
563
+ if (typeof value === 'object' && value &&
564
+ value.contentKind === soydata.SanitizedContentKind.HTML) {
565
+ return soy.esc.$$normalizeHtmlHelper(soy.$$stripHtmlTags(value.content));
279
566
  }
280
- if (str.indexOf('>') != -1) {
281
- str = str.replace(soy.$$EscapeHtmlRe_.GT, '&gt;');
567
+ return soy.esc.$$escapeHtmlHelper(value);
568
+ };
569
+
570
+
571
+ /**
572
+ * Escapes HTML special characters in a string including space and other
573
+ * characters that can end an unquoted HTML attribute value.
574
+ *
575
+ * @param {*} value The HTML to be escaped. May not be a string, but the
576
+ * value will be coerced to a string.
577
+ * @return {string} An escaped version of value.
578
+ */
579
+ soy.$$escapeHtmlAttributeNospace = function(value) {
580
+ if (typeof value === 'object' && value &&
581
+ value.contentKind === soydata.SanitizedContentKind.HTML) {
582
+ return soy.esc.$$normalizeHtmlNospaceHelper(
583
+ soy.$$stripHtmlTags(value.content));
282
584
  }
283
- if (str.indexOf('"') != -1) {
284
- str = str.replace(soy.$$EscapeHtmlRe_.QUOT, '&quot;');
585
+ return soy.esc.$$escapeHtmlNospaceHelper(value);
586
+ };
587
+
588
+
589
+ /**
590
+ * Filters out strings that cannot be a substring of a valid HTML attribute.
591
+ *
592
+ * @param {*} value The value to escape. May not be a string, but the value
593
+ * will be coerced to a string.
594
+ * @return {string} A valid HTML attribute name part or name/value pair.
595
+ * {@code "zSoyz"} if the input is invalid.
596
+ */
597
+ soy.$$filterHtmlAttribute = function(value) {
598
+ if (typeof value === 'object' && value &&
599
+ value.contentKind === soydata.SanitizedContentKind.HTML_ATTRIBUTE) {
600
+ return value.content.replace(/=([^"']*)$/, '="$1"');
285
601
  }
286
- return str;
602
+ return soy.esc.$$filterHtmlAttributeHelper(value);
287
603
  };
288
604
 
605
+
289
606
  /**
290
- * Regular expressions used within escapeHtml().
291
- * @enum {RegExp}
292
- * @private
607
+ * Filters out strings that cannot be a substring of a valid HTML element name.
608
+ *
609
+ * @param {*} value The value to escape. May not be a string, but the value
610
+ * will be coerced to a string.
611
+ * @return {string} A valid HTML element name part.
612
+ * {@code "zSoyz"} if the input is invalid.
613
+ */
614
+ soy.$$filterHtmlElementName = function(value) {
615
+ return soy.esc.$$filterHtmlElementNameHelper(value);
616
+ };
617
+
618
+
619
+ /**
620
+ * Escapes characters in the value to make it valid content for a JS string
621
+ * literal.
622
+ *
623
+ * @param {*} value The value to escape. May not be a string, but the value
624
+ * will be coerced to a string.
625
+ * @return {string} An escaped version of value.
626
+ * @deprecated
293
627
  */
294
- soy.$$EscapeHtmlRe_ = {
295
- ALL_SPECIAL_CHARS: /[&<>\"]/,
296
- AMP: /&/g,
297
- LT: /</g,
298
- GT: />/g,
299
- QUOT: /\"/g
628
+ soy.$$escapeJs = function(value) {
629
+ return soy.$$escapeJsString(value);
300
630
  };
301
631
 
302
632
 
303
633
  /**
304
- * Escapes characters in the string to make it a valid content for a JS string literal.
634
+ * Escapes characters in the value to make it valid content for a JS string
635
+ * literal.
305
636
  *
306
- * @param {*} s The string to be escaped. Can be other types, but the value
637
+ * @param {*} value The value to escape. May not be a string, but the value
307
638
  * will be coerced to a string.
308
- * @return {string} An escaped copy of the string.
309
- */
310
- soy.$$escapeJs = function(s) {
311
- s = String(s);
312
- var sb = [];
313
- for (var i = 0; i < s.length; i++) {
314
- sb[i] = soy.$$escapeChar(s.charAt(i));
639
+ * @return {string} An escaped version of value.
640
+ */
641
+ soy.$$escapeJsString = function(value) {
642
+ if (typeof value === 'object' &&
643
+ value.contentKind === soydata.SanitizedContentKind.JS_STR_CHARS) {
644
+ return value.content;
315
645
  }
316
- return sb.join('');
646
+ return soy.esc.$$escapeJsStringHelper(value);
317
647
  };
318
648
 
319
649
 
320
650
  /**
321
- * Takes a character and returns the escaped string for that character. For
322
- * example escapeChar(String.fromCharCode(15)) -> "\\x0E".
323
- * @param {string} c The character to escape.
324
- * @return {string} An escaped string representing {@code c}.
651
+ * Encodes a value as a JavaScript literal.
652
+ *
653
+ * @param {*} value The value to escape. May not be a string, but the value
654
+ * will be coerced to a string.
655
+ * @return {string} A JavaScript code representation of the input.
325
656
  */
326
- soy.$$escapeChar = function(c) {
327
- if (c in soy.$$escapeCharJs_) {
328
- return soy.$$escapeCharJs_[c];
657
+ soy.$$escapeJsValue = function(value) {
658
+ // We surround values with spaces so that they can't be interpolated into
659
+ // identifiers by accident.
660
+ // We could use parentheses but those might be interpreted as a function call.
661
+ if (value == null) { // Intentionally matches undefined.
662
+ // Java returns null from maps where there is no corresponding key while
663
+ // JS returns undefined.
664
+ // We always output null for compatibility with Java which does not have a
665
+ // distinct undefined value.
666
+ return ' null ';
329
667
  }
330
- var rv = c;
331
- var cc = c.charCodeAt(0);
332
- if (cc > 31 && cc < 127) {
333
- rv = c;
334
- } else {
335
- // tab is 9 but handled above
336
- if (cc < 256) {
337
- rv = '\\x';
338
- if (cc < 16 || cc > 256) {
339
- rv += '0';
340
- }
341
- } else {
342
- rv = '\\u';
343
- if (cc < 4096) { // \u1000
344
- rv += '0';
345
- }
346
- }
347
- rv += cc.toString(16).toUpperCase();
668
+ switch (typeof value) {
669
+ case 'boolean': case 'number':
670
+ return ' ' + value + ' ';
671
+ default:
672
+ return "'" + soy.esc.$$escapeJsStringHelper(String(value)) + "'";
348
673
  }
674
+ };
349
675
 
350
- return soy.$$escapeCharJs_[c] = rv;
676
+
677
+ /**
678
+ * Escapes characters in the string to make it valid content for a JS regular
679
+ * expression literal.
680
+ *
681
+ * @param {*} value The value to escape. May not be a string, but the value
682
+ * will be coerced to a string.
683
+ * @return {string} An escaped version of value.
684
+ */
685
+ soy.$$escapeJsRegex = function(value) {
686
+ return soy.esc.$$escapeJsRegexHelper(value);
351
687
  };
352
688
 
689
+
690
+ /**
691
+ * Takes a character and returns the escaped string for that character. For
692
+ * example escapeChar(String.fromCharCode(15)) -> "\\x0E".
693
+ * @param {string} c The character to escape.
694
+ * @return {string} An escaped string representing {@code c}.
695
+ */
696
+ soy.$$escapeChar = function(c) {
697
+ if (c in soy.$$escapeCharJs_) {
698
+ return soy.$$escapeCharJs_[c];
699
+ }
700
+ var rv = c;
701
+ var cc = c.charCodeAt(0);
702
+ if (cc > 31 && cc < 127) {
703
+ rv = c;
704
+ } else {
705
+ // tab is 9 but handled above
706
+ if (cc < 256) {
707
+ rv = '\\x';
708
+ if (cc < 16 || cc > 256) {
709
+ rv += '0';
710
+ }
711
+ } else {
712
+ rv = '\\u';
713
+ if (cc < 4096) { // \u1000
714
+ rv += '0';
715
+ }
716
+ }
717
+ rv += cc.toString(16).toUpperCase();
718
+ }
719
+
720
+ return soy.$$escapeCharJs_[c] = rv;
721
+ };
722
+
723
+ /**
724
+ * Character mappings used internally for soy.$$escapeJs
725
+ * @private
726
+ * @type {Object}
727
+ */
728
+ soy.$$escapeCharJs_ = {
729
+ '\b': '\\b',
730
+ '\f': '\\f',
731
+ '\n': '\\n',
732
+ '\r': '\\r',
733
+ '\t': '\\t',
734
+ '\x0B': '\\x0B', // '\v' is not supported in JScript
735
+ '"': '\\"',
736
+ '\'': '\\\'',
737
+ '\\': '\\\\'
738
+ };
739
+
740
+
353
741
  /**
354
- * Character mappings used internally for soy.$$escapeJs
742
+ * Matches all URI mark characters that conflict with HTML attribute delimiters
743
+ * or that cannot appear in a CSS uri.
744
+ * From <a href="http://www.w3.org/TR/CSS2/grammar.html">G.2: CSS grammar</a>
745
+ * <pre>
746
+ * url ([!#$%&*-~]|{nonascii}|{escape})*
747
+ * </pre>
748
+ *
749
+ * @type {RegExp}
355
750
  * @private
356
- * @type {Object}
357
751
  */
358
- soy.$$escapeCharJs_ = {
359
- '\b': '\\b',
360
- '\f': '\\f',
361
- '\n': '\\n',
362
- '\r': '\\r',
363
- '\t': '\\t',
364
- '\x0B': '\\x0B', // '\v' is not supported in JScript
365
- '"': '\\"',
366
- '\'': '\\\'',
367
- '\\': '\\\\'
368
- };
752
+ soy.$$problematicUriMarks_ = /['()]/g;
369
753
 
754
+ /**
755
+ * @param {string} ch A single character in {@link soy.$$problematicUriMarks_}.
756
+ * @return {string}
757
+ * @private
758
+ */
759
+ soy.$$pctEncode_ = function(ch) {
760
+ return '%' + ch.charCodeAt(0).toString(16);
761
+ };
370
762
 
371
763
  /**
372
764
  * Escapes a string so that it can be safely included in a URI.
373
765
  *
374
- * @param {*} str The string to be escaped. Can be other types, but the value
766
+ * @param {*} value The value to escape. May not be a string, but the value
767
+ * will be coerced to a string.
768
+ * @return {string} An escaped version of value.
769
+ */
770
+ soy.$$escapeUri = function(value) {
771
+ if (typeof value === 'object' &&
772
+ value.contentKind === soydata.SanitizedContentKind.URI) {
773
+ return soy.$$normalizeUri(value);
774
+ }
775
+ // Apostophes and parentheses are not matched by encodeURIComponent.
776
+ // They are technically special in URIs, but only appear in the obsolete mark
777
+ // production in Appendix D.2 of RFC 3986, so can be encoded without changing
778
+ // semantics.
779
+ var encoded = soy.esc.$$escapeUriHelper(value);
780
+ soy.$$problematicUriMarks_.lastIndex = 0;
781
+ if (soy.$$problematicUriMarks_.test(encoded)) {
782
+ return encoded.replace(soy.$$problematicUriMarks_, soy.$$pctEncode_);
783
+ }
784
+ return encoded;
785
+ };
786
+
787
+
788
+ /**
789
+ * Removes rough edges from a URI by escaping any raw HTML/JS string delimiters.
790
+ *
791
+ * @param {*} value The value to escape. May not be a string, but the value
792
+ * will be coerced to a string.
793
+ * @return {string} An escaped version of value.
794
+ */
795
+ soy.$$normalizeUri = function(value) {
796
+ return soy.esc.$$normalizeUriHelper(value);
797
+ };
798
+
799
+
800
+ /**
801
+ * Vets a URI's protocol and removes rough edges from a URI by escaping
802
+ * any raw HTML/JS string delimiters.
803
+ *
804
+ * @param {*} value The value to escape. May not be a string, but the value
805
+ * will be coerced to a string.
806
+ * @return {string} An escaped version of value.
807
+ */
808
+ soy.$$filterNormalizeUri = function(value) {
809
+ return soy.esc.$$filterNormalizeUriHelper(value);
810
+ };
811
+
812
+
813
+ /**
814
+ * Escapes a string so it can safely be included inside a quoted CSS string.
815
+ *
816
+ * @param {*} value The value to escape. May not be a string, but the value
817
+ * will be coerced to a string.
818
+ * @return {string} An escaped version of value.
819
+ */
820
+ soy.$$escapeCssString = function(value) {
821
+ return soy.esc.$$escapeCssStringHelper(value);
822
+ };
823
+
824
+
825
+ /**
826
+ * Encodes a value as a CSS identifier part, keyword, or quantity.
827
+ *
828
+ * @param {*} value The value to escape. May not be a string, but the value
375
829
  * will be coerced to a string.
376
- * @return {string} An escaped copy of the string.
377
- */
378
- soy.$$escapeUri = function(str) {
830
+ * @return {string} A safe CSS identifier part, keyword, or quanitity.
831
+ */
832
+ soy.$$filterCssValue = function(value) {
833
+ // Uses == to intentionally match null and undefined for Java compatibility.
834
+ if (value == null) {
835
+ return '';
836
+ }
837
+ return soy.esc.$$filterCssValueHelper(value);
838
+ };
839
+
840
+
841
+ // -----------------------------------------------------------------------------
842
+ // Basic directives/functions.
843
+
844
+
845
+ /**
846
+ * Converts \r\n, \r, and \n to <br>s
847
+ * @param {*} str The string in which to convert newlines.
848
+ * @return {string} A copy of {@code str} with converted newlines.
849
+ */
850
+ soy.$$changeNewlineToBr = function(str) {
379
851
 
380
852
  str = String(str);
381
853
 
382
- // Checking if the search matches before calling encodeURIComponent avoids an
383
- // extra allocation in IE6. This adds about 10us time in FF and a similiar
384
- // over head in IE6 for lower working set apps, but for large working set
385
- // apps, it saves about 70us per call.
386
- if (!soy.$$ENCODE_URI_REGEXP_.test(str)) {
387
- return encodeURIComponent(str);
388
- } else {
854
+ // This quick test helps in the case when there are no chars to replace, in
855
+ // the worst case this makes barely a difference to the time taken.
856
+ if (!soy.$$CHANGE_NEWLINE_TO_BR_RE_.test(str)) {
389
857
  return str;
390
858
  }
859
+
860
+ return str.replace(/(\r\n|\r|\n)/g, '<br>');
391
861
  };
392
862
 
393
863
  /**
394
- * Regular expression used for determining if a string needs to be encoded.
864
+ * Regular expression used within $$changeNewlineToBr().
395
865
  * @type {RegExp}
396
866
  * @private
397
867
  */
398
- soy.$$ENCODE_URI_REGEXP_ = /^[a-zA-Z0-9\-_.!~*'()]*$/;
868
+ soy.$$CHANGE_NEWLINE_TO_BR_RE_ = /[\r\n]/;
399
869
 
400
870
 
401
871
  /**
@@ -431,7 +901,7 @@ soy.$$insertWordBreaks = function(str, maxCharsBetweenWordBreaks) {
431
901
  charCode != soy.$$CharCode_.SPACE) {
432
902
  resultArr[resultArrLen++] = str.substring(flushIndex, i);
433
903
  flushIndex = i;
434
- resultArr[resultArrLen++] = soy.WORD_BREAK_;
904
+ resultArr[resultArrLen++] = soy.$$WORD_BREAK_;
435
905
  numCharsWithoutBreak = 0;
436
906
  }
437
907
 
@@ -496,7 +966,7 @@ soy.$$insertWordBreaks = function(str, maxCharsBetweenWordBreaks) {
496
966
  };
497
967
 
498
968
  /**
499
- * Special characters used within insertWordBreaks().
969
+ * Special characters used within $$insertWordBreaks().
500
970
  * @enum {number}
501
971
  * @private
502
972
  */
@@ -515,34 +985,103 @@ soy.$$CharCode_ = {
515
985
  * @type {string}
516
986
  * @private
517
987
  */
518
- soy.WORD_BREAK_ =
519
- soy.IS_WEBKIT_ ? '<wbr></wbr>' : soy.IS_OPERA_ ? '&shy;' : '<wbr>';
988
+ soy.$$WORD_BREAK_ =
989
+ soy.$$IS_WEBKIT_ ? '<wbr></wbr>' : soy.$$IS_OPERA_ ? '&shy;' : '<wbr>';
520
990
 
521
991
 
522
992
  /**
523
- * Converts \r\n, \r, and \n to <br>s
524
- * @param {*} str The string in which to convert newlines.
525
- * @return {string} A copy of {@code str} with converted newlines.
993
+ * Truncates a string to a given max length (if it's currently longer),
994
+ * optionally adding ellipsis at the end.
995
+ *
996
+ * @param {*} str The string to truncate. Can be other types, but the value will
997
+ * be coerced to a string.
998
+ * @param {number} maxLen The maximum length of the string after truncation
999
+ * (including ellipsis, if applicable).
1000
+ * @param {boolean} doAddEllipsis Whether to add ellipsis if the string needs
1001
+ * truncation.
1002
+ * @return {string} The string after truncation.
526
1003
  */
527
- soy.$$changeNewlineToBr = function(str) {
1004
+ soy.$$truncate = function(str, maxLen, doAddEllipsis) {
528
1005
 
529
1006
  str = String(str);
1007
+ if (str.length <= maxLen) {
1008
+ return str; // no need to truncate
1009
+ }
530
1010
 
531
- // This quick test helps in the case when there are no chars to replace, in
532
- // the worst case this makes barely a difference to the time taken.
533
- if (!soy.$$CHANGE_NEWLINE_TO_BR_RE_.test(str)) {
534
- return str;
1011
+ // If doAddEllipsis, either reduce maxLen to compensate, or else if maxLen is
1012
+ // too small, just turn off doAddEllipsis.
1013
+ if (doAddEllipsis) {
1014
+ if (maxLen > 3) {
1015
+ maxLen -= 3;
1016
+ } else {
1017
+ doAddEllipsis = false;
1018
+ }
535
1019
  }
536
1020
 
537
- return str.replace(/(\r\n|\r|\n)/g, '<br>');
1021
+ // Make sure truncating at maxLen doesn't cut up a unicode surrogate pair.
1022
+ if (soy.$$isHighSurrogate_(str.charAt(maxLen - 1)) &&
1023
+ soy.$$isLowSurrogate_(str.charAt(maxLen))) {
1024
+ maxLen -= 1;
1025
+ }
1026
+
1027
+ // Truncate.
1028
+ str = str.substring(0, maxLen);
1029
+
1030
+ // Add ellipsis.
1031
+ if (doAddEllipsis) {
1032
+ str += '...';
1033
+ }
1034
+
1035
+ return str;
538
1036
  };
539
1037
 
540
1038
  /**
541
- * Regular expression used within $$changeNewlineToBr().
542
- * @type {RegExp}
1039
+ * Private helper for $$truncate() to check whether a char is a high surrogate.
1040
+ * @param {string} ch The char to check.
1041
+ * @return {boolean} Whether the given char is a unicode high surrogate.
543
1042
  * @private
544
1043
  */
545
- soy.$$CHANGE_NEWLINE_TO_BR_RE_ = /[\r\n]/;
1044
+ soy.$$isHighSurrogate_ = function(ch) {
1045
+ return 0xD800 <= ch && ch <= 0xDBFF;
1046
+ };
1047
+
1048
+ /**
1049
+ * Private helper for $$truncate() to check whether a char is a low surrogate.
1050
+ * @param {string} ch The char to check.
1051
+ * @return {boolean} Whether the given char is a unicode low surrogate.
1052
+ * @private
1053
+ */
1054
+ soy.$$isLowSurrogate_ = function(ch) {
1055
+ return 0xDC00 <= ch && ch <= 0xDFFF;
1056
+ };
1057
+
1058
+
1059
+ // -----------------------------------------------------------------------------
1060
+ // Bidi directives/functions.
1061
+
1062
+
1063
+ /**
1064
+ * Returns the leading horizontal edge, i.e. "left" or "right", depending on
1065
+ * bidiGlobalDir.
1066
+ * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
1067
+ * if rtl, 0 if unknown.
1068
+ * @return {string} "right" for RTL context and "left" otherwise.
1069
+ */
1070
+ soy.$$bidiStartEdge = function(bidiGlobalDir) {
1071
+ return bidiGlobalDir < 0 ? 'right' : 'left';
1072
+ };
1073
+
1074
+
1075
+ /**
1076
+ * Returns the trailing horizontal edge, i.e. "right" or "left", depending on
1077
+ * bidiGlobalDir.
1078
+ * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
1079
+ * if rtl, 0 if unknown.
1080
+ * @return {string} "left" for RTL context and "right" otherwise.
1081
+ */
1082
+ soy.$$bidiEndEdge = function(bidiGlobalDir) {
1083
+ return bidiGlobalDir < 0 ? 'left' : 'right';
1084
+ };
546
1085
 
547
1086
 
548
1087
  /**
@@ -579,10 +1118,24 @@ soy.$$bidiTextDir = function(text, opt_isHtml) {
579
1118
  */
580
1119
  soy.$$bidiDirAttr = function(bidiGlobalDir, text, opt_isHtml) {
581
1120
  var dir = soy.$$bidiTextDir(text, opt_isHtml);
582
- if (dir != bidiGlobalDir) {
583
- return dir < 0 ? 'dir=rtl' : dir > 0 ? 'dir=ltr' : '';
584
- }
585
- return '';
1121
+ return new soydata.SanitizedHtmlAttribute(
1122
+ dir && dir != bidiGlobalDir ? dir < 0 ? 'dir=rtl' : 'dir=ltr' : '');
1123
+ };
1124
+
1125
+
1126
+ /**
1127
+ * Returns a Unicode BiDi mark matching bidiGlobalDir (LRM or RLM), or an empty
1128
+ * string if bidiGlobalDir is 0 (unknown).
1129
+ * @param {number} bidiGlobalDir The global directionality context: 1 if ltr, -1
1130
+ * if rtl, 0 if unknown.
1131
+ * @return {string} A Unicode bidi mark matching bidiGlobalDir, or the empty
1132
+ * string when bidiGlobalDir is 0 (unknown).
1133
+ */
1134
+ soy.$$bidiMark = function(bidiGlobalDir) {
1135
+ return (
1136
+ (bidiGlobalDir > 0) ? '\u200E' /*LRM*/ :
1137
+ (bidiGlobalDir < 0) ? '\u200F' /*RLM*/ :
1138
+ '');
586
1139
  };
587
1140
 
588
1141
 
@@ -597,13 +1150,13 @@ soy.$$bidiDirAttr = function(bidiGlobalDir, text, opt_isHtml) {
597
1150
  * @param {string} text The text whose directionality is to be estimated.
598
1151
  * @param {boolean=} opt_isHtml Whether text is HTML/HTML-escaped.
599
1152
  * Default: false.
600
- * @return {string} A Unicode bidi mark matching bidiGlobalDir, or
601
- * the empty string when text's overall and exit directionalities both match
602
- * bidiGlobalDir.
1153
+ * @return {string} A Unicode bidi mark matching bidiGlobalDir, or the empty
1154
+ * string when text's overall and exit directionalities both match
1155
+ * bidiGlobalDir, or bidiGlobalDir is 0 (unknown).
603
1156
  */
604
1157
  soy.$$bidiMarkAfter = function(bidiGlobalDir, text, opt_isHtml) {
605
1158
  var dir = soy.$$bidiTextDir(text, opt_isHtml);
606
- return soy.$$bidiMarkAfterKnownDir(bidiGlobalDir, dir, text, opt_isHtml);
1159
+ return soy.$$bidiMarkAfterKnownDir_(bidiGlobalDir, dir, text, opt_isHtml);
607
1160
  };
608
1161
 
609
1162
 
@@ -621,9 +1174,10 @@ soy.$$bidiMarkAfter = function(bidiGlobalDir, text, opt_isHtml) {
621
1174
  * Default: false.
622
1175
  * @return {string} A Unicode bidi mark matching bidiGlobalDir, or
623
1176
  * the empty string when text's overall and exit directionalities both match
624
- * bidiGlobalDir.
1177
+ * bidiGlobalDir, or bidiGlobalDir is 0 (unknown).
1178
+ * @private
625
1179
  */
626
- soy.$$bidiMarkAfterKnownDir = function(bidiGlobalDir, dir, text, opt_isHtml) {
1180
+ soy.$$bidiMarkAfterKnownDir_ = function(bidiGlobalDir, dir, text, opt_isHtml) {
627
1181
  return (
628
1182
  bidiGlobalDir > 0 && (dir < 0 ||
629
1183
  soy.$$bidiIsRtlExitText_(text, opt_isHtml)) ? '\u200E' : // LRM
@@ -673,7 +1227,7 @@ soy.$$BIDI_HTML_SKIP_RE_ = /<[^>]*>|&[^;]+;/g;
673
1227
  soy.$$bidiSpanWrap = function(bidiGlobalDir, str) {
674
1228
  str = String(str);
675
1229
  var textDir = soy.$$bidiTextDir(str, true);
676
- var reset = soy.$$bidiMarkAfterKnownDir(bidiGlobalDir, textDir, str, true);
1230
+ var reset = soy.$$bidiMarkAfterKnownDir_(bidiGlobalDir, textDir, str, true);
677
1231
  if (textDir > 0 && bidiGlobalDir <= 0) {
678
1232
  str = '<span dir=ltr>' + str + '</span>';
679
1233
  } else if (textDir < 0 && bidiGlobalDir >= 0) {
@@ -699,7 +1253,7 @@ soy.$$bidiSpanWrap = function(bidiGlobalDir, str) {
699
1253
  soy.$$bidiUnicodeWrap = function(bidiGlobalDir, str) {
700
1254
  str = String(str);
701
1255
  var textDir = soy.$$bidiTextDir(str, true);
702
- var reset = soy.$$bidiMarkAfterKnownDir(bidiGlobalDir, textDir, str, true);
1256
+ var reset = soy.$$bidiMarkAfterKnownDir_(bidiGlobalDir, textDir, str, true);
703
1257
  if (textDir > 0 && bidiGlobalDir <= 0) {
704
1258
  str = '\u202A' + str + '\u202C';
705
1259
  } else if (textDir < 0 && bidiGlobalDir >= 0) {
@@ -878,3 +1432,481 @@ soy.$$bidiIsRtlExitText_ = function(str, opt_isHtml) {
878
1432
  str = soy.$$bidiStripHtmlIfNecessary_(str, opt_isHtml);
879
1433
  return soy.$$bidiRtlExitDirCheckRe_.test(str);
880
1434
  };
1435
+
1436
+
1437
+ // -----------------------------------------------------------------------------
1438
+ // Generated code.
1439
+
1440
+
1441
+
1442
+
1443
+ // START GENERATED CODE FOR ESCAPERS.
1444
+
1445
+ /**
1446
+ * @type {function (*) : string}
1447
+ */
1448
+ soy.esc.$$escapeUriHelper = function(v) {
1449
+ return encodeURIComponent(String(v));
1450
+ };
1451
+
1452
+ /**
1453
+ * Maps charcters to the escaped versions for the named escape directives.
1454
+ * @type {Object.<string, string>}
1455
+ * @private
1456
+ */
1457
+ soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_ = {
1458
+ '\x00': '\x26#0;',
1459
+ '\x22': '\x26quot;',
1460
+ '\x26': '\x26amp;',
1461
+ '\x27': '\x26#39;',
1462
+ '\x3c': '\x26lt;',
1463
+ '\x3e': '\x26gt;',
1464
+ '\x09': '\x26#9;',
1465
+ '\x0a': '\x26#10;',
1466
+ '\x0b': '\x26#11;',
1467
+ '\x0c': '\x26#12;',
1468
+ '\x0d': '\x26#13;',
1469
+ ' ': '\x26#32;',
1470
+ '-': '\x26#45;',
1471
+ '\/': '\x26#47;',
1472
+ '\x3d': '\x26#61;',
1473
+ '`': '\x26#96;',
1474
+ '\x85': '\x26#133;',
1475
+ '\xa0': '\x26#160;',
1476
+ '\u2028': '\x26#8232;',
1477
+ '\u2029': '\x26#8233;'
1478
+ };
1479
+
1480
+ /**
1481
+ * A function that can be used with String.replace..
1482
+ * @param {string} ch A single character matched by a compatible matcher.
1483
+ * @return {string} A token in the output language.
1484
+ * @private
1485
+ */
1486
+ soy.esc.$$REPLACER_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_ = function(ch) {
1487
+ return soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_[ch];
1488
+ };
1489
+
1490
+ /**
1491
+ * Maps charcters to the escaped versions for the named escape directives.
1492
+ * @type {Object.<string, string>}
1493
+ * @private
1494
+ */
1495
+ soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_ = {
1496
+ '\x00': '\\x00',
1497
+ '\x08': '\\x08',
1498
+ '\x09': '\\t',
1499
+ '\x0a': '\\n',
1500
+ '\x0b': '\\x0b',
1501
+ '\x0c': '\\f',
1502
+ '\x0d': '\\r',
1503
+ '\x22': '\\x22',
1504
+ '\x26': '\\x26',
1505
+ '\x27': '\\x27',
1506
+ '\/': '\\\/',
1507
+ '\x3c': '\\x3c',
1508
+ '\x3d': '\\x3d',
1509
+ '\x3e': '\\x3e',
1510
+ '\\': '\\\\',
1511
+ '\x85': '\\x85',
1512
+ '\u2028': '\\u2028',
1513
+ '\u2029': '\\u2029',
1514
+ '$': '\\x24',
1515
+ '(': '\\x28',
1516
+ ')': '\\x29',
1517
+ '*': '\\x2a',
1518
+ '+': '\\x2b',
1519
+ ',': '\\x2c',
1520
+ '-': '\\x2d',
1521
+ '.': '\\x2e',
1522
+ ':': '\\x3a',
1523
+ '?': '\\x3f',
1524
+ '[': '\\x5b',
1525
+ ']': '\\x5d',
1526
+ '^': '\\x5e',
1527
+ '{': '\\x7b',
1528
+ '|': '\\x7c',
1529
+ '}': '\\x7d'
1530
+ };
1531
+
1532
+ /**
1533
+ * A function that can be used with String.replace..
1534
+ * @param {string} ch A single character matched by a compatible matcher.
1535
+ * @return {string} A token in the output language.
1536
+ * @private
1537
+ */
1538
+ soy.esc.$$REPLACER_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_ = function(ch) {
1539
+ return soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_[ch];
1540
+ };
1541
+
1542
+ /**
1543
+ * Maps charcters to the escaped versions for the named escape directives.
1544
+ * @type {Object.<string, string>}
1545
+ * @private
1546
+ */
1547
+ soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_CSS_STRING_ = {
1548
+ '\x00': '\\0 ',
1549
+ '\x08': '\\8 ',
1550
+ '\x09': '\\9 ',
1551
+ '\x0a': '\\a ',
1552
+ '\x0b': '\\b ',
1553
+ '\x0c': '\\c ',
1554
+ '\x0d': '\\d ',
1555
+ '\x22': '\\22 ',
1556
+ '\x26': '\\26 ',
1557
+ '\x27': '\\27 ',
1558
+ '(': '\\28 ',
1559
+ ')': '\\29 ',
1560
+ '*': '\\2a ',
1561
+ '\/': '\\2f ',
1562
+ ':': '\\3a ',
1563
+ ';': '\\3b ',
1564
+ '\x3c': '\\3c ',
1565
+ '\x3d': '\\3d ',
1566
+ '\x3e': '\\3e ',
1567
+ '@': '\\40 ',
1568
+ '\\': '\\5c ',
1569
+ '{': '\\7b ',
1570
+ '}': '\\7d ',
1571
+ '\x85': '\\85 ',
1572
+ '\xa0': '\\a0 ',
1573
+ '\u2028': '\\2028 ',
1574
+ '\u2029': '\\2029 '
1575
+ };
1576
+
1577
+ /**
1578
+ * A function that can be used with String.replace..
1579
+ * @param {string} ch A single character matched by a compatible matcher.
1580
+ * @return {string} A token in the output language.
1581
+ * @private
1582
+ */
1583
+ soy.esc.$$REPLACER_FOR_ESCAPE_CSS_STRING_ = function(ch) {
1584
+ return soy.esc.$$ESCAPE_MAP_FOR_ESCAPE_CSS_STRING_[ch];
1585
+ };
1586
+
1587
+ /**
1588
+ * Maps charcters to the escaped versions for the named escape directives.
1589
+ * @type {Object.<string, string>}
1590
+ * @private
1591
+ */
1592
+ soy.esc.$$ESCAPE_MAP_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_ = {
1593
+ '\x00': '%00',
1594
+ '\x01': '%01',
1595
+ '\x02': '%02',
1596
+ '\x03': '%03',
1597
+ '\x04': '%04',
1598
+ '\x05': '%05',
1599
+ '\x06': '%06',
1600
+ '\x07': '%07',
1601
+ '\x08': '%08',
1602
+ '\x09': '%09',
1603
+ '\x0a': '%0A',
1604
+ '\x0b': '%0B',
1605
+ '\x0c': '%0C',
1606
+ '\x0d': '%0D',
1607
+ '\x0e': '%0E',
1608
+ '\x0f': '%0F',
1609
+ '\x10': '%10',
1610
+ '\x11': '%11',
1611
+ '\x12': '%12',
1612
+ '\x13': '%13',
1613
+ '\x14': '%14',
1614
+ '\x15': '%15',
1615
+ '\x16': '%16',
1616
+ '\x17': '%17',
1617
+ '\x18': '%18',
1618
+ '\x19': '%19',
1619
+ '\x1a': '%1A',
1620
+ '\x1b': '%1B',
1621
+ '\x1c': '%1C',
1622
+ '\x1d': '%1D',
1623
+ '\x1e': '%1E',
1624
+ '\x1f': '%1F',
1625
+ ' ': '%20',
1626
+ '\x22': '%22',
1627
+ '\x27': '%27',
1628
+ '(': '%28',
1629
+ ')': '%29',
1630
+ '\x3c': '%3C',
1631
+ '\x3e': '%3E',
1632
+ '\\': '%5C',
1633
+ '{': '%7B',
1634
+ '}': '%7D',
1635
+ '\x7f': '%7F',
1636
+ '\x85': '%C2%85',
1637
+ '\xa0': '%C2%A0',
1638
+ '\u2028': '%E2%80%A8',
1639
+ '\u2029': '%E2%80%A9',
1640
+ '\uff01': '%EF%BC%81',
1641
+ '\uff03': '%EF%BC%83',
1642
+ '\uff04': '%EF%BC%84',
1643
+ '\uff06': '%EF%BC%86',
1644
+ '\uff07': '%EF%BC%87',
1645
+ '\uff08': '%EF%BC%88',
1646
+ '\uff09': '%EF%BC%89',
1647
+ '\uff0a': '%EF%BC%8A',
1648
+ '\uff0b': '%EF%BC%8B',
1649
+ '\uff0c': '%EF%BC%8C',
1650
+ '\uff0f': '%EF%BC%8F',
1651
+ '\uff1a': '%EF%BC%9A',
1652
+ '\uff1b': '%EF%BC%9B',
1653
+ '\uff1d': '%EF%BC%9D',
1654
+ '\uff1f': '%EF%BC%9F',
1655
+ '\uff20': '%EF%BC%A0',
1656
+ '\uff3b': '%EF%BC%BB',
1657
+ '\uff3d': '%EF%BC%BD'
1658
+ };
1659
+
1660
+ /**
1661
+ * A function that can be used with String.replace..
1662
+ * @param {string} ch A single character matched by a compatible matcher.
1663
+ * @return {string} A token in the output language.
1664
+ * @private
1665
+ */
1666
+ soy.esc.$$REPLACER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_ = function(ch) {
1667
+ return soy.esc.$$ESCAPE_MAP_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_[ch];
1668
+ };
1669
+
1670
+ /**
1671
+ * Matches characters that need to be escaped for the named directives.
1672
+ * @type RegExp
1673
+ * @private
1674
+ */
1675
+ soy.esc.$$MATCHER_FOR_ESCAPE_HTML_ = /[\x00\x22\x26\x27\x3c\x3e]/g;
1676
+
1677
+ /**
1678
+ * Matches characters that need to be escaped for the named directives.
1679
+ * @type RegExp
1680
+ * @private
1681
+ */
1682
+ soy.esc.$$MATCHER_FOR_NORMALIZE_HTML_ = /[\x00\x22\x27\x3c\x3e]/g;
1683
+
1684
+ /**
1685
+ * Matches characters that need to be escaped for the named directives.
1686
+ * @type RegExp
1687
+ * @private
1688
+ */
1689
+ soy.esc.$$MATCHER_FOR_ESCAPE_HTML_NOSPACE_ = /[\x00\x09-\x0d \x22\x26\x27\x2d\/\x3c-\x3e`\x85\xa0\u2028\u2029]/g;
1690
+
1691
+ /**
1692
+ * Matches characters that need to be escaped for the named directives.
1693
+ * @type RegExp
1694
+ * @private
1695
+ */
1696
+ soy.esc.$$MATCHER_FOR_NORMALIZE_HTML_NOSPACE_ = /[\x00\x09-\x0d \x22\x27\x2d\/\x3c-\x3e`\x85\xa0\u2028\u2029]/g;
1697
+
1698
+ /**
1699
+ * Matches characters that need to be escaped for the named directives.
1700
+ * @type RegExp
1701
+ * @private
1702
+ */
1703
+ soy.esc.$$MATCHER_FOR_ESCAPE_JS_STRING_ = /[\x00\x08-\x0d\x22\x26\x27\/\x3c-\x3e\\\x85\u2028\u2029]/g;
1704
+
1705
+ /**
1706
+ * Matches characters that need to be escaped for the named directives.
1707
+ * @type RegExp
1708
+ * @private
1709
+ */
1710
+ soy.esc.$$MATCHER_FOR_ESCAPE_JS_REGEX_ = /[\x00\x08-\x0d\x22\x24\x26-\/\x3a\x3c-\x3f\x5b-\x5e\x7b-\x7d\x85\u2028\u2029]/g;
1711
+
1712
+ /**
1713
+ * Matches characters that need to be escaped for the named directives.
1714
+ * @type RegExp
1715
+ * @private
1716
+ */
1717
+ soy.esc.$$MATCHER_FOR_ESCAPE_CSS_STRING_ = /[\x00\x08-\x0d\x22\x26-\x2a\/\x3a-\x3e@\\\x7b\x7d\x85\xa0\u2028\u2029]/g;
1718
+
1719
+ /**
1720
+ * Matches characters that need to be escaped for the named directives.
1721
+ * @type RegExp
1722
+ * @private
1723
+ */
1724
+ soy.esc.$$MATCHER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_ = /[\x00- \x22\x27-\x29\x3c\x3e\\\x7b\x7d\x7f\x85\xa0\u2028\u2029\uff01\uff03\uff04\uff06-\uff0c\uff0f\uff1a\uff1b\uff1d\uff1f\uff20\uff3b\uff3d]/g;
1725
+
1726
+ /**
1727
+ * A pattern that vets values produced by the named directives.
1728
+ * @type RegExp
1729
+ * @private
1730
+ */
1731
+ soy.esc.$$FILTER_FOR_FILTER_CSS_VALUE_ = /^(?!-*(?:expression|(?:moz-)?binding))(?:[.#]?-?(?:[_a-z0-9-]+)(?:-[_a-z0-9-]+)*-?|-?(?:[0-9]+(?:\.[0-9]*)?|\.[0-9]+)(?:[a-z]{1,2}|%)?|!important|)$/i;
1732
+
1733
+ /**
1734
+ * A pattern that vets values produced by the named directives.
1735
+ * @type RegExp
1736
+ * @private
1737
+ */
1738
+ soy.esc.$$FILTER_FOR_FILTER_NORMALIZE_URI_ = /^(?:(?:https?|mailto):|[^&:\/?#]*(?:[\/?#]|$))/i;
1739
+
1740
+ /**
1741
+ * A pattern that vets values produced by the named directives.
1742
+ * @type RegExp
1743
+ * @private
1744
+ */
1745
+ soy.esc.$$FILTER_FOR_FILTER_HTML_ATTRIBUTE_ = /^(?!style|on|action|archive|background|cite|classid|codebase|data|dsync|href|longdesc|src|usemap)(?:[a-z0-9_$:-]*)$/i;
1746
+
1747
+ /**
1748
+ * A pattern that vets values produced by the named directives.
1749
+ * @type RegExp
1750
+ * @private
1751
+ */
1752
+ soy.esc.$$FILTER_FOR_FILTER_HTML_ELEMENT_NAME_ = /^(?!script|style|title|textarea|xmp|no)[a-z0-9_$:-]*$/i;
1753
+
1754
+ /**
1755
+ * A helper for the Soy directive |escapeHtml
1756
+ * @param {*} value Can be of any type but will be coerced to a string.
1757
+ * @return {string} The escaped text.
1758
+ */
1759
+ soy.esc.$$escapeHtmlHelper = function(value) {
1760
+ var str = String(value);
1761
+ return str.replace(
1762
+ soy.esc.$$MATCHER_FOR_ESCAPE_HTML_,
1763
+ soy.esc.$$REPLACER_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_);
1764
+ };
1765
+
1766
+ /**
1767
+ * A helper for the Soy directive |normalizeHtml
1768
+ * @param {*} value Can be of any type but will be coerced to a string.
1769
+ * @return {string} The escaped text.
1770
+ */
1771
+ soy.esc.$$normalizeHtmlHelper = function(value) {
1772
+ var str = String(value);
1773
+ return str.replace(
1774
+ soy.esc.$$MATCHER_FOR_NORMALIZE_HTML_,
1775
+ soy.esc.$$REPLACER_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_);
1776
+ };
1777
+
1778
+ /**
1779
+ * A helper for the Soy directive |escapeHtmlNospace
1780
+ * @param {*} value Can be of any type but will be coerced to a string.
1781
+ * @return {string} The escaped text.
1782
+ */
1783
+ soy.esc.$$escapeHtmlNospaceHelper = function(value) {
1784
+ var str = String(value);
1785
+ return str.replace(
1786
+ soy.esc.$$MATCHER_FOR_ESCAPE_HTML_NOSPACE_,
1787
+ soy.esc.$$REPLACER_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_);
1788
+ };
1789
+
1790
+ /**
1791
+ * A helper for the Soy directive |normalizeHtmlNospace
1792
+ * @param {*} value Can be of any type but will be coerced to a string.
1793
+ * @return {string} The escaped text.
1794
+ */
1795
+ soy.esc.$$normalizeHtmlNospaceHelper = function(value) {
1796
+ var str = String(value);
1797
+ return str.replace(
1798
+ soy.esc.$$MATCHER_FOR_NORMALIZE_HTML_NOSPACE_,
1799
+ soy.esc.$$REPLACER_FOR_ESCAPE_HTML__AND__NORMALIZE_HTML__AND__ESCAPE_HTML_NOSPACE__AND__NORMALIZE_HTML_NOSPACE_);
1800
+ };
1801
+
1802
+ /**
1803
+ * A helper for the Soy directive |escapeJsString
1804
+ * @param {*} value Can be of any type but will be coerced to a string.
1805
+ * @return {string} The escaped text.
1806
+ */
1807
+ soy.esc.$$escapeJsStringHelper = function(value) {
1808
+ var str = String(value);
1809
+ return str.replace(
1810
+ soy.esc.$$MATCHER_FOR_ESCAPE_JS_STRING_,
1811
+ soy.esc.$$REPLACER_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_);
1812
+ };
1813
+
1814
+ /**
1815
+ * A helper for the Soy directive |escapeJsRegex
1816
+ * @param {*} value Can be of any type but will be coerced to a string.
1817
+ * @return {string} The escaped text.
1818
+ */
1819
+ soy.esc.$$escapeJsRegexHelper = function(value) {
1820
+ var str = String(value);
1821
+ return str.replace(
1822
+ soy.esc.$$MATCHER_FOR_ESCAPE_JS_REGEX_,
1823
+ soy.esc.$$REPLACER_FOR_ESCAPE_JS_STRING__AND__ESCAPE_JS_REGEX_);
1824
+ };
1825
+
1826
+ /**
1827
+ * A helper for the Soy directive |escapeCssString
1828
+ * @param {*} value Can be of any type but will be coerced to a string.
1829
+ * @return {string} The escaped text.
1830
+ */
1831
+ soy.esc.$$escapeCssStringHelper = function(value) {
1832
+ var str = String(value);
1833
+ return str.replace(
1834
+ soy.esc.$$MATCHER_FOR_ESCAPE_CSS_STRING_,
1835
+ soy.esc.$$REPLACER_FOR_ESCAPE_CSS_STRING_);
1836
+ };
1837
+
1838
+ /**
1839
+ * A helper for the Soy directive |filterCssValue
1840
+ * @param {*} value Can be of any type but will be coerced to a string.
1841
+ * @return {string} The escaped text.
1842
+ */
1843
+ soy.esc.$$filterCssValueHelper = function(value) {
1844
+ var str = String(value);
1845
+ if (!soy.esc.$$FILTER_FOR_FILTER_CSS_VALUE_.test(str)) {
1846
+ return 'zSoyz';
1847
+ }
1848
+ return str;
1849
+ };
1850
+
1851
+ /**
1852
+ * A helper for the Soy directive |normalizeUri
1853
+ * @param {*} value Can be of any type but will be coerced to a string.
1854
+ * @return {string} The escaped text.
1855
+ */
1856
+ soy.esc.$$normalizeUriHelper = function(value) {
1857
+ var str = String(value);
1858
+ return str.replace(
1859
+ soy.esc.$$MATCHER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_,
1860
+ soy.esc.$$REPLACER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_);
1861
+ };
1862
+
1863
+ /**
1864
+ * A helper for the Soy directive |filterNormalizeUri
1865
+ * @param {*} value Can be of any type but will be coerced to a string.
1866
+ * @return {string} The escaped text.
1867
+ */
1868
+ soy.esc.$$filterNormalizeUriHelper = function(value) {
1869
+ var str = String(value);
1870
+ if (!soy.esc.$$FILTER_FOR_FILTER_NORMALIZE_URI_.test(str)) {
1871
+ return 'zSoyz';
1872
+ }
1873
+ return str.replace(
1874
+ soy.esc.$$MATCHER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_,
1875
+ soy.esc.$$REPLACER_FOR_NORMALIZE_URI__AND__FILTER_NORMALIZE_URI_);
1876
+ };
1877
+
1878
+ /**
1879
+ * A helper for the Soy directive |filterHtmlAttribute
1880
+ * @param {*} value Can be of any type but will be coerced to a string.
1881
+ * @return {string} The escaped text.
1882
+ */
1883
+ soy.esc.$$filterHtmlAttributeHelper = function(value) {
1884
+ var str = String(value);
1885
+ if (!soy.esc.$$FILTER_FOR_FILTER_HTML_ATTRIBUTE_.test(str)) {
1886
+ return 'zSoyz';
1887
+ }
1888
+ return str;
1889
+ };
1890
+
1891
+ /**
1892
+ * A helper for the Soy directive |filterHtmlElementName
1893
+ * @param {*} value Can be of any type but will be coerced to a string.
1894
+ * @return {string} The escaped text.
1895
+ */
1896
+ soy.esc.$$filterHtmlElementNameHelper = function(value) {
1897
+ var str = String(value);
1898
+ if (!soy.esc.$$FILTER_FOR_FILTER_HTML_ELEMENT_NAME_.test(str)) {
1899
+ return 'zSoyz';
1900
+ }
1901
+ return str;
1902
+ };
1903
+
1904
+ /**
1905
+ * Matches all tags, HTML comments, and DOCTYPEs in tag soup HTML.
1906
+ *
1907
+ * @type {RegExp}
1908
+ * @private
1909
+ */
1910
+ soy.esc.$$HTML_TAG_REGEX_ = /<(?:!|\/?[a-zA-Z])(?:[^>'"]|"[^"]*"|'[^']*')*>/g;
1911
+
1912
+ // END GENERATED CODE