All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wicketstuff.tinymce6.TinyMceBehavior Maven / Gradle / Ivy

/*
    This file is part of Wicket-Contrib-TinyMce. See
    

    Wicket-Contrib-TinyMce is free software: you can redistribute it and/
    or modify it under the terms of the GNU Lesser General Public License
    as published by the Free Software Foundation, either version 3 of the
    License, or (at your option) any later version.

    Wicket-Contrib-TinyMce is distributed in the hope that it will be
    useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with Wicket-Contrib-TinyMce.  If not, see
    .
 */
package org.wicketstuff.tinymce6;

import java.util.Collection;
import java.util.Collections;
import java.util.stream.Collectors;

import org.apache.wicket.Application;
import org.apache.wicket.Component;
import org.apache.wicket.RuntimeConfigurationType;
import org.apache.wicket.behavior.Behavior;
import org.apache.wicket.markup.head.HeaderItem;
import org.apache.wicket.markup.head.IHeaderResponse;
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
import org.apache.wicket.markup.head.OnDomReadyHeaderItem;
import org.apache.wicket.markup.html.IHeaderContributor;
import org.apache.wicket.request.resource.ResourceReference;
import org.wicketstuff.tinymce6.settings.TinyMCESettings;

/**
 * Renders a component (textarea) as WYSIWYG editor, using TinyMce.
 */
public class TinyMceBehavior extends Behavior
{
	private static final long serialVersionUID = 3L;

	private Component component;
	private final TinyMCESettings settings;
	private boolean rendered = false;

	public TinyMceBehavior()
	{
		this(new TinyMCESettings());
	}

	public TinyMceBehavior(TinyMCESettings settings)
	{
		this.settings = settings;
	}

	@Override
	public void renderHead(Component component, IHeaderResponse response)
	{
		// FIXME Problem when a previously rendered Tinymce textarea is made invisibale again, there
		// is no way to destroy the tinymce instance.

		super.renderHead(component, response);
		if (component == null)
		{
			throw new IllegalStateException("TinyMceBehavior is not bound to a component");
		}

		// TinyMce javascript:
		response.render(JavaScriptHeaderItem.forReference(getTinyMCEReference()));

		String renderOnDomReady = getAddTinyMceSettingsScript(Collections.singletonList(component));
		response.render(wrapTinyMceSettingsScript(renderOnDomReady, component));
	}

	/**
	 * Wrap the initialization script for TinyMCE into a HeaderItem. In this way we can control when
	 * and how the script should be executed.
	 *
	 * @param settingScript
	 *            the actual initialization script for TinyMCE
	 * @param component
	 *            the target component that must be decorated with TinyMCE
	 * @return the HeaderItem containing {@paramref settingScript}
	 */
	protected HeaderItem wrapTinyMceSettingsScript(String settingScript, Component component)
	{
		return OnDomReadyHeaderItem.forScript(settingScript);
	}

	protected String getAddTinyMceSettingsScript(Collection components)
	{
		final var script = new StringBuilder();
		// If this behavior is run a second time, it means we're redrawing this
		// component via an ajax call. The tinyMCE javascript does not handle
		// this scenario, so we must remove the old editor before initializing
		// it again.
		if (rendered)
		{
			for (Component c : components)
			{
				String tryToRemoveJS = "try {let editor = tinymce.get('%s'); if (editor) { editor.destroy(); }} catch (e) { console.debug(e); }\n;";
				script.append(String.format(tryToRemoveJS, c.getMarkupId()));
			}
		}

		script
			.append(";tinyMCE.init({").append(settings.toJavaScript(components.stream()
				.filter(c -> c.isVisibleInHierarchy()).collect(Collectors.toList())))
			.append(" });\n");
		rendered = true;

		return script.toString();
	}

	@Override
	public void bind(Component component)
	{
		if (this.component != null)
		{
			throw new IllegalStateException(
				"TinyMceBehavior can not bind to more than one component");
		}
		super.bind(component);
		if (isMarkupIdRequired())
		{
			component.setOutputMarkupId(true);
		}
		this.component = component;
	}

	protected boolean isMarkupIdRequired()
	{
		return true;
	}

	protected Component getComponent()
	{
		return component;
	}

	/**
	 * 

* TinyMCE javascript resource. *

*

* Note: The TinyMCE source cannot be lazily loaded via ajax. Therefore, adding * this in a {@link IHeaderContributor#renderHead(IHeaderResponse)} must be done in a component * that is not rendered via Ajax. *

* * @return Tinymce JS Resource Reference */ protected ResourceReference getTinyMCEReference() { Application app = Application.get(); if (RuntimeConfigurationType.DEVELOPMENT.equals(app.getConfigurationType())) { return TinyMCESettings.TINYMCE_JS_REF; } return TinyMCESettings.TINYMCE_JS_REF_MIN; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy