1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34 package info.magnolia.cms.gui.dialog;
35
36 import info.magnolia.cms.core.NodeData;
37 import info.magnolia.cms.gui.control.Edit;
38 import info.magnolia.cms.gui.misc.CssConstants;
39 import info.magnolia.cms.util.BooleanUtil;
40
41 import java.io.IOException;
42 import java.io.Writer;
43 import java.util.HashMap;
44 import java.util.Map;
45
46 import javax.jcr.PropertyType;
47
48 import org.apache.commons.lang.StringUtils;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74 public class DialogEditCode extends DialogBox {
75
76
77
78
79 private static final String ATTRIBUTE_CODEMIRROR_LOADED = "info.magnolia.cms.gui.dialog.codemirror.loaded";
80
81 private static final Logger log = LoggerFactory.getLogger(DialogEditCode.class);
82
83
84
85 public static final Map<String,String> availableParsers = new HashMap<String,String>();
86
87
88
89
90 public static final Long LINE_HEIGHT = 16L;
91
92 static {
93 availableParsers.put("js", "JSParser");
94 availableParsers.put("javascript", "JSParser");
95 availableParsers.put("processedJs", "JSParser");
96 availableParsers.put("css", "CSSParser");
97 availableParsers.put("processedCss", "CSSParser");
98 availableParsers.put("html", "HTMLMixedParser");
99 availableParsers.put("freemarker", "FreemarkerParser");
100 availableParsers.put("ftl", "FreemarkerParser");
101 availableParsers.put("groovy", "GroovyParser");
102 availableParsers.put("generic", "HTMLMixedParser");
103 }
104
105
106
107
108 public void drawHtml(Writer out) throws IOException {
109 final Edit control = new Edit(this.getName(), this.getValue());
110 control.setType(this.getConfigValue("type", PropertyType.TYPENAME_STRING));
111 control.setCssClass(CssConstants.CSSCLASS_EDIT);
112 if (this.getConfigValue("saveInfo").equals("false")) {
113 control.setSaveInfo(false);
114 }
115
116 boolean useHighlighter = BooleanUtil.toBoolean(this.getConfigValue("useCodeHighlighter"), true);
117 log.debug("useHighlighter? {}",useHighlighter);
118 if(!useHighlighter){
119 drawSimpleEditor(out, control);
120 return;
121 }
122
123 final String language = this.getConfigValue("language");
124 String parser = null;
125 if(StringUtils.isNotBlank(language)){
126 parser = getAvailableParser(language.trim());
127 if(parser == null){
128 log.warn("No suitable parser found for language {}. Syntax highlighting will not be available. Look at the control's documentation for the supported languages. ", language);
129 drawSimpleEditor(out, control);
130 return;
131 }
132 }
133 drawEditorWithSyntaxHighligher(out, control, parser);
134 }
135
136 private void drawSimpleEditor(Writer out, Edit control) throws IOException {
137 control.setRows(this.getConfigValue("rows", "20"));
138 control.setCssStyles("width", this.getConfigValue("width", "100%"));
139 control.setCssStyles("font-family", "Courier New, monospace");
140 control.setCssStyles("font-size", "14px");
141 if (this.getConfigValue("onchange", null) != null) {
142 control.setEvent("onchange", this.getConfigValue("onchange"));
143 }
144 this.drawHtmlPre(out);
145 out.write(control.getHtml());
146 this.drawHtmlPost(out);
147 }
148
149 private void drawEditorWithSyntaxHighligher(Writer out, Edit control, String parser) throws IOException{
150 final String pathToCodeMirror = this.getRequest().getContextPath() + "/.resources/js/codemirror/";
151 if(parser == null) {
152
153 final NodeData extNodeData = this.getStorageNode().getNodeData("extension");
154
155 if(extNodeData.isExist()){
156 final String ext = extNodeData.getString();
157 parser = getAvailableParser(ext);
158 } else {
159
160 final String template = this.getStorageNode().getMetaData().getTemplate();
161 parser = getAvailableParser(template);
162 }
163 if(parser == null){
164 log.debug("No suitable parser found. Falling back to generic.");
165 parser = getAvailableParser("generic");
166 }
167 }
168
169 final String rows = this.getConfigValue("rows", "20");
170 final String height = String.valueOf(Long.valueOf(rows) * LINE_HEIGHT);
171 control.setRows(height);
172
173 this.drawHtmlPre(out);
174
175 if (getRequest().getAttribute(ATTRIBUTE_CODEMIRROR_LOADED) == null) {
176 out.write("<script type=\"text/javascript\" src=\""+ pathToCodeMirror + "codemirror-min.js\"></script>");
177 getRequest().setAttribute(ATTRIBUTE_CODEMIRROR_LOADED, true);
178 }
179
180 final StringBuilder inlineStyle = new StringBuilder("<style>\n");
181 inlineStyle.append(".CodeMirror-line-numbers {\n");
182 inlineStyle.append("background-color: #eee;\n");
183 inlineStyle.append("text-align: right;\n");
184 inlineStyle.append("font-family: monospace;\n");
185 inlineStyle.append("font-size: 10pt;\n");
186 inlineStyle.append("color: #aaa;\n");
187 inlineStyle.append("line-height: "+ LINE_HEIGHT +"px;\n");
188 inlineStyle.append("padding: .4em;\n");
189 inlineStyle.append("width: 2.2em;\n");
190 inlineStyle.append("</style>\n");
191 out.write(inlineStyle.toString());
192
193 out.write("<div class=\"editorWrapper\" style=\"border: 1px solid #999; padding: 3px;\">");
194 out.write(control.getHtml());
195
196 final boolean lineNumbers = BooleanUtil.toBoolean(this.getConfigValue("lineNumbers"), true);
197 final boolean readOnly = BooleanUtil.toBoolean(this.getConfigValue("readOnly"), false);
198
199 final String editorVar = "editor" + this.getName();
200
201 out.write("\n<script>\n");
202 out.write("MgnlDHTMLUtil.addOnLoad(function(){\n");
203 String codeMirrorEditor = editorVar +" = CodeMirror.fromTextArea(\""+this.getName()+"\", {\n"+
204 " path: \"" + pathToCodeMirror + "\",\n" +
205 " textWrapping: false,\n" +
206 " height: \"" + height + "px\",\n" +
207 " basefiles: [\"codemirror-base.min.js\"],\n" +
208 " parserfile: [\"allinone.js\"],\n" +
209 " stylesheet: [\""+ pathToCodeMirror +"css/jscolors.css\",\""+ pathToCodeMirror +"css/csscolors.css\",\""+
210 pathToCodeMirror +"css/xmlcolors.css\",\""+ pathToCodeMirror +"css/freemarkercolors.css\",\""+ pathToCodeMirror +"css/groovycolors.css\"],\n" +
211 (lineNumbers ? " lineNumbers:true,\n":"") +
212 (readOnly ? " readOnly:true,\n":"") +
213 " initCallback:function(e){ \n"+
214 " e.setParser('"+ parser +"');\n"+
215 " e.focus();\n"+
216 " } \n" +
217 "});\n";
218
219 out.write(codeMirrorEditor);
220
221 out.write(" var b = document.getElementById('mgnlSaveButton');\n");
222 out.write(" var existingOnClick = b.onclick;\n");
223 out.write(" b.onclick=function(){\n");
224
225 out.write(" document.getElementById('cm_hidden_"+ this.getName() + "').value = " + editorVar + ".getCode();\n");
226 out.write(" existingOnClick.apply(this);\n");
227 out.write(" }\n});\n");
228 out.write("</script>\n");
229 out.write("<input type=\"hidden\" name=\"" + this.getName() + "\" id=\"cm_hidden_" + this.getName() + "\" />\n");
230 out.write("</div><!-- closing editorWrapper -->\n");
231
232 this.drawHtmlPost(out);
233 }
234
235 private String getAvailableParser(String language){
236 final String parser = availableParsers.get(language);
237 log.debug("language is {}, parser is {}", language, parser);
238 return parser;
239 }
240 }