SunEditor is a lightweight, flexible, customizable WYSIWYG text editor for your web applications.
Based on SunEditor by Yi JiHong.
With the default theme auto the editor follows the color scheme of the page.
A WYSIWYG editor with a configurable toolbar. Change the mode, theme, locale and direction with AJAX; the paste, copy, cut, drop and save events are sent to the server. The theme Auto picks the light or dark theme from the page.
A popular use case is an editor inside a PrimeFaces dialog. This example is for both demonstration and testing.
Use the extender attribute to extend the editor, here with the KaTeX math plugin (KaTeX CSS and JavaScript must be loaded).
this is the widget and this.cfg its JSON configuration.
<div class="card">
<h5>Basic Usage</h5>
<p>
A WYSIWYG editor with a configurable toolbar. Change the mode, theme, locale and direction with AJAX; the paste, copy, cut,
drop and save events are sent to the server. The theme <i>Auto</i> picks the light or dark theme from the page.
</p>
<h:form id="formBasicUsage">
<p:messages id="messages" showSummary="true" showDetail="true"/>
<h:panelGrid columns="8" id="pnlDetails" styleClass="p-4">
<p:outputLabel value="Mode:" for="@next" styleClass="mr-2"/>
<p:selectOneMenu id="cboMode" value="#{basicSunEditorController.mode}" styleClass="w-12rem mr-2"
required="true">
<f:selectItems value="#{basicSunEditorController.modes}"/>
<p:ajax update="sunEditor"/>
</p:selectOneMenu>
<p:outputLabel value="Theme:" for="@next" styleClass="mr-2"/>
<p:selectOneMenu id="cboTheme" value="#{basicSunEditorController.theme}" styleClass="w-12rem mr-2"
required="true">
<f:selectItems value="#{basicSunEditorController.themes}"/>
<p:ajax update="sunEditor"/>
</p:selectOneMenu>
<p:outputLabel value="Locale:" for="@next" styleClass="mr-2"/>
<p:selectOneMenu id="cboLocale" value="#{basicSunEditorController.language}" styleClass="w-12rem mr-2"
required="true">
<f:selectItems value="#{basicSunEditorController.languages}"/>
<p:ajax update="sunEditor"/>
</p:selectOneMenu>
<p:outputLabel value="RTL:" for="@next" styleClass="mr-2"/>
<p:selectBooleanCheckbox id="chkRtl" value="#{basicSunEditorController.rtl}" styleClass="mr-2">
<p:ajax update="sunEditor"/>
</p:selectBooleanCheckbox>
</h:panelGrid>
<pe:sunEditor id="sunEditor" widgetVar="sunEditorBasic" value="#{basicSunEditorController.html}"
mode="#{basicSunEditorController.mode}" dir="#{basicSunEditorController.rtl ? 'rtl' : 'ltr'}"
theme="#{basicSunEditorController.theme}" locale="#{basicSunEditorController.language}"
toolbar="[['font','fontSize','blockStyle'],['paragraphStyle','blockquote'], ['bold', 'underline', 'italic', 'strike', 'subscript', 'superscript', 'removeFormat'],
'/'
,['fontColor', 'backgroundColor', 'outdent', 'indent', 'align', 'hr', 'list', 'table'],
['link', 'image', 'audio', 'video', 'fullScreen', 'showBlocks', 'codeView', 'preview', 'exportPDF', 'save']]">
<p:ajax event="paste" listener="#{basicSunEditorController.onPaste}" update="messages"/>
<p:ajax event="copy" listener="#{basicSunEditorController.onCopy}" update="messages"/>
<p:ajax event="cut" listener="#{basicSunEditorController.onCut}" update="messages"/>
<p:ajax event="drop" listener="#{basicSunEditorController.onDrop}" update="messages"/>
<p:ajax event="save" listener="#{basicSunEditorController.onSave}" update="@this messages"/>
<f:attribute name="widgetPostConstruct" value="widget.editor.onPaste = function (e, cleanData, maxCharCount) {console.log('onpaste event detected!'); };"/>
</pe:sunEditor>
</h:form>
</div>
<div class="card">
<h5>Dialog</h5>
<p>A popular use case is an editor inside a PrimeFaces dialog. This example is for both demonstration and testing.</p>
<h:form id="formDialog">
<p:messages id="messages" showSummary="true" showDetail="true"/>
<h:panelGrid columns="6" id="pnlDetails" styleClass="p-4">
<p:commandButton value="Open Dialog" type="button" icon="pi pi-external-link" onclick="PF('dlgSunEditor').show()"/>
</h:panelGrid>
<p:dialog header="Header" widgetVar="dlgSunEditor" minHeight="40" width="60vw" showEffect="fade" closable="true" closeOnEscape="true" modal="true">
<pe:sunEditor id="sunEditor" widgetVar="sunEditorDialog" value="#{basicSunEditorController.htmlDialog}"
mode="classic" dir="#{basicSunEditorController.rtl ? 'rtl' : 'ltr'}"
theme="#{basicSunEditorController.theme}" locale="#{basicSunEditorController.language}"
toolbar="[['font','fontSize','blockStyle'],['paragraphStyle','blockquote'], ['bold', 'underline', 'italic', 'strike', 'subscript', 'superscript', 'removeFormat'],
'/'
,['fontColor', 'backgroundColor', 'outdent', 'indent', 'align', 'hr', 'list', 'table'],
['link', 'image', 'audio', 'video', 'fullScreen', 'showBlocks', 'codeView', 'preview', 'exportPDF', 'save']]">
<p:ajax event="save" listener="#{basicSunEditorController.onSave}" update="@this messages"/>
</pe:sunEditor>
</p:dialog>
</h:form>
</div>
<div class="card">
<h5>Extender</h5>
<p>
Use the <code>extender</code> attribute to extend the editor, here with the KaTeX math plugin (KaTeX CSS and JavaScript must be loaded).
<code>this</code> is the widget and <code>this.cfg</code> its JSON configuration.
</p>
<h:form id="formExtender">
<!-- Must load Katext CSS and JS (here using OmniFaces CDN handler) -->
<h:outputStylesheet library="cdn" name="katex.css"/>
<h:outputScript library="cdn" name="katex.js"/>
<!-- Google Fonts can be used "Roboto", "Bruno Ace SC"
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin="anonymous" />
<link href="https://fonts.googleapis.com/css2?family=Bruno+Ace+SC&family=Roboto&display=swap" rel="stylesheet" />
-->
<pe:sunEditor id="sunEditor" widgetVar="sunEditorExtender" value="#{basicSunEditorController.html}"
theme="#{basicSunEditorController.theme}" extender="editorExtender" toolbar="[['font','fontSize','blockStyle'],['math']]">
</pe:sunEditor>
</h:form>
<script type="text/javascript">
function editorExtender() {
// this = widget
// this.cfg = JSON configuration
this.cfg.katex = window.katex;
this.cfg.font = ["Serif", "Sans-Serif", "Monospace", "Roboto", "Bruno Ace SC"];
this.cfg.defaultStyle = "font-family:Serif;";
};
</script>
</div>
package org.primefaces.extensions.showcase.controller.suneditor;
import java.io.Serial;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import jakarta.faces.application.FacesMessage;
import jakarta.faces.context.FacesContext;
import jakarta.faces.event.AjaxBehaviorEvent;
import jakarta.faces.model.SelectItem;
import jakarta.faces.view.ViewScoped;
import jakarta.inject.Named;
@Named
@ViewScoped
public class BasicSunEditorController implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
private String html = "<h1>SunEditor</h1><p>Lightweight, flexible, customizable <strong>WYSIWYG</strong> text editor.</p>";
private String htmlDialog = "<h1>SunEditor in Dialog</h1><p>Hover over the image below to see paragraph helpers:</p><p><img alt=\"Sample Image\" src=\"https://placehold.co/600x400/EEE/999?text=Sample+Image\" style=\"display: block; margin: 20px auto; max-width: 100%; height: auto;\"></p>";
private String mode = "classic";
private String theme = "auto";
private String language = "fr";
private boolean rtl = false;
public void addMessage(String message) {
FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(message));
}
public void onPaste(AjaxBehaviorEvent e) {
addMessage("Paste event.");
}
public void onCopy(AjaxBehaviorEvent e) {
addMessage("Copy event.");
}
public void onCut(AjaxBehaviorEvent e) {
addMessage("Cut event.");
}
public void onDrop(AjaxBehaviorEvent e) {
addMessage("Drop event.");
}
public void onSave(AjaxBehaviorEvent e) {
addMessage("Save event.");
}
public String getHtml() {
return html;
}
public void setHtml(String html) {
this.html = html;
}
public String getHtmlDialog() {
return htmlDialog;
}
public void setHtmlDialog(String htmlDialog) {
this.htmlDialog = htmlDialog;
}
public List<SelectItem> getLanguages() {
final List<SelectItem> results = new ArrayList<>();
results.add(new SelectItem("da", "Dansk (Danish)"));
results.add(new SelectItem("de", "Deutsch (German)"));
results.add(new SelectItem("en", "English (US/UK)"));
results.add(new SelectItem("es", "Español (Spanish)"));
results.add(new SelectItem("fr", "Français (French)"));
results.add(new SelectItem("fa", "Farsi (Iran)"));
results.add(new SelectItem("he", "Hebrew (Israel)"));
results.add(new SelectItem("it", "Italiano (Italian)"));
results.add(new SelectItem("ja", "Japanese"));
results.add(new SelectItem("ko", "Korean"));
results.add(new SelectItem("lv", "Latvian"));
results.add(new SelectItem("nl", "Nederlands (Dutch)"));
results.add(new SelectItem("pl", "Polski (Polish)"));
results.add(new SelectItem("pt_BR", "Português (Portuguese/Brazilian)"));
results.add(new SelectItem("ro", "Romanian"));
results.add(new SelectItem("ru", "\u0420\u0443\u0441\u0441\u043a\u0438\u0439 (Russian)"));
results.add(new SelectItem("se", "Swedish"));
results.add(new SelectItem("tr", "Turkish"));
results.add(new SelectItem("uk_ua", "\u0423\u043a\u0440\u0430\u0457\u043d\u0441\u044c\u043a\u0430 (Ukrainian)"));
results.add(new SelectItem("zh_CN", "\u4e2d\u6587 (\u7b80\u4f53) (Chinese, Simplified)"));
return results;
}
public List<SelectItem> getModes() {
final List<SelectItem> results = new ArrayList<>();
results.add(new SelectItem("classic", "Classic"));
results.add(new SelectItem("inline", "Inline"));
results.add(new SelectItem("balloon", "Balloon"));
results.add(new SelectItem("balloon-always", "Balloon (Always)"));
return results;
}
public List<SelectItem> getThemes() {
final List<SelectItem> results = new ArrayList<>();
results.add(new SelectItem("auto", "Auto"));
results.add(new SelectItem("default", "Default"));
results.add(new SelectItem("dark", "Dark"));
results.add(new SelectItem("cobalt", "Cobalt"));
results.add(new SelectItem("cream", "Cream"));
results.add(new SelectItem("midnight", "Midnight"));
return results;
}
public String getMode() {
return mode;
}
public void setMode(final String mode) {
this.mode = mode;
}
public String getTheme() {
return theme;
}
public void setTheme(final String theme) {
this.theme = theme;
}
public String getLanguage() {
return language;
}
public void setLanguage(final String language) {
this.language = language;
}
public boolean isRtl() {
return rtl;
}
public void setRtl(final boolean rtl) {
this.rtl = rtl;
}
}