View Javadoc
1   // Copyright (C) 2010-2016 Yozons, Inc.
2   // CKEditor for Vaadin - Widget linkage for using CKEditor within a Vaadin application.
3   //
4   // This software is released under the Apache License 2.0 <http://www.apache.org/licenses/LICENSE-2.0.html>
5   //
6   package org.vaadin.openesignforms.ckeditor;
7   
8   import java.util.HashMap;
9   import java.util.HashSet;
10  import java.util.LinkedList;
11  import java.util.List;
12  import java.util.ListIterator;
13  import java.util.Set;
14  
15  /**
16   * Configuration utility for using the CKEditorTextField.  You can use this "config javascript builder" object for our
17   * tested/common options, or just set the options using a JavaScript/JSON string as you prefer.
18   */
19  public class CKEditorConfig implements java.io.Serializable {
20  	private static final long serialVersionUID = 5437536212215099564L;
21  
22  	// If this is set, we'll just use it and ignore everything else.
23  	protected String inPageConfig;
24  	
25  	// Hack to allow a caller to add any line to the inPageConfig. Of course, if they specify it wrong or overwrite our API standard ones
26  	// that will just be their own issue. This is not recommended and we prefer that if you have an option to set, let us know and
27  	// we'll get it added to the API. We know most CKEDITOR.config options are never set by regular users, but those that are, we'd like
28  	// to document them as part of the API.
29  	protected LinkedList<String> extraConfigLines = null;
30  
31  	// Otherwise, we'll build the config based on settings contained here
32  	protected HashMap<String,String> writerRules = null;
33  	protected String writerIndentationChars = null;
34  	protected HashMap<Integer,String> keystrokeMappings = null;
35  	protected LinkedList<String> extraPlugins = null;
36  	protected LinkedList<String> removePlugins = null;
37  	protected LinkedList<String> customToolbarLines = null;
38  	protected Boolean toolbarCanCollapse = null;
39  	protected Boolean disableNativeSpellChecker = null;
40  	protected Boolean resizeEnabled = null;
41  	protected String resizeDir = null;
42  	protected Integer resizeMinWidth = null;
43  	protected Integer resizeMaxWidth = null;
44  	protected Integer resizeMinHeight = null;
45  	protected Integer resizeMaxHeight = null;
46  	protected String width = null;
47  	protected String height = null;
48  	protected Integer baseFloatZIndex = null;
49  	protected Integer tabSpaces = null;
50  	protected Boolean pasteFromWordRemoveFontStyles = null;  // Deprecated soon after the 4.6.0 release on 11/17/2016
51  	protected Boolean pasteFromWordPromptCleanup = null;
52  	protected String startupMode = null; // either "source" or "wysiwyg" (defaults to wysiwyg, so generally only used if you'd like to startup in source mode)
53  	protected Boolean startupFocus = null;
54  	protected Boolean readOnly = null; // for startup readOnly setting
55  	protected String[] contentsCssFiles = null;
56  	protected String fontNames = null;
57  	protected String stylesSet = null;
58  	protected String bodyClass = null;
59  	protected String skin = null;
60  	protected Boolean toolbarStartupExpanded = null;
61  	protected LinkedList<String> templates_files = null;
62  	protected Boolean templates_replaceContent = null;
63  
64  	protected String allowedContent = null; // Advanced content filtering added in CKEditor 4.1
65  	protected String extraAllowedContent = null;
66  	
67  	protected String filebrowserBrowseUrl = null;
68  	protected String filebrowserUploadUrl = null;
69  	protected String filebrowserWindowWidth = null; // defaults to 80% width
70  	protected String filebrowserWindowHeight = null; // defaults to 70% height
71  	
72  	protected String filebrowserImageBrowseUrl = null;
73  	protected String filebrowserImageUploadUrl = null;
74  	protected String filebrowserImageWindowWidth = null; // defaults to 80% width
75  	protected String filebrowserImageWindowHeight = null; // defaults to 70% height
76  	protected String filebrowserImageBrowseLinkUrl = null;
77  	
78  	protected String filebrowserFlashBrowseUrl = null;
79  	protected String filebrowserFlashUploadUrl = null;
80  	protected String filebrowserFlashBrowseLinkUrl = null; // available? Not in CKEditor JS docs
81  
82  	protected String filebrowserLinkBrowseUrl = null; // available? Not in CKEditor JS docs
83  	
84  	protected Integer enterMode = null;
85  	protected Integer shiftEnterMode = null;
86  	protected Boolean forceEnterMode = null;
87  	protected Boolean forcePasteAsPlainText = null;
88  	
89  	protected Boolean fullPage = null;
90  	
91  	protected String language = null;
92  	
93  	// Sent separately since cannot make it work with inPageConfig
94  	protected LinkedList<String> protectedSource = null;
95  
96  	
97  	public CKEditorConfig() {
98  	}
99  	
100 	public String getInPageConfig() {
101 		if ( inPageConfig != null ) {
102 			return inPageConfig;
103 		}
104 		
105 		// Build the JSON config
106 		StringBuilder config = new StringBuilder(4096);
107 		config.append("{ "); // we assume this is 2 chars in the buffer append routines to know whether anything more has been appended yet
108 		
109 		if ( customToolbarLines != null ) {
110 			
111 			StringBuilder buf = new StringBuilder();
112 			ListIterator<String> iter = customToolbarLines.listIterator();
113 			while( iter.hasNext() ) {
114 				if ( buf.length() > 0 )
115 					buf.append(",'/',");
116 				String js = iter.next();
117 				buf.append(js);
118 			}
119 
120 			appendJSONConfig(config, "toolbar : 'Custom'");
121 			appendJSONConfig(config, "toolbar_Custom : [" + buf.toString() + "]");
122 		}
123 		
124 		if ( toolbarCanCollapse != null ) {
125 			appendJSONConfig(config, "toolbarCanCollapse : " + toolbarCanCollapse);
126 		}
127 		
128 		if ( resizeEnabled != null ) {
129 			appendJSONConfig(config, "resize_enabled : " + resizeEnabled);
130 		}
131 
132 		if ( resizeDir != null ) {
133 			appendJSONConfig(config, "resize_dir : '" + resizeDir + "'");
134 		}
135 		
136 		if ( resizeMinWidth != null ) {
137 			appendJSONConfig(config, "resize_minWidth : " + resizeMinWidth);
138 		}	
139 		if ( resizeMaxWidth != null ) {
140 			appendJSONConfig(config, "resize_maxWidth : " + resizeMaxWidth);
141 		}
142 		if ( resizeMinHeight != null ) {
143 			appendJSONConfig(config, "resize_minHeight : " + resizeMinHeight);
144 		}
145 		if ( resizeMaxHeight != null ) {
146 			appendJSONConfig(config, "resize_maxHeight : " + resizeMaxHeight);
147 		}
148 		
149 		if ( extraPlugins != null ) {
150 			StringBuilder buf = new StringBuilder();
151 			ListIterator<String> iter = extraPlugins.listIterator();
152 			while( iter.hasNext() ) {
153 				if ( buf.length() > 0 )
154 					buf.append(",");
155 				buf.append(iter.next());
156 			}
157 			
158 			appendJSONConfig(config, "extraPlugins : '" + buf.toString() + "'");
159 		}
160 
161 		if ( removePlugins != null ) {
162 			StringBuilder buf = new StringBuilder();
163 			ListIterator<String> iter = removePlugins.listIterator();
164 			while( iter.hasNext() ) {
165 				if ( buf.length() > 0 )
166 					buf.append(",");
167 				buf.append(iter.next());
168 			}
169 			
170 			appendJSONConfig(config, "removePlugins : '" + buf.toString() + "'");
171 		}
172 		
173 		if ( width != null ) {
174 			appendJSONConfig(config, "width : '" + width + "'");
175 		}
176 		
177 		if ( height != null ) {
178 			appendJSONConfig(config, "height : '" + height + "'");
179 		}
180 				
181 		if ( baseFloatZIndex != null ) {
182 			appendJSONConfig(config, "baseFloatZIndex : " + baseFloatZIndex);
183 		}
184 		
185 		if ( tabSpaces != null ) {
186 			appendJSONConfig(config, "tabSpaces : " + tabSpaces);
187 		}
188 		
189 		if ( pasteFromWordRemoveFontStyles != null ) {
190 			appendJSONConfig(config, "pasteFromWordRemoveFontStyles : " + pasteFromWordRemoveFontStyles);
191 		}
192 		
193 		if ( pasteFromWordPromptCleanup != null ) {
194 			appendJSONConfig(config, "pasteFromWordPromptCleanup : " + pasteFromWordPromptCleanup);
195 		}
196 		
197 		if ( startupMode != null ) {
198 			appendJSONConfig(config, "startupMode : '" + startupMode + "'");
199 		}
200 
201 		if ( startupFocus != null ) {
202 			appendJSONConfig(config, "startupFocus : " + startupFocus);
203 		}
204 
205 		if ( readOnly != null ) {
206 			appendJSONConfig(config, "readOnly : " + readOnly);
207 		}
208 		
209 		if ( skin != null ) {
210 			appendJSONConfig(config, "skin : '" + skin + "'");
211 		}
212                 
213 		if ( toolbarStartupExpanded != null ) {
214 			appendJSONConfig(config, "toolbarStartupExpanded : " + toolbarStartupExpanded );
215 		}
216                 
217 		if ( contentsCssFiles != null && contentsCssFiles.length > 0 ) {
218 			if ( contentsCssFiles.length == 1 ) {
219 				appendJSONConfig(config, "contentsCss : '" + contentsCssFiles[0] + "'");
220 			} else {
221 				StringBuilder buf = new StringBuilder();
222 				for( String file : contentsCssFiles ) {
223 					if ( buf.length() > 0 )
224 						buf.append(",");
225 					buf.append("'").append(file).append("'");
226 				}
227 				appendJSONConfig(config, "contentsCss : [" + buf.toString() + "]");
228 			}
229 		}
230 		
231 		if ( bodyClass != null ) {
232 			appendJSONConfig(config, "bodyClass : '" + bodyClass + "'");
233 		}
234 		
235 		if ( disableNativeSpellChecker != null ) {
236 			appendJSONConfig(config, "disableNativeSpellChecker : " + disableNativeSpellChecker);
237 		}
238 		
239 		if ( fontNames != null ) {
240 			appendJSONConfig(config, "font_names : '" + fontNames + "'");
241 		}
242 		
243 		if ( stylesSet != null ) {
244 			appendJSONConfig(config, "stylesSet : '" + stylesSet + "'");
245 		}
246 		
247 		if ( filebrowserBrowseUrl != null ) {
248 			appendJSONConfig(config, "filebrowserBrowseUrl : '" + filebrowserBrowseUrl + "'");
249 		}	
250 		if ( filebrowserUploadUrl != null ) {
251 			appendJSONConfig(config, "filebrowserUploadUrl : '" + filebrowserUploadUrl + "'");
252 		}	
253 		if ( filebrowserWindowWidth != null ) {
254 			appendJSONConfig(config, "filebrowserWindowWidth : '" + filebrowserWindowWidth + "'");
255 		}
256 		if ( filebrowserWindowHeight != null ) {
257 			appendJSONConfig(config, "filebrowserWindowHeight : '" + filebrowserWindowHeight + "'");
258 		}
259 		if ( filebrowserLinkBrowseUrl != null ) {
260 			appendJSONConfig(config, "filebrowserLinkBrowseUrl : '" + filebrowserLinkBrowseUrl + "'");
261 		}
262 		
263 		if ( filebrowserImageBrowseUrl != null ) {
264 			appendJSONConfig(config, "filebrowserImageBrowseUrl : '" + filebrowserImageBrowseUrl + "'");
265 		}
266 		if ( filebrowserImageUploadUrl != null ) {
267 			appendJSONConfig(config, "filebrowserImageUploadUrl : '" + filebrowserImageUploadUrl + "'");
268 		}
269 		if ( filebrowserImageWindowWidth != null ) {
270 			appendJSONConfig(config, "filebrowserImageWindowWidth : '" + filebrowserImageWindowWidth + "'");
271 		}
272 		if ( filebrowserImageWindowHeight != null ) {
273 			appendJSONConfig(config, "filebrowserImageWindowHeight : '" + filebrowserImageWindowHeight + "'");
274 		}
275 		if ( filebrowserImageBrowseLinkUrl != null ) {
276 			appendJSONConfig(config, "filebrowserImageBrowseLinkUrl : '" + filebrowserImageBrowseLinkUrl + "'");
277 		}
278 		
279 		if ( filebrowserFlashBrowseUrl != null ) {
280 			appendJSONConfig(config, "filebrowserFlashBrowseUrl : '" + filebrowserFlashBrowseUrl + "'");
281 		}
282 		if ( filebrowserFlashUploadUrl != null ) {
283 			appendJSONConfig(config, "filebrowserFlashUploadUrl : '" + filebrowserFlashUploadUrl + "'");
284 		}
285 		if ( filebrowserFlashBrowseLinkUrl != null ) {
286 			appendJSONConfig(config, "filebrowserFlashBrowseLinkUrl : '" + filebrowserFlashBrowseLinkUrl + "'");
287 		}
288 
289 		if ( templates_files != null ) {
290 			StringBuilder buf = new StringBuilder();
291 			ListIterator<String> iter = templates_files.listIterator();
292 			while( iter.hasNext() ) {
293 				if ( buf.length() > 0 )
294 					buf.append("','");
295 				buf.append(iter.next());
296 			}
297 			appendJSONConfig(config, "templates_files : ['" + buf.toString() + "']");
298 		}
299 		
300 		if (templates_replaceContent != null) {
301 			appendJSONConfig(config, "templates_replaceContent : "+ templates_replaceContent);
302 		}
303 		
304 		if (enterMode != null) {
305 			appendJSONConfig(config, "enterMode : " + enterMode);
306 		}
307 		
308 		if (shiftEnterMode != null) {
309 			appendJSONConfig(config, "shiftEnterMode : " + shiftEnterMode);
310 		}
311 		
312 		if (forceEnterMode != null) {
313 			appendJSONConfig(config, "forceEnterMode : " + forceEnterMode);
314 		}
315 
316 		if (forcePasteAsPlainText != null) {
317 			appendJSONConfig(config, "forcePasteAsPlainText : " + forcePasteAsPlainText);
318 		}
319 		
320 		if (language != null) {
321 			appendJSONConfig(config, "language : '" + language.toLowerCase() + "'");
322 		}
323 		
324 		if (fullPage != null) {
325 			appendJSONConfig(config, "fullPage : " + fullPage);
326 		}
327 		
328 		// These are the hack extra options for setting options we don't support in our API
329 		if ( extraConfigLines != null ) {
330 			for( String extra : extraConfigLines ) {
331 				appendJSONConfig(config, extra);
332 			}
333 		}
334 
335 		// Advanced content filtering was added in CKEditor 4.1
336 		if ( allowedContent != null ) {
337 			if ( "true".equals(allowedContent) ) 
338 				appendJSONConfig(config, "allowedContent : true"); // basically allows all content and disables ACF
339 			else
340 				appendJSONConfig(config, "allowedContent : '" + allowedContent + "'");
341 		}
342 
343 		if ( extraAllowedContent != null ) {
344 			appendJSONConfig(config, "extraAllowedContent : '" + extraAllowedContent + "'");
345 		}
346 		
347 		config.append(" }");
348 		return config.toString();
349 	}
350 	
351 	protected StringBuilder appendJSONConfig(StringBuilder configBuf, String oneOptions) {
352 		if ( configBuf.length() > 2 )
353 			configBuf.append(", ");
354 		configBuf.append(oneOptions);
355 		return configBuf;
356 	}
357 	
358 	/**
359 	 * You can use this to just set the JavaScript/JSON notation for setting the 'in page config' option when the editor is
360 	 * created.  If you don't use this, you can use all of the other builder routines that set options that are then used
361 	 * to generate the JSON notation to use
362 	 * @param js the String JSON 'config' for the new editor instance.
363 	 */
364 	public void setInPageConfig(String js) {
365 		inPageConfig = js;
366 	}
367 	
368 	/**
369 	 * Allows you to add any CKEDITOR.config option line to the in page configuration. This is considered a hack, but may be
370 	 * necessary if you are setting an option not supported by the API of this class. We recommend that you request the API
371 	 * be changed when you find you need to use this, but it'll keep you going while you await the API-based scheme.
372 	 * The resulting will be to add a JSON line to the in-page config like: <code>extraConfigName : extraConfigValue<code>
373 	 * @param extraConfigName the String CKEDITOR.config name to set
374 	 * @param extraConfigValue the String value
375 	 */
376 	public void addExtraConfig(String extraConfigName, String extraConfigValue) {
377 		if ( extraConfigLines == null )
378 			extraConfigLines = new LinkedList<String>();
379 		extraConfigLines.add(extraConfigName + " : " + extraConfigValue);
380 	}
381 	
382 	public boolean hasWriterRules() {
383 		return writerRules != null && ! writerRules.isEmpty();
384 	}
385 	public Set<String> getWriterRulesTagNames() {
386 		return writerRules == null ? new HashSet<String>() : writerRules.keySet();
387 	}
388 	public String getWriterRuleByTagName(String tagName) {
389 		return writerRules == null ? null : writerRules.get(tagName);
390 	}
391 	public synchronized void addWriterRules(String tagName, String jsRule) {
392 		if ( writerRules == null ) {
393 			writerRules = new HashMap<String,String>();
394 		}
395 		writerRules.put( tagName, jsRule );
396 	}
397 	
398 	public boolean hasKeystrokeMappings() {
399 		return keystrokeMappings != null && ! keystrokeMappings.isEmpty();
400 	}
401 	public Set<Integer> getKeystrokes() {
402 		return keystrokeMappings == null ? new HashSet<Integer>() : keystrokeMappings.keySet();
403 	}
404 	public String getKeystrokeCommandByKeystroke(Integer keystroke) {
405 		return keystrokeMappings == null ? null : keystrokeMappings.get(keystroke);
406 	}
407 	public static int CKEDITOR_KEYSTROKE_ALT = 0x440000;
408 	public static int CKEDITOR_KEYSTROKE_CTRL = 0x110000;
409 	public static int CKEDITOR_KEYSTROKE_SHIFT = 0x220000;
410 	public synchronized void addKeystrokeMapping(int keystroke, String command) {
411 		if ( keystrokeMappings == null ) {
412 			keystrokeMappings = new HashMap<Integer,String>();
413 		}
414 		keystrokeMappings.put( keystroke, command );
415 	}
416 	/**
417 	 * Enabled the vaadinsave plugin and sets the keystroke mapping for CTRL-S to trigger it.
418 	 * @see #enableVaadinSavePlugin()
419 	 * @see #addKeystrokeMapping
420 	 */
421 	public void enableCtrlSWithVaadinSavePlugin() {
422 		enableVaadinSavePlugin(); // You must have the vaadin save plugin to use this keystroke mapping
423 		addKeystrokeMapping(CKEDITOR_KEYSTROKE_CTRL + 83, "vaadinsave");  // CTRL-S triggers vaadinsave
424 	}
425 
426 	// A convenience method to set a bunch of compact HTML rules.
427 	public void useCompactTags() {
428 		addWriterRules("p",  "{indent : false, breakBeforeOpen : true, breakAfterOpen : false, breakBeforeClose : false, breakAfterClose : true}" );
429 		addWriterRules("h1", "{indent : false, breakBeforeOpen : true, breakAfterOpen : false, breakBeforeClose : false, breakAfterClose : true}" );
430 		addWriterRules("h2", "{indent : false, breakBeforeOpen : true, breakAfterOpen : false, breakBeforeClose : false, breakAfterClose : true}" );
431 		addWriterRules("h3", "{indent : false, breakBeforeOpen : true, breakAfterOpen : false, breakBeforeClose : false, breakAfterClose : true}" );
432 		addWriterRules("h4", "{indent : false, breakBeforeOpen : true, breakAfterOpen : false, breakBeforeClose : false, breakAfterClose : true}" );
433 		addWriterRules("h5", "{indent : false, breakBeforeOpen : true, breakAfterOpen : false, breakBeforeClose : false, breakAfterClose : true}" );
434 		addWriterRules("h6", "{indent : false, breakBeforeOpen : true, breakAfterOpen : false, breakBeforeClose : false, breakAfterClose : true}" );
435 		addWriterRules("li", "{indent : true,  breakBeforeOpen : true, breakAfterOpen : false, breakBeforeClose : false, breakAfterClose : true}" );
436 	}
437 	
438 	public String getWriterIndentationChars() {
439 		return writerIndentationChars;
440 	}
441 	public boolean hasWriterIndentationChars() {
442 		return writerIndentationChars != null;
443 	}
444 	public void setWriterIndentationChars(String v) {
445 		writerIndentationChars = v;
446 	}
447 
448 	public synchronized void addToExtraPlugins(String pluginName) {
449 		if ( extraPlugins == null ) {
450 			extraPlugins = new LinkedList<String>();
451 		}
452 		if ( ! extraPlugins.contains(pluginName) ) {
453 			extraPlugins.add(pluginName);
454 		}
455 		// If for whatever reason this plugin name is in the remove plugins, we remove it.
456 		if ( removePlugins != null && removePlugins.contains(pluginName) ) {
457 			removePlugins.remove(pluginName);
458 		}
459 	}
460 	
461 	/**
462 	 * This enables the vaadinsave plugin.  You will also need a custom toolbar with the entry 'VaadinSave' included to put it 
463 	 * the specified position. If you'd also like CTRL-S to trigger, call enableCtrlSWithVaadinSavePlugin() instead.
464 	 * @see #enableCtrlSWithVaadinSavePlugin()
465 	 */
466 	public void enableVaadinSavePlugin() {
467 		addToExtraPlugins("vaadinsave");
468 	}
469 
470 	
471 	/**
472 	 * Convenience method for the Open eSignForms project sponsors to set the plugins and configuration in a common way needed.
473 	 */
474 	public void setupForOpenESignForms(String contextPath, String ckeditorContextIdInSession, String bodyCssClass, String... extraCssFiles) {
475 		addCustomToolbarLine("{ items: ['Styles','Format','Bold','Italic','Underline','Strike','Subscript','Superscript','-','RemoveFormat'] }," +
476 							 "{ items: ['TextColor','BGColor'] }," +
477 	                         "{ items: ['Font','FontSize'] }," +
478 	             			 "{ items: ['JustifyLeft','JustifyCenter','JustifyRight','JustifyBlock'] }"); // all wrong icons
479 		addCustomToolbarLine("{ items: ['Cut','Copy','Paste','PasteText','PasteFromWord'] }," + // paste word wrong
480 				 			 "{ items: ['Find','Replace'] }," + 
481 				 			 "{ items: ['Undo','Redo'] }," +
482 				 			 "{ items: ['NumberedList','BulletedList'] }," + 
483 				 			 "{ items: ['Outdent','Indent','CreateDiv'] }," + 
484 				 			 "{ items: ['Table','HorizontalRule','PageBreak','SpecialChar'] }," +
485 				 			 "{ items: ['Image','Link','Unlink'] }," +
486 				 			 "{ items: ['Source','ShowBlocks'] }");
487 
488 		setHeight("300px");
489 		setBaseFloatZIndex(20000);
490 		setTabSpaces(4);
491 		
492 		disableSpellChecker();
493 		setDisableNativeSpellChecker(false);
494 		disableResizeEditor();
495 		//setPasteFromWordRemoveFontStyles(false); -- sadly, we want font color to be kept, but having this creates too much HTML mess
496 		setPasteFromWordPromptCleanup(true);
497 		
498 		useCompactTags();
499 		addWriterRules("script", "{indent : false, breakBeforeOpen : true, breakAfterOpen : true, breakBeforeClose : true, breakAfterClose : true}" );
500 		addWriterRules("style",  "{indent : false, breakBeforeOpen : true, breakAfterOpen : true, breakBeforeClose : true, breakAfterClose : true}" );
501 		setWriterIndentationChars("    ");
502 		
503 		enableCtrlSWithVaadinSavePlugin();
504 
505 		addFontName("Calibri/Calibri, Arial, Helvetica, sans-serif");
506 		
507 		setStylesSet("esfStyleSet:" + contextPath + "/static/esf/esfStyleSet.js");
508 		if ( extraCssFiles == null )
509 			setContentsCss(contextPath + "/static/esf/esf.css");
510 		else
511 		{
512 			String[] cssFiles = new String[extraCssFiles.length+1];
513 			cssFiles[0] = contextPath + "/static/esf/esf.css";
514 			for(int i=0; i < extraCssFiles.length; ++i )
515 				cssFiles[i+1] = contextPath + extraCssFiles[i];
516 			setContentsCss(cssFiles);
517 		}
518 		if ( bodyCssClass == null || "".equals(bodyCssClass) )
519 			bodyCssClass = "esf";
520 		else
521 			bodyCssClass = "esf " + bodyCssClass;
522 		setBodyClass(bodyCssClass);
523 		setFilebrowserBrowseUrl(contextPath + "/ckeditorFileBrowser.jsp?ccid="+ckeditorContextIdInSession);
524 		setFilebrowserWindowWidth("500"); // 4/27/2012 believe there's a bug for any value less than 640 being ignored
525 		setFilebrowserWindowHeight("500");
526 		setFilebrowserImageBrowseUrl(contextPath + "/ckeditorImageBrowser.jsp?ccid="+ckeditorContextIdInSession);
527 		setFilebrowserImageWindowWidth("600");
528 		setFilebrowserImageWindowHeight("500");
529 		setAllowedContentAll(); // turn off ACF introduced in CKEditor 4.1 since people can enter whatever they want via the editor SOURCE mode.
530 		addProtectedSource("/<%.*%>/g"); // allow JSP code like <%=(new java.util.Date())%> or <% any java code you want %>
531 		addProtectedSource("/<[a-z]*:.*\\/>/g"); // allow JSP tags like <esf:out value="Test"/>
532 	}
533 	
534 	public synchronized void addToRemovePlugins(String pluginName) {
535 		if ( removePlugins == null ) {
536 			removePlugins = new LinkedList<String>();
537 		}
538 		if ( ! removePlugins.contains(pluginName) ) {
539 			removePlugins.add(pluginName);
540 		}
541 		// If for whatever reason this plugin is defined in the extraPlugins list, we remove it.
542 		if ( extraPlugins != null && extraPlugins.contains(pluginName) ) {
543 			extraPlugins.remove(pluginName);
544 		}
545 	}
546 	
547 	public void disableElementsPath() {
548 		addToRemovePlugins("elementspath"); 
549 	}
550 
551 	public void disableResizeEditor() {
552 		addToRemovePlugins("resize"); 
553 	}
554 	
555 	public void disableSpellChecker() {
556 		addToRemovePlugins("scayt"); 
557 	}
558 	
559 	public void setDisableNativeSpellChecker(boolean v) {
560 		disableNativeSpellChecker = v;
561 	}
562 
563 
564 	
565 	/**
566 	 * If no custom toolbar is defined, it will use the Full toolbar by default (config.toolbar = 'Full').
567 	 * The format of the toolbar configuration changed in CKEditor 3.6 (our library 1.3).
568 	 * 
569 	 * Note that each line is generally one 'band' so that they all appear together.  
570 	 * For example:
571 	 * { name: 'styles', items : ['Styles','Format','Font','FontSize','TextColor','BGColor','Maximize', 'ShowBlocks','-','About'] }
572 	 * @param toolbarLineJS
573 	 */
574 	public synchronized void addCustomToolbarLine(String toolbarLineJS) {
575 		if ( customToolbarLines == null ) {
576 			customToolbarLines = new LinkedList<String>();
577 		}
578 		if ( ! customToolbarLines.contains(toolbarLineJS) ) {
579 			customToolbarLines.add(toolbarLineJS);
580 		}
581 	}
582 	
583 	public void setToolbarCanCollapse(boolean v) {
584 		toolbarCanCollapse = v;
585 	}
586 
587     /**
588      * Possible skins:
589      * moono The default skin for CKEditor 4.x
590      * kama The default skin for CKEditor 3.x
591      * office2003
592      * v2 
593      * 
594      * @param newSkin the skin to set
595      */
596     public void setSkin(String newSkin) 
597     {
598         skin = newSkin;
599     }
600 
601     /**
602      * @param newToolbarStartupExpanded the toolbarStartupExpanded status
603      */
604     public void setToolbarStartupExpanded(Boolean newToolbarStartupExpanded) 
605     {
606         toolbarStartupExpanded = newToolbarStartupExpanded;
607     }
608 
609 	public void setResizeEnabled(boolean v) {
610 		resizeEnabled = v;
611 	}
612 
613 	public enum RESIZE_DIR { BOTH, VERTICAL, HORIZONTAL }
614 	
615 	public void setResizeDir(RESIZE_DIR dir) {
616 		if ( dir.equals(RESIZE_DIR.BOTH) ) {
617 			resizeDir = "both";
618 		} else if ( dir.equals(RESIZE_DIR.VERTICAL) ) {
619 			resizeDir = "vertical";
620 		} else if ( dir.equals(RESIZE_DIR.HORIZONTAL) ) {
621 			resizeDir = "horizontal";
622 		} else {
623 			resizeDir = null; // won't set it since it's not what we expect
624 		}
625 	}
626 
627 	public void setResizeMinWidth(int pixels) {
628 		resizeMinWidth = pixels;
629 	}
630 
631 	public void setResizeMaxWidth(int pixels) {
632 		resizeMaxWidth = pixels;
633 	}
634 
635 	public void setResizeMinHeight(int pixels) {
636 		resizeMinHeight = pixels;
637 	}
638 
639 	public void setResizeMaxHeight(int pixels) {
640 		resizeMaxHeight = pixels;
641 	}
642 
643 	public void setWidth(String cssSize) {
644 		width = cssSize;
645 	}
646 
647 	public void setHeight(String cssSize) {
648 		height = cssSize;
649 	}
650 	
651 	public void setBaseFloatZIndex(int zIndex) {
652 		baseFloatZIndex = zIndex;
653 	}
654 	
655 	public void setTabSpaces(int numSpaces) {
656 		tabSpaces = numSpaces;
657 	}
658 	
659 	public void setPasteFromWordRemoveFontStyles(boolean v)
660 	{
661 		pasteFromWordRemoveFontStyles = v;
662 	}
663 
664 	public void setPasteFromWordPromptCleanup(boolean v)
665 	{
666 		pasteFromWordPromptCleanup = v;
667 	}
668 	
669 	public void setStartupModeSource()
670 	{
671 		startupMode = "source";
672 	}
673 	public void setStartupModeWysiwyg()
674 	{
675 		startupMode = "wysiwyg";
676 	}
677 	
678 	public void setStartupFocus(boolean v) {
679 		startupFocus = v;
680 	}
681 
682 	public  boolean isReadOnly() {
683 		return readOnly != null && readOnly.booleanValue();
684 	}
685 	public void setReadOnly(boolean v) {
686 		readOnly = v;
687 	}
688 
689 	/**
690 	 * Sets the file or list of files for the contents to be applied to the editor.  Used to set the styles that will
691 	 * be used in the page where the HTML will actually be rendered.
692 	 * @param cssFiles zero or more String file URL paths -- for same system, starting with context path is recommended: /myapp/path/to/cssfile.css
693 	 */
694 	public void setContentsCss(String... cssFiles) {
695 		contentsCssFiles = cssFiles;
696 	}
697 	
698 
699 	/**
700 	 * Sets the body class for the HTML editor, so if you render the results in a body with a given class, you can give it here
701 	 * too so that the editor will show the same styles you may have in your contents css.
702 	 * @param bc
703 	 */
704 	public void setBodyClass(String bc) {
705 		bodyClass = bc;
706 	}
707 	
708 	/**
709 	 * Sets the font_names config option, which is the registered style name 
710 	 * @param fontNamesSpec the new font names to use.
711 	 * @see #CKEDITOR_DEFAULT_FONT_NAMES for a string that shows the built-in fonts.
712 	 * @see #getCkeditorDefaultFontNameList() to get a list of the fonts so you can insert easily if you want and then set with #setFontNames(List<String> fontNamesSpec) or addFontName(String newFontName)
713 	 * 
714 	 */
715 	public static final String CKEDITOR_DEFAULT_FONT_NAMES = /* as of CKEditor 3.6.2 */
716 		"Arial/Arial, Helvetica, sans-serif;Comic Sans MS/Comic Sans MS, cursive;Courier New/Courier New, Courier, monospace;Georgia/Georgia, serif;Lucida Sans Unicode/Lucida Sans Unicode, Lucida Grande, sans-serif;Tahoma/Tahoma, Geneva, sans-serif;Times New Roman/Times New Roman, Times, serif;Trebuchet MS/Trebuchet MS, Helvetica, sans-serif;Verdana/Verdana, Geneva, sans-serif";
717 	public List<String> getCkeditorDefaultFontNameList()
718 	{
719 		LinkedList<String> list = new LinkedList<String>();
720 		for( String fontName : CKEDITOR_DEFAULT_FONT_NAMES.split(";") )
721 			list.add(fontName);
722 		return list;
723 	}
724 	public List<String> getFontNameList()
725 	{
726 		if ( fontNames == null )
727 			return getCkeditorDefaultFontNameList();
728 		LinkedList<String> list = new LinkedList<String>();
729 		for( String fontName : fontNames.split(";") )
730 			list.add(fontName);
731 		return list;
732 	}
733 	public void setFontNames(List<String> fontNamesSpec) {
734 		if ( fontNamesSpec != null && fontNamesSpec.size() > 0 ) {
735 			StringBuilder buf = new StringBuilder( (fontNamesSpec.size()+1) * 80);
736 			for( String spec : fontNamesSpec ) {
737 				if ( buf.length() > 0 ) {
738 					buf.append(';');
739 				}
740 				buf.append(spec);
741 			}
742 			setFontNames(buf.toString());
743 		}
744 	}
745 	public void addFontName(String newFontNameSpec) {
746 		LinkedList<String> newFontNames = new LinkedList<String>();
747 		boolean fontNeedsInsert = true;
748 		for( String fontName : getFontNameList() ) {
749 			if ( fontName.compareTo(newFontNameSpec) > 0 && fontNeedsInsert ) {
750 				newFontNames.add(newFontNameSpec);
751 				fontNeedsInsert = false;
752 			}
753 			newFontNames.add(fontName);
754 		}
755 		setFontNames(newFontNames);
756 	}
757 	public void setFontNames(String fontNamesSpec) {
758 		fontNames = fontNamesSpec;
759 	}
760 	
761 	/**
762 	 * Sets the stylesSet config option, which is the registered style name 
763 	 * @param styleSetSpec
764 	 */
765 	public void setStylesSet(String styleSetSpec) {
766 		stylesSet = styleSetSpec;
767 	}
768 	
769 	/**
770 	 * Sets the filebrowserBrowseUrl config option, which is an URL that will list files a user can select from
771 	 * @param url
772 	 */
773 	public void setFilebrowserBrowseUrl(String url) {
774 		filebrowserBrowseUrl = url;
775 	}
776 	
777 	/**
778 	 * Sets the filebrowserUploadUrl config option, which is an URL that will allow a file to be uploaded
779 	 * @param url
780 	 */
781 	public void setFilebrowserUploadUrl(String url) {
782 		filebrowserUploadUrl = url;
783 	}
784 	
785 	/**
786 	 * Sets the filebrowserWindowWidth config option, which is a width size spec (like "600" for 600 pixels); CKEditor defaults to 80%
787 	 * @param url
788 	 */
789 	public void setFilebrowserWindowWidth(String size) {
790 		filebrowserWindowWidth = size;
791 	}
792 	
793 	/**
794 	 * Sets the filebrowserWindowHeight config option, which is a height size spec (like "600" for 600 pixels); CKEditor defaults to 70%
795 	 * @param url
796 	 */
797 	public void setFilebrowserWindowHeight(String size) {
798 		filebrowserWindowHeight = size;
799 	}
800 	
801 	/**
802 	 * Sets the filebrowserImageBrowseUrl config option, which is an URL that will list images a user can select from
803 	 * @param url
804 	 */
805 	public void setFilebrowserImageBrowseUrl(String url) {
806 		filebrowserImageBrowseUrl = url;
807 	}
808 	
809 	/**
810 	 * Sets the filebrowserImageUploadUrl config option, which is an URL that will allow an image file to be uploaded
811 	 * @param url
812 	 */
813 	public void setFilebrowserImageUploadUrl(String url) {
814 		filebrowserImageUploadUrl = url;
815 	}
816 	
817 	/**
818 	 * Sets the filebrowserImageWindowWidth config option, which is a width size spec (like "600" for 600 pixels); CKEditor defaults to 80%
819 	 * @param url
820 	 */
821 	public void setFilebrowserImageWindowWidth(String size) {
822 		filebrowserImageWindowWidth = size;
823 	}
824 	
825 	/**
826 	 * Sets the filebrowserImageWindowHeight config option, which is a height size spec (like "600" for 600 pixels); CKEditor defaults to 70%
827 	 * @param url
828 	 */
829 	public void setFilebrowserImageWindowHeight(String size) {
830 		filebrowserImageWindowHeight = size;
831 	}
832 	
833 	
834 	/**
835 	 * Sets the filebrowserFlashBrowseUrl config option, which is an URL that will allow browsing for Flash
836 	 * @param url
837 	 */
838 	public void setFilebrowserFlashBrowseUrl(String url) {
839 		filebrowserFlashBrowseUrl = url;
840 	}
841 	
842 	/**
843 	 * Sets the filebrowserFlashUploadUrl config option, which is an URL that will allow a Flash file to be uploaded
844 	 * @param url
845 	 */
846 	public void setFilebrowserFlashUploadUrl(String url) {
847 		filebrowserFlashUploadUrl = url;
848 	}
849 	
850 	/**
851 	 * Sets the filebrowserLinkBrowseUrl config option, which is an URL that will allow for link browsing
852 	 * @param url
853 	 */
854 	public void setFilebrowserLinkBrowseUrl(String url) {
855 		filebrowserLinkBrowseUrl = url;
856 	}
857 
858 
859     /**
860      * Sets the filebrowserFlashBrowseLinkUrl config option, 
861      * which is an URL that will allow for link browsing
862      * in the Flash property dialog
863      * 
864      * @param url the filebrowserFlashBrowseLinkUrl to set
865      */
866     public void setFilebrowserFlashBrowseLinkUrl(String url) 
867     {
868         filebrowserFlashBrowseLinkUrl = url;
869     }
870 
871     /**
872      * Sets the filebrowserImageBrowseLinkUrl config option, 
873      * which is an URL that will allow for link browsing
874      * in the Image property dialog
875      * 
876      * @param url the filebrowserImageBrowseLinkUrl to set
877      */
878     public void setFilebrowserImageBrowseLinkUrl(String url) 
879     {
880         filebrowserImageBrowseLinkUrl = url;
881     }
882 
883     /**
884      * Add a new template url to the list of templates
885      * 
886      * @param templateURL 
887      */
888     public synchronized void addTemplatesFiles(String templateURL) {
889     	if ( templates_files == null ) {
890     		templates_files = new LinkedList<String>();
891     	}
892     	if ( ! templates_files.contains(templateURL) ) {
893     		templates_files.add(templateURL);
894     	}
895     }
896 
897     /**
898      * Sets 'enterMode' such that "P" will be 1, "BR" will be 2 and "DIV" will be 3. Any other value will set it to 1.
899      * @param enterMode string code
900      * @see #setEnterMode(int) to just set by integer value
901      */
902 	public void setEnterMode(String enterMode) {
903 		if ("BR".equalsIgnoreCase(enterMode)) 
904 			this.enterMode = 2;
905 		else if ("DIV".equalsIgnoreCase(enterMode)) 
906 			this.enterMode = 3;
907 		else
908 			this.enterMode = 1;
909 	}
910 	public void setEnterMode(int enterMode) {
911 		this.enterMode = enterMode;
912 	}
913 
914     /**
915      * Sets 'shiftEnterMode' such that "P" will be 1, "BR" will be 2 and "DIV" will be 3. Any other value will set it to 1.
916      * @param shiftEnterMode string code
917      * @see #setShiftEnterMode(int) to just set by integer value
918      */
919 	public void setShiftEnterMode(String shiftEnterMode) {
920 		if ("BR".equalsIgnoreCase(shiftEnterMode)) 
921 			this.shiftEnterMode = 2;
922 		else if ("DIV".equalsIgnoreCase(shiftEnterMode)) 
923 			this.shiftEnterMode = 3;
924 		else
925 			this.shiftEnterMode = 1;
926 	}
927 	public void setShiftEnterMode(int shiftEnterMode) {
928 		this.shiftEnterMode = shiftEnterMode;
929 	}
930 
931 	public void setForceEnterMode(Boolean forceEnterMode) {
932 		this.forceEnterMode = forceEnterMode;
933 	}
934 
935 	public void setForcePasteAsPlainText(Boolean forcePasteAsPlainText) {
936 		this.forcePasteAsPlainText = forcePasteAsPlainText;
937 	}
938 
939 	public void setLanguage(String language) {
940 		this.language = language;
941 	}
942 
943 	public void setTemplatesReplaceContent(Boolean templatesReplaceContent) {
944 		this.templates_replaceContent = templatesReplaceContent;
945 	}
946 
947 	public void setFullPage(Boolean fullPage) {
948 		this.fullPage = fullPage;
949 	}
950         
951     /**
952      * Add a protected source regular express to the list
953      * 
954      * Typical values are regular expressions like: /<%.*%>/g
955      * 
956      * If you don't start with a '/', a leading '/' and a trailing '/g' will be added automatically. 
957      * 
958      * @param regex the String regular expression of the protected source 
959      */
960     public synchronized void addProtectedSource(String regex) {
961     	if ( protectedSource == null ) {
962     		protectedSource = new LinkedList<String>();
963     	}
964     	if ( ! regex.startsWith("/") ) {
965     		regex = "/" + regex + "/g";
966     	}
967     	if ( ! protectedSource.contains(regex) ) {
968     		protectedSource.add(regex);
969     	}
970     }
971     
972     public boolean hasProtectedSource() {
973     	return protectedSource != null && protectedSource.size() > 0;
974     }
975     
976     public List<String> getProtectedSource() {
977 		return protectedSource;
978     }
979 
980     // Advanced content filtering added in CKEditor 4.1
981     public void setAllowedContent(String acfSpec)
982     {
983     	allowedContent = acfSpec;
984     }
985     // Basically allows all content and disables ACF
986     public void setAllowedContentAll() {
987     	allowedContent = "true";
988     }
989     
990     public void setExtraAllowedContent(String acfSpec)
991     {
992     	extraAllowedContent = acfSpec;
993     }
994 
995 }