View Javadoc
1   /*
2    * Copyright (c) 2011-2024 PrimeFaces Extensions
3    *
4    *  Permission is hereby granted, free of charge, to any person obtaining a copy
5    *  of this software and associated documentation files (the "Software"), to deal
6    *  in the Software without restriction, including without limitation the rights
7    *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8    *  copies of the Software, and to permit persons to whom the Software is
9    *  furnished to do so, subject to the following conditions:
10   *
11   *  The above copyright notice and this permission notice shall be included in
12   *  all copies or substantial portions of the Software.
13   *
14   *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15   *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16   *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17   *  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18   *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19   *  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20   *  THE SOFTWARE.
21   */
22  package org.primefaces.extensions.component.codemirror;
23  
24  import java.io.IOException;
25  import java.util.List;
26  import java.util.Map;
27  
28  import javax.faces.component.UIComponent;
29  import javax.faces.context.FacesContext;
30  import javax.faces.context.ResponseWriter;
31  import javax.faces.convert.Converter;
32  import javax.faces.event.PhaseId;
33  
34  import org.primefaces.expression.SearchExpressionUtils;
35  import org.primefaces.extensions.event.CompleteEvent;
36  import org.primefaces.extensions.util.Attrs;
37  import org.primefaces.renderkit.InputRenderer;
38  import org.primefaces.util.ComponentUtils;
39  import org.primefaces.util.HTML;
40  import org.primefaces.util.WidgetBuilder;
41  
42  /**
43   * Renderer for the {@link CodeMirror} component.
44   *
45   * @author Thomas Andraschko / last modified by $Author$
46   * @version $Revision$
47   * @since 0.3
48   */
49  public class CodeMirrorRenderer extends InputRenderer {
50  
51      @Override
52      public void decode(final FacesContext facesContext, final UIComponent component) {
53          final CodeMirror codeMirror = (CodeMirror) component;
54  
55          if (!shouldDecode(codeMirror)) {
56              return;
57          }
58  
59          // set value
60          final String clientId = codeMirror.getClientId(facesContext);
61          final Map<String, String> params = facesContext.getExternalContext().getRequestParameterMap();
62          if (params.containsKey(clientId)) {
63              codeMirror.setSubmittedValue(params.get(clientId));
64          }
65  
66          // decode behaviors
67          decodeBehaviors(facesContext, component);
68  
69          // complete event
70          final String token = params.get(clientId + "_token");
71          if (token != null) {
72              final String context = params.get(clientId + "_context");
73              final int line = Integer.parseInt(params.get(clientId + "_line"));
74              final int column = Integer.parseInt(params.get(clientId + "_column"));
75  
76              final CompleteEvent autoCompleteEvent = new CompleteEvent(
77                          codeMirror, token, context, line, column);
78              autoCompleteEvent.setPhaseId(PhaseId.APPLY_REQUEST_VALUES);
79              codeMirror.queueEvent(autoCompleteEvent);
80          }
81      }
82  
83      @Override
84      public void encodeEnd(final FacesContext context, final UIComponent component) throws IOException {
85          final CodeMirror codeMirror = (CodeMirror) component;
86  
87          final Map<String, String> params = context.getExternalContext().getRequestParameterMap();
88          final String token = params.get(codeMirror.getClientId(context) + "_token");
89  
90          if (token != null) {
91              encodeSuggestions(context, codeMirror, codeMirror.getSuggestions());
92          }
93          else {
94              encodeMarkup(context, codeMirror);
95              encodeScript(context, codeMirror);
96          }
97      }
98  
99      protected void encodeMarkup(final FacesContext context, final CodeMirror codeMirror) throws IOException {
100         final ResponseWriter writer = context.getResponseWriter();
101         final String clientId = codeMirror.getClientId(context);
102 
103         writer.startElement("textarea", codeMirror);
104         writer.writeAttribute("id", clientId, null);
105         writer.writeAttribute("name", clientId, null);
106 
107         renderAccessibilityAttributes(context, codeMirror);
108         renderPassThruAttributes(context, codeMirror, HTML.TEXTAREA_ATTRS_WITHOUT_EVENTS);
109         renderDomEvents(context, codeMirror, HTML.INPUT_TEXT_EVENTS);
110         renderValidationMetadata(context, codeMirror);
111 
112         final String valueToRender = ComponentUtils.getValueToRender(context, codeMirror);
113         if (valueToRender != null) {
114             if (codeMirror.isEscape()) {
115                 writer.writeText(valueToRender, null);
116             }
117             else {
118                 writer.write(valueToRender);
119             }
120         }
121 
122         writer.endElement("textarea");
123     }
124 
125     protected void encodeScript(final FacesContext context, final CodeMirror codeMirror) throws IOException {
126         final WidgetBuilder wb = getWidgetBuilder(context);
127         wb.init("ExtCodeMirror", codeMirror);
128         wb.attr("theme", codeMirror.getTheme())
129                     .attr("mode", codeMirror.getMode())
130                     .attr("indentUnit", codeMirror.getIndentUnit())
131                     .attr("smartIndent", codeMirror.isSmartIndent())
132                     .attr("tabSize", codeMirror.getTabSize())
133                     .attr("indentWithTabs", codeMirror.isIndentWithTabs())
134                     .attr("electricChars", codeMirror.isElectricChars())
135                     .attr("keyMap", codeMirror.getKeyMap())
136                     .attr("lineWrapping", codeMirror.isLineWrapping())
137                     .attr("lineNumbers", codeMirror.isLineNumbers())
138                     .attr("firstLineNumber", codeMirror.getFirstLineNumber())
139                     .attr("gutter", codeMirror.isGutter())
140                     .attr("fixedGutter", codeMirror.isFixedGutter())
141                     .attr("readOnly", codeMirror.isReadonly())
142                     .attr("matchBrackets", codeMirror.isMatchBrackets())
143                     .attr("workTime", codeMirror.getWorkTime())
144                     .attr("workDelay", codeMirror.getWorkDelay())
145                     .attr("pollInterval", codeMirror.getPollInterval())
146                     .attr(Attrs.TABINDEX, codeMirror.getTabindex())
147                     .attr("undoDepth", codeMirror.getUndoDepth());
148 
149         if (codeMirror.getExtraKeys() != null) {
150             wb.append(",extraKeys:" + codeMirror.getExtraKeys());
151         }
152         if (!codeMirror.isGlobal()) {
153             wb.attr("global", false);
154         }
155         if (codeMirror.isAsync()) {
156             wb.attr("async", true);
157         }
158         if (codeMirror.getProcess() != null) {
159             wb.attr("process", SearchExpressionUtils.resolveClientIdsForClientSide(context, codeMirror, codeMirror.getProcess()));
160         }
161         if (codeMirror.getOnstart() != null) {
162             wb.callback("onstart", "function(request)", codeMirror.getOnstart());
163         }
164         if (codeMirror.getOncomplete() != null) {
165             wb.callback("oncomplete", "function(xhr, status, args)", codeMirror.getOncomplete());
166         }
167         if (codeMirror.getOnsuccess() != null) {
168             wb.callback("onsuccess", "function(data, status, xhr)", codeMirror.getOnsuccess());
169         }
170         if (codeMirror.getOnerror() != null) {
171             wb.callback("onerror", "function(xhr, status, error)", codeMirror.getOnerror());
172         }
173 
174         encodeClientBehaviors(context, codeMirror);
175 
176         wb.finish();
177     }
178 
179     @Override
180     public Object getConvertedValue(final FacesContext context, final UIComponent component, final Object submittedValue) {
181         final CodeMirror codeMirror = (CodeMirror) component;
182         final String value = (String) submittedValue;
183         final Converter<?> converter = ComponentUtils.getConverter(context, component);
184 
185         if (converter != null) {
186             return converter.getAsObject(context, codeMirror, value);
187         }
188 
189         return value;
190     }
191 
192     protected void encodeSuggestions(final FacesContext context, final CodeMirror codeMirror, final List<String> suggestions) throws IOException {
193         final ResponseWriter writer = context.getResponseWriter();
194 
195         writer.startElement("ul", codeMirror);
196 
197         for (final String suggestion : suggestions) {
198             writer.startElement("li", null);
199 
200             if (codeMirror.isEscapeSuggestions()) {
201                 writer.writeText(suggestion, null);
202             }
203             else {
204                 writer.write(suggestion);
205             }
206 
207             writer.endElement("li");
208         }
209 
210         writer.endElement("ul");
211     }
212 }