View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.myfaces.custom.inputHtml;
20  
21  import javax.faces.FacesException;
22  import javax.faces.application.Resource;
23  import javax.faces.component.NamingContainer;
24  import javax.faces.component.UIViewRoot;
25  import javax.faces.component.UniqueIdVendor;
26  import javax.faces.context.ExternalContext;
27  import javax.faces.context.FacesContext;
28  import javax.faces.event.ComponentSystemEvent;
29  import javax.faces.event.ListenerFor;
30  
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.apache.myfaces.buildtools.maven2.plugin.builder.annotation.JSFComponent;
34  import org.apache.myfaces.component.html.ext.HtmlInputText;
35  import org.apache.myfaces.shared_tomahawk.renderkit.RendererUtils;
36  import org.apache.myfaces.shared_tomahawk.renderkit.html.HtmlRendererUtils;
37  import org.apache.myfaces.shared_tomahawk.renderkit.html.util.FormInfo;
38  import org.apache.myfaces.shared_tomahawk.renderkit.html.util.JavascriptUtils;
39  import org.apache.myfaces.tomahawk.application.PreRenderViewAddResourceEvent;
40  import org.apache.myfaces.tomahawk.util.TomahawkResourceUtils;
41  
42  /**
43   * HTML Editor using the kupu library.
44   * http://kupu.oscom.org/
45   *
46   * An inline HTML based word processor based on the Kupu library. 
47   * 
48   * See http://kupu.oscom.org 
49   * 
50   * Right now, the support is limited to one editor per page 
51   * (but you can use tabs to have multiple editors, but only 
52   * one rendered at a time). 
53   * 
54   * Unless otherwise specified, all attributes accept static values or EL expressions.
55   *
56   * @author Sylvain Vieujot (latest modification by $Author: skitching $)
57   * @version $Revision: 673833 $ $Date: 2008-07-03 16:58:05 -0500 (jue, 03 jul 2008) $
58   */
59  @JSFComponent(
60     name = "t:inputHtml",
61     tagClass = "org.apache.myfaces.custom.inputHtml.InputHtmlTag",
62     composite=true)
63  @ListenerFor(systemEventClass=PreRenderViewAddResourceEvent.class)
64  public class InputHtml extends HtmlInputText implements NamingContainer, UniqueIdVendor {
65      public static final String COMPONENT_TYPE = "org.apache.myfaces.InputHtml";
66  
67      public static final String DEFAULT_RENDERER_TYPE = "org.apache.myfaces.InputHtml";
68  
69      private static final String INPUT_HTML_LIBRARY = "oam.custom.inputHtml";
70      private static final String INPUT_HTML_LIBRARY_KUPU_DRAWERS = "oam.custom.inputHtml.kupudrawers";
71  
72      private static final Log log = LogFactory.getLog(HtmlInputText.class);
73  
74      public InputHtml() {
75          setRendererType(DEFAULT_RENDERER_TYPE);
76      }
77      
78      @Override
79      public void setRendererType(String rendererType)
80      {
81          if (!"javax.faces.Composite".equals(rendererType))
82          {
83              super.setRendererType(rendererType);
84          }
85      }
86      
87      public void processEvent(ComponentSystemEvent event)
88      {
89          super.processEvent(event);
90          if (event instanceof PreRenderViewAddResourceEvent)
91          {
92              InputHtml editor = (InputHtml) event.getComponent();
93              if( !HtmlRendererUtils.isDisplayValueOnly(editor) && !useFallback(editor))
94              {
95                  FacesContext facesContext = FacesContext.getCurrentInstance();
96                  //AddResource addResource = AddResourceFactory.getInstance(facesContext);
97                  //addResource.addStyleSheet(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "kupustyles.css");
98                  TomahawkResourceUtils.addOutputStylesheetResource(facesContext, INPUT_HTML_LIBRARY, "myFacesKupustyles.css");
99                  //addResource.addStyleSheet(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "kupudrawerstyles.css");
100                 TomahawkResourceUtils.addOutputStylesheetResource(facesContext, INPUT_HTML_LIBRARY, "kupudrawerstyles.css");
101                 //addResource.addStyleSheet(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "myFaces.css");
102                 TomahawkResourceUtils.addOutputStylesheetResource(facesContext, INPUT_HTML_LIBRARY, "myFaces.css");
103     
104                 //addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "sarissa.js");
105                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "sarissa.js");
106                 //addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "sarissa_ieemu_xpath.js");
107                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "sarissa_ieemu_xpath.js");
108                 //addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "kupuhelpers.js");
109                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "kupuhelpers.js");
110                 //addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "kupueditor.js");
111                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "kupueditor.js");
112                 //addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "kupubasetools.js");
113                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "kupubasetools.js");
114                 //addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "kupuloggers.js");
115                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "kupuloggers.js");
116                 //addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "kupunoi18n.js");
117                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "kupunoi18n.js");
118                 //addResource.addJavaScriptAtPosition(context, InputHtmlRenderer.class, "i18n/i18n.js"); //NO
119                 //addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "kupucleanupexpressions.js");
120                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "kupucleanupexpressions.js");
121                 //addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "kupucontentfilters.js");
122                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "kupucontentfilters.js");
123     
124                 if (editor.isShowAnyToolBox())
125                 {
126                     //addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "kuputoolcollapser.js");
127                     TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "kuputoolcollapser.js");
128                 }
129                 //addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "kupucontextmenu.js");
130                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "kupucontextmenu.js");
131     
132                 //addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "kupuinit.js");
133                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "kupuinit.js");
134                 //addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "kupustart.js");
135                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "kupustart.js");
136     
137                 //addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "kupusourceedit.js");
138                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "kupusourceedit.js");
139                 //addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "kupuspellchecker.js");
140                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "kupuspellchecker.js");
141                 //addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "kupudrawers.js");
142                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "kupudrawers.js");
143     
144                 //addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "kupuundo.js");
145                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "kupuundo.js");
146                 //addResource.addJavaScriptAtPosition(context, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "diff_match_patch.js");
147                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "diff_match_patch.js");
148     
149                 //addResource.addJavaScriptAtPosition(facesContext, AddResource.HEADER_BEGIN, InputHtmlRenderer.class, "myFacesUtils.js");
150                 TomahawkResourceUtils.addOutputScriptResource(facesContext, INPUT_HTML_LIBRARY, "myFacesUtils.js");
151                 
152                 if( editor.getStyle()!=null ){
153                     // Convert the style into an style declaration so that it doesn't preempt the Zoom works as it's relying on changing the class
154                     //addResource.addInlineStyleAtPosition(
155                     //        context, AddResource.HEADER_BEGIN,
156                     //        "#kupu-editor{height: inherit;}\n"+
157                     //        "div.kupu-fulleditor{"+editor.getStyle()+"}");
158                     TomahawkResourceUtils.addInlineOutputStylesheetResource(facesContext, 
159                             "#kupu-editor{height: inherit;}\n"+
160                             "div.kupu-fulleditor{"+editor.getStyle()+"}");
161                 }
162             }
163         }
164     }
165     
166     private static boolean useFallback(InputHtml editor){
167         // TODO : Handle fallback="auto"
168         return editor.getFallback().equals("true");
169     }
170     /*
171     <xml id="kupuconfig" class="kupuconfig">
172       <kupuconfig>
173         
174   <dst>#{resource['oam.custom.inputHtml:fulldoc.html']}</dst>
175   <use_css>1</use_css>
176   <reload_after_save>0</reload_after_save>
177   <strict_output>1</strict_output>
178   <content_type>application/xhtml+xml</content_type>
179   <compatible_singletons>1</compatible_singletons>
180   <table_classes>
181     <class>plain</class>
182     <class>listing</class>
183     <class>grid</class>
184     <class>data</class>
185   </table_classes>
186 
187   <cleanup_expressions>
188     <set>
189       <name>Convert single quotes to curly ones</name>
190       <expression>
191         <reg>
192           (\W)'
193         </reg>
194         <replacement>
195           \1&#8216;
196         </replacement>
197       </expression>
198       <expression>
199         <reg>
200           '
201         </reg>
202         <replacement>
203           &#8217;
204         </replacement>
205       </expression>
206     </set>
207     <set>
208       <name>Reduce whitespace</name>
209       <expression>
210         <reg>
211           [\n\r\t]
212         </reg>
213         <replacement>
214           \x20
215         </replacement>
216       </expression>
217       <expression>
218         <reg>
219           [ ]{2}
220         </reg>
221         <replacement>
222           \x20
223         </replacement>
224       </expression>
225     </set>
226   </cleanup_expressions>
227 
228   <image_xsl_uri>#{resource['oam.custom.inputHtml.kupudrawers:drawer.xsl']}</image_xsl_uri>
229   <link_xsl_uri>#{resource['oam.custom.inputHtml.kupudrawers:drawer.xsl']}</link_xsl_uri>
230   <image_libraries_uri>#{resource['oam.custom.inputHtml.kupudrawers:imagelibrary.xml']}</image_libraries_uri>
231   <link_libraries_uri>#{resource['oam.custom.inputHtml.kupudrawers:linklibrary.xml']}</link_libraries_uri>
232   <search_images_uri> </search_images_uri>
233   <search_links_uri> </search_links_uri>
234 
235       </kupuconfig>
236     </xml>
237    */
238     public String getKupuConfig()
239     {
240         StringBuilder builder = new StringBuilder();
241         builder.append("<xml id=\"kupuconfig\" class=\"kupuconfig\">");
242         builder.append("<kupuconfig>");
243         builder.append("<dst>fulldoc.html</dst>");
244         builder.append("<use_css>1</use_css>");
245         builder.append("<reload_after_save>0</reload_after_save>");
246         builder.append("<strict_output>1</strict_output>");
247         builder.append("<content_type>application/xhtml+xml</content_type>");
248         builder.append("<compatible_singletons>1</compatible_singletons>");
249         builder.append("<table_classes>");
250         builder.append("<class>plain</class>");
251         builder.append("<class>listing</class>");
252         builder.append("<class>grid</class>");
253         builder.append("<class>data</class>");
254         builder.append("</table_classes>");
255 
256         builder.append("<cleanup_expressions>");
257         builder.append("<set>");
258         builder.append("<name>Convert single quotes to curly ones</name>");
259         builder.append("<expression>");
260         builder.append("<reg>");
261         builder.append("(\\W)'");
262         builder.append("</reg>");
263         builder.append("<replacement>");
264         builder.append("\1&#8216;");
265         builder.append("</replacement>");
266         builder.append("</expression>");
267         builder.append("<expression>");
268         builder.append("<reg>");
269         builder.append("'");
270         builder.append("</reg>");
271         builder.append("<replacement>");
272         builder.append("&#8217;");
273         builder.append("</replacement>");
274         builder.append("</expression>");
275         builder.append("</set>");
276         builder.append("<set>");
277         builder.append("<name>Reduce whitespace</name>");
278         builder.append("<expression>");
279         builder.append("<reg>");
280         builder.append("[\n\r\t]");
281         builder.append("</reg>");
282         builder.append("<replacement>");
283         builder.append("\\x20");
284         builder.append("</replacement>");
285         builder.append("</expression>");
286         builder.append("<expression>");
287         builder.append("<reg>");
288         builder.append("[ ]{2}");
289         builder.append("</reg>");
290         builder.append("<replacement>");
291         builder.append("\\x20");
292         builder.append("</replacement>");
293         builder.append("</expression>");
294         builder.append("</set>");
295         builder.append("</cleanup_expressions>");
296 
297         FacesContext context = getFacesContext();
298         Resource resource = context.getApplication().getResourceHandler()
299                 .createResource("drawer.xsl",
300                         INPUT_HTML_LIBRARY_KUPU_DRAWERS);
301         builder.append("<image_xsl_uri>" + resource.getRequestPath()
302                 + "</image_xsl_uri>");
303         builder.append("<link_xsl_uri>" + resource.getRequestPath()
304                 + "</link_xsl_uri>");
305 
306         resource = context.getApplication().getResourceHandler()
307                 .createResource("imagelibrary.xml",
308                         INPUT_HTML_LIBRARY_KUPU_DRAWERS);
309         builder.append("<image_libraries_uri>" + resource.getRequestPath()
310                 + "</image_libraries_uri>");
311 
312         resource = context.getApplication().getResourceHandler()
313                 .createResource("linklibrary.xml",
314                         INPUT_HTML_LIBRARY_KUPU_DRAWERS);
315         builder.append("<link_libraries_uri>" + resource.getRequestPath()
316                 + "</link_libraries_uri>");
317 
318         builder.append("<search_images_uri> </search_images_uri>");
319         builder.append("<search_links_uri> </search_links_uri>");
320 
321         builder.append("</kupuconfig>");
322         builder.append("</xml>");
323         return builder.toString();
324     }
325     
326     
327     public String getDisplayValueOnlyText()
328     {
329         return getHtmlBody(RendererUtils.getStringValue(getFacesContext(), this));
330     }
331     
332     public String getFallbackText()
333     {
334         String text = RendererUtils.getStringValue(getFacesContext(), this);
335         return htmlToPlainText(text, this);
336     }
337     
338     public String getHiddenText()
339     {
340         return RendererUtils.getStringValue(getFacesContext(), this);
341     }
342     
343     private static String htmlToPlainText(String html, InputHtml editor){
344         return editor.getHtmlBody( html )
345                 .replaceAll("<br.*>","\n")
346                 .replaceAll("<.+?>", "");
347     }
348     
349     
350     public String getFormId()
351     {
352         FormInfo parentFormInfo = RendererUtils.findNestingForm(this, getFacesContext());
353         if(parentFormInfo == null)
354             throw new FacesException("InputHtml must be embedded in a form.");
355         return parentFormInfo.getFormName(); 
356     }
357     
358     public String getEncodedText()
359     {
360         String text = this.getValueAsHtmlDocument(getFacesContext());
361         return (text == null) ? "" : JavascriptUtils.encodeString( text );
362 
363     }
364     
365     /**
366      * 
367      * {@inheritDoc}
368      * 
369      * @since 2.0
370      */
371     public String createUniqueId(FacesContext context, String seed)
372     {
373         StringBuilder bld = new StringBuilder();
374 
375         // Generate an identifier for a component. The identifier will be prefixed with UNIQUE_ID_PREFIX, and will be unique within this UIViewRoot. 
376         if(seed==null)
377         {
378             Long uniqueIdCounter = (Long) getStateHelper().get(PropertyKeys.uniqueIdCounter);
379             uniqueIdCounter = (uniqueIdCounter == null) ? 0 : uniqueIdCounter;
380             getStateHelper().put(PropertyKeys.uniqueIdCounter, (uniqueIdCounter+1L));
381             return bld.append(UIViewRoot.UNIQUE_ID_PREFIX).append(uniqueIdCounter).toString();    
382         }
383         // Optionally, a unique seed value can be supplied by component creators which should be included in the generated unique id.
384         else
385         {
386             return bld.append(UIViewRoot.UNIQUE_ID_PREFIX).append(seed).toString();
387         }
388     }    
389     
390     /**
391      * Use a text area instead of the javascript HTML editor. 
392      * 
393      * Default is false. Use with caution.
394      * 
395      * @JSFProperty
396      */
397     public String getFallback(){
398         return (String) getStateHelper().eval(PropertyKeys.fallback, "false");
399     }
400     public void setFallback(String _fallback){
401         getStateHelper().put(PropertyKeys.fallback, _fallback);
402     }
403 
404     /**
405      * The type of the value. It can be either fragment for an HTML 
406      * fragment (default) or document for a full HTML document, with 
407      * head, title, body, ... tags.
408      * 
409      * @JSFProperty
410      */
411     public String getType(){
412         return (String) getStateHelper().eval(PropertyKeys.type, "fragment");
413     }
414     public void setType(String _type){
415         getStateHelper().put(PropertyKeys.type, _type);
416     }
417     public boolean isTypeDocument(){
418         return getType().equals("document");
419     }
420 
421     /**
422      * Allows the user to edit the HTML source code. Default is true.
423      * 
424      * @JSFProperty
425      */
426     public boolean isAllowEditSource(){
427         return (Boolean) getStateHelper().eval(PropertyKeys.allowEditSource, Boolean.TRUE);
428     }
429     public void setAllowEditSource(boolean allowEditSource){
430         getStateHelper().put(PropertyKeys.allowEditSource, allowEditSource);
431     }
432 
433     /**
434      * Allows the user to insert external links. Default is true.
435      * 
436      * @JSFProperty
437      */
438     public boolean isAllowExternalLinks(){
439         return (Boolean) getStateHelper().eval(PropertyKeys.allowExternalLinks, Boolean.TRUE);
440     }
441     public void setAllowExternalLinks(boolean allowExternalLinks){
442         getStateHelper().put(PropertyKeys.allowExternalLinks, allowExternalLinks);
443     }
444 
445     /**
446      * Show the Kupu Logo in the buttons bar. Default is true.
447      * 
448      * @JSFProperty
449      */
450     public boolean isAddKupuLogo(){
451         return (Boolean) getStateHelper().eval(PropertyKeys.addKupuLogo, Boolean.TRUE);
452     }
453     public void setAddKupuLogo(boolean addKupuLogo){
454         getStateHelper().put(PropertyKeys.addKupuLogo, addKupuLogo);
455     }
456 
457     /**
458      * Shortcut to avoid setting all the showXXToolBox to true. Default is false.
459      * 
460      * @JSFProperty
461      */
462     public boolean isShowAllToolBoxes(){
463         return (Boolean) getStateHelper().eval(PropertyKeys.showAllToolBoxes, Boolean.FALSE);
464     }
465     public void setShowAllToolBoxes(boolean showAllToolBoxes){
466         getStateHelper().put(PropertyKeys.showAllToolBoxes, showAllToolBoxes);
467     }
468 
469     /**
470      * Show the Properties tool box next to the text. Default is false.
471      * 
472      * @JSFProperty
473      */
474     public boolean isShowPropertiesToolBox(){
475         if( isShowAllToolBoxes() )
476             return true;
477 
478         return (Boolean) getStateHelper().eval(PropertyKeys.showPropertiesToolBox, Boolean.FALSE);
479     }
480 
481     public void setShowPropertiesToolBox(boolean showPropertiesToolBox){
482         getStateHelper().put(PropertyKeys.showPropertiesToolBox, showPropertiesToolBox);
483     }
484 
485     /**
486      * Show the Links tool box next to the text. Default is false.
487      * 
488      * @JSFProperty
489      */
490     public boolean isShowLinksToolBox(){
491         if( isShowAllToolBoxes() )
492             return true;
493 
494         return (Boolean) getStateHelper().eval(PropertyKeys.showLinksToolBox, Boolean.FALSE);
495     }
496     
497     public void setShowLinksToolBox(boolean showLinksToolBox){
498         getStateHelper().put(PropertyKeys.showLinksToolBox, showLinksToolBox);
499     }
500 
501     /**
502      * Show the Images tool box next to the text. Default is false.
503      * 
504      * @JSFProperty
505      */
506     public boolean isShowImagesToolBox(){
507         if( isShowAllToolBoxes() )
508             return true;
509 
510         return (Boolean) getStateHelper().eval(PropertyKeys.showImagesToolBox, Boolean.FALSE);
511     }
512     public void setShowImagesToolBox(boolean showImagesToolBox){
513         getStateHelper().put(PropertyKeys.showImagesToolBox, showImagesToolBox);
514     }
515 
516     /**
517      * Show the Tables tool box next to the text. Default is false.
518      * 
519      * @JSFProperty
520      */
521     public boolean isShowTablesToolBox(){
522         if( isShowAllToolBoxes() )
523             return true;
524 
525         return (Boolean) getStateHelper().eval(PropertyKeys.showTablesToolBox, Boolean.FALSE);
526     }
527     public void setShowTablesToolBox(boolean showTablesToolBox){
528         getStateHelper().put(PropertyKeys.showTablesToolBox, showTablesToolBox);
529     }
530 
531     /**
532      * Show the Cleanup Expressions tool box next to the text. Default is false.
533      * 
534      * @JSFProperty
535      */
536     public boolean isShowCleanupExpressionsToolBox(){
537         if( isShowAllToolBoxes() )
538             return true;
539 
540         return (Boolean) getStateHelper().eval(PropertyKeys.showCleanupExpressionsToolBox, Boolean.FALSE);
541     }
542     public void setShowCleanupExpressionsToolBox(boolean showCleanupExpressionsToolBox){
543         getStateHelper().put(PropertyKeys.showCleanupExpressionsToolBox, showCleanupExpressionsToolBox);
544     }
545 
546     /**
547      * Show the Debug tool box next to the text. Default is false.
548      * 
549      * @JSFProperty
550      */
551     public boolean isShowDebugToolBox(){
552         if( isShowAllToolBoxes() )
553             return true;
554 
555         return (Boolean) getStateHelper().eval(PropertyKeys.showDebugToolBox, Boolean.FALSE);
556     }
557     public void setShowDebugToolBox(boolean showTablesToolBox){        
558         getStateHelper().put(PropertyKeys.showDebugToolBox, showTablesToolBox);
559     }
560 
561     public boolean isShowAnyToolBox(){
562            return isShowAllToolBoxes()
563                || isShowPropertiesToolBox()
564                || isShowLinksToolBox()
565                || isShowImagesToolBox()
566                || isShowTablesToolBox()
567                || isShowCleanupExpressionsToolBox()
568                || isShowDebugToolBox();
569     }
570 
571     public String getValueAsHtmlDocument(FacesContext context){
572         String val = RendererUtils.getStringValue(context, this);
573         if( isHtmlDocument( val ) )
574             return val;
575 
576         return "<html><body>"+(val==null ? "" : val)+"</body></html>";
577     }
578 
579     private static boolean isHtmlDocument(String text){
580         if( text == null )
581             return false;
582 
583         if( text.indexOf("<body>")!=-1 || text.indexOf("<body ")!=-1
584             || text.indexOf("<BODY>")!=-1 || text.indexOf("<BODY ")!=-1 )
585             return true;
586 
587         return false;
588     }
589 
590     public String getValueFromDocument(String text){
591         if( text == null )
592             return "";
593 
594         if( isTypeDocument() )
595             return text.trim();
596 
597         if( !isHtmlDocument(text) )
598             return text.trim();
599 
600         // Extract the fragment from the document.
601         String fragment = getHtmlBody( text );
602         if( fragment.endsWith("<br />") )
603             return fragment.substring(0, fragment.length()-6);
604         return fragment;
605     }
606     
607     String getHtmlBody(String html){
608         html = html.trim();
609         if( html.length() == 0 )
610             return "";
611 
612         String lcText = html.toLowerCase();
613         int textLength = lcText.length();
614         int bodyStartIndex = -1;
615         while(bodyStartIndex < textLength){
616             bodyStartIndex++;
617             bodyStartIndex = lcText.indexOf("<body", bodyStartIndex);
618             if( bodyStartIndex == -1 )
619                 break; // not found.
620 
621             bodyStartIndex += 5;
622             char c = lcText.charAt(bodyStartIndex);
623             if( c=='>' )
624                 break;
625 
626             if( c!=' ' && c!='\t' )
627                 continue;
628 
629             bodyStartIndex = lcText.indexOf('>', bodyStartIndex);
630             break;
631         }
632         bodyStartIndex++;
633 
634         int bodyEndIndex = lcText.lastIndexOf("</body>")-1;
635         
636         if( bodyStartIndex<0 || bodyEndIndex<0
637            || bodyStartIndex > bodyEndIndex
638            || bodyStartIndex>=textLength || bodyEndIndex>=textLength ){
639 
640             if( lcText.indexOf("<body/>")!=-1 || lcText.indexOf("<body />")!=-1 )
641                 return "";
642             
643             int htmlStartIndex = lcText.indexOf("<html>");
644             int htmlEndIndex = lcText.indexOf("</html>");
645             if( htmlStartIndex != -1 && htmlEndIndex > htmlStartIndex )
646                 return html.substring(htmlStartIndex+6, htmlEndIndex);
647             
648             if( isTypeDocument() )
649                 log.warn("Couldn't extract HTML body from :\n"+html);
650             return html.trim();
651         }
652 
653         return html.substring(bodyStartIndex, bodyEndIndex+1).trim();
654     }
655     
656     protected enum PropertyKeys
657     {
658         fallback,
659         type,
660         allowEditSource,
661         allowExternalLinks,
662         addKupuLogo,
663         showAllToolBoxes,
664         showPropertiesToolBox,
665         showLinksToolBox,
666         showImagesToolBox,
667         showTablesToolBox,
668         showCleanupExpressionsToolBox,
669         showDebugToolBox,
670         uniqueIdCounter
671     }
672 }