001/*
002 * This library is part of OpenCms -
003 * the Open Source Content Management System
004 *
005 * Copyright (c) Alkacon Software GmbH & Co. KG (http://www.alkacon.com)
006 *
007 * This library is free software; you can redistribute it and/or
008 * modify it under the terms of the GNU Lesser General Public
009 * License as published by the Free Software Foundation; either
010 * version 2.1 of the License, or (at your option) any later version.
011 *
012 * This library is distributed in the hope that it will be useful,
013 * but WITHOUT ANY WARRANTY; without even the implied warranty of
014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
015 * Lesser General Public License for more details.
016 *
017 * For further information about Alkacon Software, please see the
018 * company website: http://www.alkacon.com
019 *
020 * For further information about OpenCms, please see the
021 * project website: http://www.opencms.org
022 *
023 * You should have received a copy of the GNU Lesser General Public
024 * License along with this library; if not, write to the Free Software
025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
026 */
027
028package org.opencms.ui.components.codemirror;
029
030import org.opencms.i18n.CmsMessages;
031import org.opencms.json.JSONObject;
032import org.opencms.ui.components.Messages;
033import org.opencms.workplace.CmsWorkplace;
034
035import java.lang.reflect.Method;
036import java.util.Arrays;
037import java.util.HashSet;
038import java.util.Set;
039
040import com.vaadin.annotations.JavaScript;
041import com.vaadin.event.FieldEvents;
042import com.vaadin.shared.ui.JavaScriptComponentState;
043import com.vaadin.ui.AbstractJavaScriptComponent;
044import com.vaadin.ui.Button;
045import com.vaadin.ui.Component;
046import com.vaadin.ui.JavaScriptFunction;
047import com.vaadin.ui.UI;
048import com.vaadin.v7.data.Property;
049
050import elemental.json.JsonArray;
051
052/**
053 * Code mirror input component.<p>
054 */
055@JavaScript({"codemirror-connector.js"})
056public class CmsCodeMirror extends AbstractJavaScriptComponent
057implements Property<String>, Property.ValueChangeNotifier {
058
059    /** The available editor languages. */
060    public enum CodeMirrorLanguage {
061        /** CSS. */
062        CSS("css", new String[] {"css"}),
063        /** HTML. */
064        HTML("text/html", new String[] {"html", "htm", "xhtml"}),
065        /** JAVA. */
066        JAVA("text/x-java", new String[] {"java"}),
067        /** JavaScript. */
068        JAVASCRIPT("javascript", new String[] {"js"}),
069        /** JSP. */
070        JSP("application/x-jsp", new String[] {"jsp"}),
071        /** XML. */
072        XML("application/xml", new String[] {"xml", "xsd"});
073
074        /** The language name. */
075        private final String m_languageName;
076
077        /** The supported file types. */
078        private Set<String> m_supportedFileTypes;
079
080        /**
081         * Constructor.<p>
082         *
083         * @param languageName the language name
084         * @param fileTypes the file types suported by this language
085         */
086        private CodeMirrorLanguage(String languageName, String[] fileTypes) {
087
088            m_languageName = languageName;
089            m_supportedFileTypes = new HashSet<String>(Arrays.asList(fileTypes));
090        }
091
092        /**
093         * Returns the language name.<p>
094         *
095         * @return the language name
096         */
097        public String getLanguageName() {
098
099            return m_languageName;
100        }
101
102        /**
103         * Returns whether the given file ending is a supported file type.<p>
104         *
105         * @param fileNameSuffix the file name suffix
106         *
107         * @return <code>true</code> in case the file type is supported
108         */
109        public boolean isSupportedFileType(String fileNameSuffix) {
110
111            return m_supportedFileTypes.contains(fileNameSuffix);
112        }
113
114        /**
115         * @see java.lang.Enum#toString()
116         */
117        @Override
118        public String toString() {
119
120            return m_languageName;
121        }
122    }
123
124    /**
125     * The editor state.<p>
126     */
127    public static class CodeMirrorState extends JavaScriptComponentState {
128
129        /** The serial version id. */
130        private static final long serialVersionUID = 1L;
131
132        /** The close brackets flag. */
133        public boolean m_closeBrackets = true;
134
135        /** The content value. */
136        public String m_contentValue = "//please start to code";
137
138        /** The required CSS stylesheet URIs. */
139        public String[] m_cssURIs;
140
141        /** The search and replace enabled flag. */
142        public boolean m_enableSearchReplace;
143
144        /** The undo redo enabled flag. */
145        public boolean m_enableUndoRedo;
146
147        /** The font size. */
148        public String m_fontSize = "14px";
149
150        /** The height. */
151        public String m_height = "600";
152
153        /** The highlighting flag. */
154        public boolean m_highlighting = true;
155
156        /** The editor instance id. */
157        public long m_id;
158
159        /** The line wrapping flag. */
160        public boolean m_lineWrapping = true;
161
162        /** The language mode. */
163        public String m_mode = CodeMirrorLanguage.JAVASCRIPT.toString();
164
165        /** The required JavaScript resource URIs. */
166        public String[] m_scriptURIs;
167
168        /** The short cut descriptions HTML. */
169        public String m_shortcutsMessage;
170
171        /** The tab visibility flag. */
172        public boolean m_tabsVisible = true;
173
174        /** The theme. */
175        public String m_theme = CodeMirrorTheme.ECLIPSE.toString();
176
177        /** The width. */
178        public String m_width = "600";
179
180        /** The code mirror localization. */
181        public String m_messages;
182    }
183
184    /** The available editor themes. */
185    public enum CodeMirrorTheme {
186        /** Theme. */
187        AMBIANCE("ambiance"),
188        /** Theme. */
189        BASE16_DARK("base16-dark"),
190        /** Theme. */
191        BASE16_LIGHT("base16-light"),
192        /** Theme. */
193        BLACKBOARD("blackboard"),
194        /** Theme. */
195        COBALT("cobalt"),
196        /** Theme. */
197        DAY_3024("3024-day"),
198        /** Theme. */
199        DEFAULT("default"),
200        /** Theme. */
201        ECLIPSE("eclipse"),
202        /** Theme. */
203        ELEGANT("elegant"),
204        /** Theme. */
205        ERLANG_DARK("erlang-dark"),
206        /** Theme. */
207        ERLANG_LIGHT("erlang-light"),
208        /** Theme. */
209        LESSER_DARK("lesser-dark"),
210        /** Theme. */
211        MBO("mbo"),
212        /** Theme. */
213        MDN_LIKE("mdn-like"),
214        /** Theme. */
215        MIDNIGHT("midnight"),
216        /** Theme. */
217        MONOKAI("monokai"),
218        /** Theme. */
219        NEAT("neat"),
220        /** Theme. */
221        NIGHT("night"),
222        /** Theme. */
223        NIGHT_3024("3024-night"),
224        /** Theme. */
225        PARAISO_DARK("paraiso-dark"),
226        /** Theme. */
227        PARAISO_LIGHT("paraiso-light"),
228        /** Theme. */
229        PASTEL_ON_DARK("paster-on-dark"),
230        /** Theme. */
231        RUBYBLUE("rubyblue"),
232        /** Theme. */
233        SOLARIZED_DARK("solarized dark"),
234        /** Theme. */
235        SOLARIZED_LIGHT("solarized light"),
236        /** Theme. */
237        THE_MATRIX("the-matrix"),
238        /** Theme. */
239        TOMORROW_NIGHT_EIGHTIES("tomorrow-night-eighties"),
240        /** Theme. */
241        TWILIGHT("twilight"),
242        /** Theme. */
243        VIBRANT_INK("vibrant-ink"),
244        /** Theme. */
245        XQ_DARK("xq-dark"),
246        /** Theme. */
247        XQ_LIGHT("xq-light");
248
249        /** The theme name. */
250        private final String m_themeName;
251
252        /**
253         * Constructor.<p>
254         *
255         * @param themeName the theme name
256         */
257        private CodeMirrorTheme(String themeName) {
258
259            this.m_themeName = themeName;
260        }
261
262        /**
263         * Returns the theme name.<p>
264         *
265         * @return the theme name
266         */
267        public String getThemeName() {
268
269            return m_themeName;
270        }
271
272        /**
273         * @see java.lang.Enum#toString()
274         */
275        @Override
276        public String toString() {
277
278            return m_themeName;
279        }
280
281    }
282
283    /**
284     * The code mirror change event.<p>
285     */
286    public static class ValueChangeEvent extends Component.Event implements Property.ValueChangeEvent {
287
288        /** The serial version id. */
289        private static final long serialVersionUID = 1L;
290
291        /**
292         * Constructs a new event object with the specified source field object.
293         *
294         * @param eventSource the field that caused the event.
295         */
296        public ValueChangeEvent(CmsCodeMirror eventSource) {
297
298            super(eventSource);
299        }
300
301        /**
302         * Gets the Property which triggered the event.
303         *
304         * @return the Source Property of the event.
305         */
306        @Override
307        public Property getProperty() {
308
309            return (Property<?>)getSource();
310        }
311    }
312
313    /** The blur method. */
314    private static final Method BLUR_METHOD;
315
316    /** The required CSS stylesheet URIs. */
317    private static String[] CSS_URIS;
318
319    /** The HTML id prefix. */
320    private static final String HTML_ID_PREFIX = "cm-addon-";
321
322    /** The required JavaScript resource URIs. */
323    private static String[] JAVASCRIPT_URIS;
324
325    /** The code mirror component count, used as HTML ids. */
326    private static long m_componentCount;
327
328    /** The serial version id. */
329    private static final long serialVersionUID = -4921119175861329688L;
330
331    /** Them value change method. */
332    private static final Method VALUE_CHANGE_METHOD;
333
334    static {
335        try {
336            VALUE_CHANGE_METHOD = Property.ValueChangeListener.class.getDeclaredMethod(
337                "valueChange",
338                new Class[] {Property.ValueChangeEvent.class});
339        } catch (final java.lang.NoSuchMethodException e) {
340            // This should never happen
341            throw new java.lang.RuntimeException("Internal error finding methods in ValueChangeListener");
342        }
343
344        try {
345            BLUR_METHOD = FieldEvents.BlurListener.class.getDeclaredMethod(
346                "blur",
347                new Class[] {FieldEvents.BlurEvent.class});
348        } catch (final java.lang.NoSuchMethodException e) {
349            // This should never happen
350            throw new java.lang.RuntimeException("Internal error finding methods in ValueChangeListener");
351        }
352    }
353
354    /** The current content value. */
355    private String m_codeValue;
356
357    /** The component id. */
358    private final long m_componentId;
359
360    /**
361     * Constructor.<p>
362     */
363    public CmsCodeMirror() {
364
365        m_componentId = m_componentCount;
366        m_componentCount++;
367        m_codeValue = "";
368        getState().m_id = m_componentId;
369        getState().m_contentValue = m_codeValue;
370        CmsMessages messages = Messages.get().getBundle(UI.getCurrent().getLocale());
371        getState().m_shortcutsMessage = getShortcutMessages(messages);
372        getState().m_messages = getLocalizationMessages(messages);
373
374        if (CSS_URIS == null) {
375            CSS_URIS = new String[] {
376                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/lib/codemirror.css"),
377                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/theme/eclipse.css"),
378                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/dialog/dialog.css"),
379                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/hint/show-hint.css")};
380        }
381        getState().m_cssURIs = CSS_URIS;
382        if (JAVASCRIPT_URIS == null) {
383            JAVASCRIPT_URIS = new String[] {
384                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/lib/codemirror.js"),
385                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/dialog/dialog.js"),
386                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/search/searchcursor.js"),
387                CmsWorkplace.getStaticResourceUri("/editors/codemirror/js/search.js"),
388                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/edit/closebrackets.js"),
389                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/edit/closetag.js"),
390                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/edit/matchbrackets.js"),
391                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/hint/show-hint.js"),
392                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/hint/html-hint.js"),
393                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/hint/javascript-hint.js"),
394                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/hint/xml-hint.js"),
395                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/fold/foldcode.js"),
396                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/fold/brace-fold.js"),
397                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/fold/xml-fold.js"),
398                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/comment/comment.js"),
399                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/addon/selection/active-line.js"),
400                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/mode/css/css.js"),
401                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/mode/xml/xml.js"),
402                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/mode/clike/clike.js"),
403                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/mode/javascript/javascript.js"),
404                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/mode/css/css.js"),
405                CmsWorkplace.getStaticResourceUri("/editors/codemirror/dist/mode/htmlmixed/htmlmixed.js"),
406                CmsWorkplace.getStaticResourceUri("/editors/codemirror/js/htmlembedded_modified.js")};
407        }
408        getState().m_scriptURIs = JAVASCRIPT_URIS;
409
410        addFunction("onBlur", new JavaScriptFunction() {
411
412            private static final long serialVersionUID = 1L;
413
414            @Override
415            public void call(JsonArray arguments) {
416
417                onBlur(arguments.getString(0));
418            }
419        });
420        addFunction("onChange", new JavaScriptFunction() {
421
422            private static final long serialVersionUID = 1L;
423
424            @Override
425            public void call(JsonArray arguments) {
426
427                onChange(arguments.getString(0));
428            }
429        });
430        addStyleName("o-codemirror");
431    }
432
433    /**
434     * Adds a blur listener.<p>
435     *
436     * @param listener the blur listener
437     */
438    public void addBlurListener(FieldEvents.BlurListener listener) {
439
440        addListener(FieldEvents.BlurEvent.class, listener, BLUR_METHOD);
441        markAsDirty();
442    }
443
444    /**
445     * @see com.vaadin.v7.data.Property.ValueChangeNotifier#addListener(com.vaadin.v7.data.Property.ValueChangeListener)
446     * @deprecated use {@link #addValueChangeListener(com.vaadin.v7.data.Property.ValueChangeListener)}
447     */
448    @Deprecated
449    public void addListener(com.vaadin.v7.data.Property.ValueChangeListener listener) {
450
451        addValueChangeListener(listener);
452    }
453
454    /**
455     * @see com.vaadin.v7.data.Property.ValueChangeNotifier#addValueChangeListener(com.vaadin.v7.data.Property.ValueChangeListener)
456     */
457    public void addValueChangeListener(Property.ValueChangeListener listener) {
458
459        addListener(Property.ValueChangeEvent.class, listener, VALUE_CHANGE_METHOD);
460        // ensure "automatic immediate handling" works
461        markAsDirty();
462    }
463
464    /**
465     * Returns the font size.<p>
466     *
467     * @return the font size
468     */
469    public String getFontSize() {
470
471        return getState().m_fontSize;
472    }
473
474    /**
475     * @see com.vaadin.v7.data.Property#getType()
476     */
477    public Class<? extends String> getType() {
478
479        return String.class;
480    }
481
482    /**
483     * Returns the current editor content value.<p>
484     *
485     * @return the editor content value
486     */
487    public String getValue() {
488
489        return m_codeValue;
490    }
491
492    /**
493     * Returns whether auto close brackets is active.<p>
494     *
495     * @return <code>true</code> if auto close brackets is active
496     */
497    public boolean isCloseBrackets() {
498
499        return getState().m_closeBrackets;
500    }
501
502    /**
503     * Returns whether highlighting is active.<p>
504     *
505     * @return <code>true</code> if highlighting is active
506     */
507    public boolean isHighlighting() {
508
509        return getState().m_highlighting;
510    }
511
512    /**
513     * Returns whether line wrapping is active.<p>
514     *
515     * @return <code>true</code> if line wrapping is active
516     */
517    public boolean isLineWrapping() {
518
519        return getState().m_lineWrapping;
520    }
521
522    /**
523     * @see com.vaadin.ui.AbstractComponent#isReadOnly()
524     */
525    @Override
526    public boolean isReadOnly() {
527
528        return super.isReadOnly();
529    }
530
531    /**
532     * Returns whether tabs are visible.<p>
533     *
534     * @return <code>true</code> if tabs are visible
535     */
536    public boolean isTabsVisible() {
537
538        return getState().m_tabsVisible;
539    }
540
541    /**
542     * Registers the given buttons as the search and replace buttons.<p>
543     *
544     * @param search the search button
545     * @param replace the replace button
546     */
547    public void registerSearchReplace(Button search, Button replace) {
548
549        if (getState().m_enableSearchReplace) {
550            throw new RuntimeException("Search/replace already registered.");
551        }
552        search.setId(HTML_ID_PREFIX + m_componentId + "-search");
553        replace.setId(HTML_ID_PREFIX + m_componentId + "-replace");
554        getState().m_enableSearchReplace = true;
555        markAsDirty();
556    }
557
558    /**
559     * Registers the given buttons as undo redo buttons.<p>
560     *
561     * @param undo the undo button
562     * @param redo the redo button
563     */
564    public void registerUndoRedo(Button undo, Button redo) {
565
566        if (getState().m_enableUndoRedo) {
567            throw new RuntimeException("Undo/redo already registered.");
568        }
569        undo.setId(HTML_ID_PREFIX + m_componentId + "-undo");
570        redo.setId(HTML_ID_PREFIX + m_componentId + "-redo");
571        getState().m_enableUndoRedo = true;
572        markAsDirty();
573    }
574
575    /**
576     * Removes the given blur listener.<p>
577     *
578     * @param listener the listener to remove
579     */
580    public void removeBlurListener(FieldEvents.BlurNotifier listener) {
581
582        removeListener(FieldEvents.BlurEvent.class, listener, BLUR_METHOD);
583        // ensure "automatic immediate handling" works
584        markAsDirty();
585    }
586
587    /**
588     * @see com.vaadin.v7.data.Property.ValueChangeNotifier#removeListener(com.vaadin.v7.data.Property.ValueChangeListener)
589     * @deprecated use {@link #removeValueChangeListener(com.vaadin.v7.data.Property.ValueChangeListener)}
590     */
591    @Deprecated
592    public void removeListener(com.vaadin.v7.data.Property.ValueChangeListener listener) {
593
594        removeValueChangeListener(listener);
595    }
596
597    /**
598     * @see com.vaadin.v7.data.Property.ValueChangeNotifier#removeValueChangeListener(com.vaadin.v7.data.Property.ValueChangeListener)
599     */
600    public void removeValueChangeListener(Property.ValueChangeListener listener) {
601
602        removeListener(Property.ValueChangeEvent.class, listener, VALUE_CHANGE_METHOD);
603        markAsDirty();
604    }
605
606    /**
607     * Sets the auto close brackets feature enabled.<p>
608     *
609     * @param closeBrackets <code>true</code> to auto close brackets
610     */
611    public void setCloseBrackets(boolean closeBrackets) {
612
613        getState().m_closeBrackets = closeBrackets;
614        markAsDirty();
615    }
616
617    /**
618     * Sets the editor font size.<p>
619     *
620     * @param fontSize the editor font size
621     */
622    public void setFontSize(String fontSize) {
623
624        getState().m_fontSize = fontSize;
625        markAsDirty();
626    }
627
628    /**
629     * @see com.vaadin.ui.AbstractComponent#setHeight(float, com.vaadin.server.Sizeable.Unit)
630     */
631    @Override
632    public void setHeight(float height, Unit unit) {
633
634        super.setHeight(height, unit);
635        getState().m_height = height + unit.getSymbol();
636        markAsDirty();
637    }
638
639    /**
640     * @see com.vaadin.ui.AbstractComponent#setHeight(java.lang.String)
641     */
642    @Override
643    public void setHeight(String height) {
644
645        super.setHeight(height);
646        getState().m_height = height;
647        markAsDirty();
648    }
649
650    /**
651     * Sets the language highlighting enabled.<p>
652     *
653     * @param highlighting <code>true</code> to highlight
654     */
655    public void setHighlighting(boolean highlighting) {
656
657        getState().m_highlighting = highlighting;
658        markAsDirty();
659    }
660
661    /**
662     * Sets the editor language.<p>
663     *
664     * @param codeMirrorLanguage the editor language
665     */
666    public void setLanguage(CodeMirrorLanguage codeMirrorLanguage) {
667
668        getState().m_mode = codeMirrorLanguage.getLanguageName();
669        markAsDirty();
670    }
671
672    /**
673     * Sets line wrapping enabled.<p>
674     *
675     * @param lineWrapping <code>true</code> to wrap lines
676     */
677    public void setLineWrapping(boolean lineWrapping) {
678
679        getState().m_lineWrapping = lineWrapping;
680        markAsDirty();
681    }
682
683    /**
684     * @see com.vaadin.ui.AbstractComponent#setReadOnly(boolean)
685     */
686    @Override
687    public void setReadOnly(boolean readOnly) {
688
689        super.setReadOnly(readOnly);
690    }
691
692    /**
693     * Sets tab characters visible.<p>
694     *
695     * @param tabsVisible <code>true</code> to show tab characters
696     */
697    public void setTabsVisible(boolean tabsVisible) {
698
699        getState().m_tabsVisible = tabsVisible;
700        markAsDirty();
701    }
702
703    /**
704     * Sets the editor theme.<p>
705     *
706     * @param codeMirrorTheme the editor theme
707     */
708    public void setTheme(CodeMirrorTheme codeMirrorTheme) {
709
710        //   getState().m_id = m_componentId;
711        getState().m_theme = codeMirrorTheme.getThemeName();
712        markAsDirty();
713    }
714
715    /**
716     * Sets the editor content value.<p>
717     *
718     * @param value the content value
719     */
720    public void setValue(String value) {
721
722        m_codeValue = value;
723
724        getState().m_contentValue = value;
725        markAsDirty();
726    }
727
728    /**
729     * @see com.vaadin.ui.AbstractComponent#setWidth(float, com.vaadin.server.Sizeable.Unit)
730     */
731    @Override
732    public void setWidth(float width, Unit unit) {
733
734        super.setWidth(width, unit);
735        getState().m_width = width + unit.getSymbol();
736        markAsDirty();
737    }
738
739    /**
740     * @see com.vaadin.ui.AbstractComponent#setWidth(java.lang.String)
741     */
742    @Override
743    public void setWidth(String width) {
744
745        super.setWidth(width);
746        getState().m_width = width;
747        markAsDirty();
748    }
749
750    /**
751     * @see com.vaadin.ui.AbstractJavaScriptComponent#getState()
752     */
753    @Override
754    protected CodeMirrorState getState() {
755
756        return (CodeMirrorState)super.getState();
757    }
758
759    /**
760     * Called on blur.<p>
761     *
762     * @param value the content value
763     */
764    void onBlur(String value) {
765
766        m_codeValue = value;
767        fireEvent(new FieldEvents.BlurEvent(CmsCodeMirror.this));
768    }
769
770    /**
771     * Called on blur.<p>
772     *
773     * @param value the content value
774     */
775    void onChange(String value) {
776
777        boolean changed = !m_codeValue.equals(value);
778        m_codeValue = value;
779        if (changed) {
780            fireEvent(new ValueChangeEvent(this));
781        }
782    }
783
784    /**
785     * Returns the code mirror localization JSON.<p>
786     *
787     * @param messages the message bundle to use
788     *
789     * @return the localization
790     */
791    private String getLocalizationMessages(CmsMessages messages) {
792
793        JSONObject result = new JSONObject();
794        try {
795            result.put("search", messages.key(Messages.GUI_CODEMIRROR_LANG_SEARCH_0));
796            result.put("hint", messages.key(Messages.GUI_CODEMIRROR_LANG_HINT_0));
797            result.put("replace", messages.key(Messages.GUI_CODEMIRROR_LANG_REPLACE_0));
798            result.put("replacewith", messages.key(Messages.GUI_CODEMIRROR_LANG_REPLACE_WITH_0));
799            result.put("replaceconfirm", messages.key(Messages.GUI_CODEMIRROR_LANG_REPLACE_CONFIRM_0));
800            result.put("replaceyes", messages.key(Messages.GUI_CODEMIRROR_LANG_REPLACE_YES_0));
801            result.put("replaceno", messages.key(Messages.GUI_CODEMIRROR_LANG_RELACE_NO_0));
802            result.put("replacestop", messages.key(Messages.GUI_CODEMIRROR_LANG_REPLACE_STOP_0));
803
804            result.put("fontsize", messages.key(Messages.GUI_CODEMIRROR_LANG_FONT_SIZE_0));
805        } catch (org.opencms.json.JSONException e) {
806            // should never happen
807        }
808        return result.toString();
809    }
810
811    /**
812     * Returns the localized short cut messages HTML.<p>
813     *
814     * @param messages the message bundle to use
815     *
816     * @return the HTML
817     */
818    private String getShortcutMessages(CmsMessages messages) {
819
820        StringBuffer buffer = new StringBuffer();
821        buffer.append("<span class=\"col1\"><b>").append(messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_SEARCH_0));
822        buffer.append("</b> ").append(messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_SEARCH_HELP_0));
823        buffer.append(" </span><span class=\"col2\"><b>").append(
824            messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_FIND_NEXT_0));
825        buffer.append("</b> ").append(messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_FIND_NEXT_HELP_0));
826        buffer.append(" </span><span class=\"col3\"><b>").append(
827            messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_FIND_PREVIOUS_0));
828        buffer.append("</b> ").append(messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_FIND_PREVIOUS_HELP_0));
829        buffer.append(" </span><span class=\"col1\"><b>").append(
830            messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_REPLACE_0));
831        buffer.append("</b> ").append(messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_REPLACE_HELP_0));
832        buffer.append(" </span><span class=\"col2\"><b>").append(
833            messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_REPLACE_ALL_0));
834        buffer.append("</b> ").append(messages.key(Messages.GUI_CODEMIRROR_SHORTCUT_REPLACE_ALL_HELP_0)).append(
835            "</span>");
836        return buffer.toString();
837    }
838}