1
// Copyright 2004-2008 Castle Project - http://www.castleproject.org/
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
7 // http://www.apache.org/licenses/LICENSE-2.0
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.
15 namespace Castle
.MonoRail
.Framework
.JSGeneration
18 using System
.Collections
;
19 using System
.Collections
.Generic
;
22 using System
.Text
.RegularExpressions
;
29 public class JSCodeGenerator
: IJSCodeGenerator
31 private Dictionary
<string, object> extensions
=
32 new Dictionary
<string, object>(StringComparer
.InvariantCultureIgnoreCase
);
34 private StringBuilder lines
= new StringBuilder();
35 private IJSGenerator generator
;
36 private IServerUtility serverUtility
;
37 private IViewEngineManager viewEngineManager
;
38 private IEngineContext engineContext
;
39 private IController controller
;
40 private IControllerContext context
;
41 private IUrlBuilder urlBuilder
;
44 /// Initializes a new instance of the <see cref="JSCodeGenerator"/> class.
46 public JSCodeGenerator()
51 /// Initializes a new instance of the <see cref="JSCodeGenerator"/> class.
53 /// <param name="serverUtility">The server utility.</param>
54 /// <param name="viewEngineManager">The view engine manager.</param>
55 /// <param name="engineContext">The engine context.</param>
56 /// <param name="controller">The controller.</param>
57 /// <param name="context">The context.</param>
58 /// <param name="urlBuilder">The URL builder.</param>
59 public JSCodeGenerator(IServerUtility serverUtility
, IViewEngineManager viewEngineManager
,
60 IEngineContext engineContext
, IController controller
, IControllerContext context
,
61 IUrlBuilder urlBuilder
)
63 this.serverUtility
= serverUtility
;
64 this.viewEngineManager
= viewEngineManager
;
65 this.engineContext
= engineContext
;
66 this.controller
= controller
;
67 this.context
= context
;
68 this.urlBuilder
= urlBuilder
;
72 /// Gets the main JS generator.
74 /// <value>The JS generator.</value>
75 public IJSGenerator JSGenerator
77 get { return generator; }
78 set { generator = value; }
82 /// Gets or sets the URL helper.
84 /// <value>The URL helper.</value>
85 public IUrlBuilder UrlBuilder
87 get { return urlBuilder; }
88 set { urlBuilder = value; }
92 /// Gets or sets the server utility.
94 /// <value>The server utility.</value>
95 public IServerUtility ServerUtility
97 get { return serverUtility; }
98 set { serverUtility = value; }
102 /// Gets or sets the engine context.
104 /// <value>The engine context.</value>
105 public IEngineContext EngineContext
107 get { return engineContext; }
108 set { engineContext = value; }
112 /// Gets the extensions.
114 /// <value>The extensions.</value>
115 public IDictionary
<string, object> Extensions
117 get { return extensions; }
121 /// Calls the specified function with the optional arguments.
123 /// <param name="function">The function name.</param>
124 /// <param name="args">The arguments.</param>
126 /// The following example uses nvelocity syntax:
128 /// $page.call('myJsFunctionAlreadyDeclared', '10', "'message'", $somethingfrompropertybag, $anothermessage.to_squote)
134 /// myJsFunctionAlreadyDeclared(10, 'message', 1001, 'who let the dogs out?')
137 public void Call(object function
, params object[] args
)
139 if (String
.IsNullOrEmpty(function
.ToString()))
141 throw new ArgumentException("function cannot be null or an empty string.", "function");
144 Record(function
+ "(" + BuildJSArguments(args
) + ")");
148 /// Writes the content specified to the generator instance
150 /// <param name="content">The content.</param>
152 /// This is for advanced scenarios and for the infrastructure. Usually not useful.
154 public void Write(string content
)
156 lines
.Append(content
);
160 /// Outputs the content using the renderOptions approach.
162 /// If the renderOptions is a string, the content is escaped and quoted.
165 /// If the renderOptions is a dictionary, we extract the key <c>partial</c>
166 /// and evaluate the template it points to. The content is escaped and quoted.
169 /// <param name="renderOptions">The render options.</param>
170 /// <returns></returns>
172 /// The following example uses nvelocity syntax:
174 /// $page.Call('myJsFunction', $page.render("%{partial='shared/newmessage.vm'}") )
180 /// myJsFunction('the content from the newmessage partial view template')
183 public object Render(object renderOptions
)
185 if (renderOptions
== null)
187 throw new ArgumentNullException("renderOptions",
188 "renderOptions cannot be null. Must be a string or a dictionary");
190 else if (renderOptions
is IDictionary
)
192 IDictionary options
= (IDictionary
) renderOptions
;
194 String partialName
= (String
) options
["partial"];
196 if (partialName
== null)
198 throw new ArgumentNullException("renderOptions",
199 "renderOptions, as a dictionary, must have a 'partial' " +
200 "entry with the template name to render");
205 StringWriter writer
= new StringWriter();
207 viewEngineManager
.ProcessPartial(partialName
, writer
, engineContext
, controller
, context
);
209 // Ideally we would call (less overhead and safer)
210 // viewEngineManager.ProcessPartial(partialName, writer, engineContext, parameters);
212 renderOptions
= writer
.ToString();
216 throw new MonoRailException("Could not process partial " + partialName
, ex
);
220 return AbstractHelper
.Quote(JsEscape(renderOptions
.ToString()));
224 /// Writes the content specified to the generator instance
226 /// <param name="content">The content.</param>
228 /// This is for advanced scenarios and for the infrastructure. Usually not useful.
230 public void AppendLine(string content
)
236 /// Gets the js lines.
238 /// <value>The js lines.</value>
239 public StringBuilder Lines
241 get { return lines; }
245 /// Records the specified line on the generator.
247 /// <param name="line">The line.</param>
248 public void Record(string line
)
250 Lines
.AppendFormat("{0};\r\n", line
);
254 /// Builds the JS arguments.
256 /// <param name="args">The args.</param>
257 /// <returns></returns>
258 public string BuildJSArguments(object[] args
)
260 if (args
== null || args
.Length
== 0) return String
.Empty
;
262 StringBuilder tempBuffer
= new StringBuilder();
266 foreach(object arg
in args
)
268 if (comma
) tempBuffer
.Append(',');
270 tempBuffer
.Append(arg
);
272 if (!comma
) comma
= true;
275 return tempBuffer
.ToString();
279 /// Replaces the tail by period.
281 public void ReplaceTailByPeriod()
283 int len
= Lines
.Length
;
293 /// Removes the tail.
295 public void RemoveTail()
297 int len
= Lines
.Length
;
301 if (Lines
[len
- 3] == ';')
303 Lines
.Length
= len
- 3;
309 /// Generates the final js code.
311 /// <returns></returns>
312 public string GenerateFinalJsCode()
319 "alert('JS error ' + e.toString());\n" +
324 /// Returns a <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
327 /// A <see cref="T:System.String"/> that represents the current <see cref="T:System.Object"/>.
329 public override string ToString()
331 return GenerateFinalJsCode();
334 #region Internal and private methods
337 /// Escapes the content with C style escapes
339 /// <param name="content">The content.</param>
340 /// <returns></returns>
341 protected static string JsEscape(string content
)
343 // This means: replace all variations of line breaks by a \n
344 content
= Regex
.Replace(content
, "(\r\n)|(\r)|(\n)", "\\n", RegexOptions
.Multiline
);
345 // This is a lookbehind. It means: replace all " -- that are not preceded by \ -- by \"
346 content
= Regex
.Replace(content
, "(?<!\\\\)\"", "\\\"", RegexOptions
.Multiline
);
351 /// Escapes the content with C style escapes (favoring single quotes)
353 /// <param name="content">The content.</param>
354 /// <returns></returns>
355 protected static string JsEscapeWithSQuotes(string content
)
357 // This replaces all ' references by \'
358 return Regex
.Replace(JsEscape(content
), "(\')", "\\'", RegexOptions
.Multiline
);