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.monacoeditor;
23  
24  import java.io.IOException;
25  
26  import javax.faces.component.UIComponent;
27  import javax.faces.context.FacesContext;
28  import javax.faces.context.ResponseWriter;
29  
30  import org.primefaces.context.PrimeRequestContext;
31  import org.primefaces.renderkit.InputRenderer;
32  import org.primefaces.shaded.json.JSONWriter;
33  import org.primefaces.shaded.owasp.encoder.Encode;
34  import org.primefaces.util.Constants;
35  import org.primefaces.util.WidgetBuilder;
36  
37  /**
38   * Base renderer for both the {@link MonacoEditorFramed framed} and {@link MonacoEditorInline inline} monaco editor as well as the diff editor.
39   *
40   * @since 11.1.0
41   */
42  abstract class MonacoEditorCommonRenderer<TEditor extends MonacoEditorCommon<TEditorOpts>, TEditorOpts> extends InputRenderer {
43  
44      protected static final String CALLBACK_SIGNATURE = "function()";
45  
46      protected final Class<TEditor> componentClass;
47  
48      protected MonacoEditorCommonRenderer(Class<TEditor> clazz) {
49          this.componentClass = clazz;
50      }
51  
52      protected abstract void addBaseWidgetProperties(FacesContext context, WidgetBuilder wb, TEditor monacoEditor) throws IOException;
53  
54      protected final void array(WidgetBuilder wb, String key, Iterable<String> values) throws IOException {
55          final StringBuilder builder = new StringBuilder();
56          builder.append('[');
57          for (final String item : values) {
58              builder.append('"');
59              builder.append(Encode.forJavaScript(item));
60              builder.append('"');
61              builder.append(',');
62          }
63          if (builder.length() > 1) {
64              builder.setLength(builder.length() - 1);
65          }
66          builder.append(']');
67          wb.nativeAttr(key, builder.toString());
68      }
69  
70      @Override
71      public final void encodeEnd(final FacesContext context, final UIComponent component) throws IOException {
72          boolean hideResourceVersion = PrimeRequestContext.getCurrentInstance(context).isHideResourceVersion();
73          if (hideResourceVersion) {
74              logDevelopmentWarning(context, "Monaco Editor requires a resource version to work properly and '" +
75                          Constants.ContextParams.HIDE_RESOURCE_VERSION + "' is currently configured.");
76          }
77          final TEditor monacoEditor = componentClass.cast(component);
78          encodeMarkup(context, monacoEditor);
79          encodeScript(context, monacoEditor);
80      }
81  
82      protected final void encodeMarkup(final FacesContext context, final TEditor monacoEditor) throws IOException {
83          final ResponseWriter writer = context.getResponseWriter();
84          final String clientId = monacoEditor.getClientId();
85  
86          String style = monacoEditor.getStyle() != null ? monacoEditor.getStyle() : "";
87          style = style.concat(";");
88          if (monacoEditor.getWidth() != null && !monacoEditor.getWidth().isEmpty()) {
89              style = style.concat("width:" + monacoEditor.getWidth() + ";");
90          }
91          if (monacoEditor.getHeight() != null && !monacoEditor.getHeight().isEmpty()) {
92              style = style.concat("height:" + monacoEditor.getHeight() + ";");
93          }
94          final StringBuilder styleClass = new StringBuilder();
95          styleClass.append(getMainStyleClass() + " ui-hidden-container ");
96          if (isEntireEditorDisabled(monacoEditor)) {
97              styleClass.append("ui-state-disabled ");
98          }
99          if (monacoEditor.getStyleClass() != null) {
100             styleClass.append(monacoEditor.getStyleClass());
101         }
102 
103         writer.startElement("div", null);
104         writer.writeAttribute("id", clientId, null);
105         writer.writeAttribute("data-widget-var", monacoEditor.resolveWidgetVar(), null);
106         writer.writeAttribute("class", styleClass.toString(), null);
107         writer.writeAttribute("style", style, null);
108 
109         encodeHiddenInput(context, monacoEditor);
110         encodeMonacoEditor(context, monacoEditor);
111 
112         writer.endElement("div");
113     }
114 
115     protected abstract boolean isEntireEditorDisabled(TEditor monacoEditor);
116 
117     protected abstract void encodeHiddenInput(FacesContext context, TEditor monacoEditor) throws IOException;
118 
119     protected abstract void encodeMonacoEditor(final FacesContext context, final TEditor monacoEditor) throws IOException;
120 
121     protected final void encodeScript(final FacesContext context, final TEditor monacoEditor) throws IOException {
122         final WidgetBuilder wb = PrimeRequestContext.getCurrentInstance(context).getWidgetBuilder();
123 
124         wb.init(getWidgetName(), monacoEditor);
125 
126         array(wb, "availableEvents", monacoEditor.getEventNames());
127 
128         if (monacoEditor.getCustomThemes() != null && !monacoEditor.getCustomThemes().isEmpty()) {
129             wb.nativeAttr("customThemes", JSONWriter.valueToString(monacoEditor.getCustomThemes()));
130         }
131 
132         wb.attr("autoResize", monacoEditor.isAutoResize(), MonacoEditorCommon.DEFAULT_AUTO_RESIZE);
133         wb.attr("basename", monacoEditor.getBasename(), MonacoEditorCommon.DEFAULT_BASENAME);
134         wb.attr("directory", monacoEditor.getDirectory(), MonacoEditorCommon.DEFAULT_DIRECTORY);
135         wb.attr("disabled", monacoEditor.isDisabled(), MonacoEditorCommon.DEFAULT_DISABLED);
136         wb.attr("editorOptions", monacoEditor.getEditorOptions().toString());
137         wb.attr("extension", monacoEditor.getExtension(), MonacoEditorCommon.DEFAULT_EXTENSION);
138         wb.attr("language", getLanguage(monacoEditor), MonacoEditorCommon.DEFAULT_LANGUAGE);
139         wb.attr("locale", monacoEditor.calculateLocale().toString());
140         wb.attr("localeUrl", monacoEditor.getLocaleUrl());
141         wb.attr("readonly", monacoEditor.isReadonly(), MonacoEditorCommon.DEFAULT_READONLY);
142         wb.attr("placeholder", monacoEditor.getPlaceholder(), MonacoEditorCommon.DEFAULT_PLACEHOLDER);
143         wb.attr("scheme", monacoEditor.getScheme(), MonacoEditorCommon.DEFAULT_SCHEME);
144         wb.attr("tabIndex", monacoEditor.getTabindex(), MonacoEditorCommon.DEFAULT_TABINDEX);
145         wb.attr("height", monacoEditor.getHeight(), MonacoEditorCommon.DEFAULT_HEIGHT);
146         wb.attr("width", monacoEditor.getWidth(), MonacoEditorCommon.DEFAULT_WIDTH);
147 
148         wb.callback("onblur", CALLBACK_SIGNATURE, monacoEditor.getOnblur());
149         wb.callback("onchange", CALLBACK_SIGNATURE, monacoEditor.getOnchange());
150         wb.callback("onfocus", CALLBACK_SIGNATURE, monacoEditor.getOnfocus());
151         wb.callback("oninitialized", CALLBACK_SIGNATURE, monacoEditor.getOninitialized());
152         wb.callback("onkeyup", CALLBACK_SIGNATURE, monacoEditor.getOnkeyup());
153         wb.callback("onmousedown", CALLBACK_SIGNATURE, monacoEditor.getOnmousedown());
154         wb.callback("onmousemove", CALLBACK_SIGNATURE, monacoEditor.getOnmousemove());
155         wb.callback("onmouseup", CALLBACK_SIGNATURE, monacoEditor.getOnmouseup());
156         wb.callback("onkeydown", CALLBACK_SIGNATURE, monacoEditor.getOnkeydown());
157         wb.callback("onpaste", CALLBACK_SIGNATURE, monacoEditor.getOnpaste());
158 
159         addBaseWidgetProperties(context, wb, monacoEditor);
160 
161         encodeClientBehaviors(context, monacoEditor);
162         wb.finish();
163     }
164 
165     protected abstract String getLanguage(TEditor monacoEditor);
166 
167     protected abstract String getMainStyleClass();
168 
169     protected abstract String getWidgetName();
170 }