/* * Christopher Deckers (chrriis@nextencia.net) * http://www.nextencia.net * * See the file "readme.txt" for information on usage and redistribution of * this file, and for a DISCLAIMER OF ALL WARRANTIES. */ package chrriis.dj.nativeswing.swtimpl.components; import java.io.File; import java.io.FileFilter; import java.io.InputStream; import java.util.Map; import javax.swing.SwingUtilities; import chrriis.common.Utils; import chrriis.common.WebServer; import chrriis.common.WebServer.HTTPRequest; import chrriis.common.WebServer.WebServerContent; import chrriis.dj.nativeswing.swtimpl.EventDispatchUtils; import chrriis.dj.nativeswing.swtimpl.Message; import chrriis.dj.nativeswing.swtimpl.NSSystemPropertySWT; import chrriis.dj.nativeswing.swtimpl.NativeComponent; import chrriis.dj.nativeswing.swtimpl.components.JHTMLEditor.JHTMLEditorImplementation; /** * @author Christopher Deckers * @author Simon Pope */ class JHTMLEditorCKeditor implements JHTMLEditorImplementation { private static final String PACKAGE_PREFIX = "/ckeditor/"; private static final String EDITOR_INSTANCE = "HTMLeditor1"; private final JHTMLEditor htmlEditor; private final String customOptions; @SuppressWarnings("unchecked") public JHTMLEditorCKeditor(JHTMLEditor htmlEditor, Map<Object, Object> optionMap) { if(getClass().getResource(PACKAGE_PREFIX + "ckeditor.js") == null) { throw new IllegalStateException("The CKEditor distribution is missing from the classpath!"); } this.htmlEditor = htmlEditor; Map<String, String> customOptionsMap = (Map<String, String>)optionMap.get(JHTMLEditor.CKEditorOptions.SET_OPTIONS_OPTION_KEY); if(customOptionsMap != null) { StringBuilder sb = new StringBuilder(); for(String key: customOptionsMap.keySet()) { String value = customOptionsMap.get(key); if(value != null && value.length() > 0) { if(sb.length() > 0) { sb.append(',' + LS); } sb.append(" " + key + ": " + value); } } customOptions = sb.length() > 0? sb.toString(): null; } else { customOptions = null; } // We want potential dialogs to actually be dialogs. htmlEditor.getWebBrowser().addWebBrowserListener(new WebBrowserAdapter() { @Override public void windowWillOpen(WebBrowserWindowWillOpenEvent e) { e.setDialogWindow(true); } }); } static final String LS = Utils.LINE_SEPARATOR; public WebServerContent getWebServerContent(final HTTPRequest httpRequest, final String resourcePath, final int instanceID) { if("index.html".equals(resourcePath)) { return new WebServerContent() { @Override public String getContentType() { int index = resourcePath.lastIndexOf('.'); return getDefaultMimeType(index == -1? null: resourcePath.substring(index)); } @Override public InputStream getInputStream() { String content = "<html>" + LS + " <head>" + LS + " <meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\"/>" + LS + " <style type=\"text/css\">" + LS + " body, form {margin: 0; padding: 0; overflow: auto;}" + LS + " </style>" + LS + " <script type=\"text/javascript\" src=\"ckeditor.js\"></script>" + LS + " <script type=\"text/javascript\">" + LS + " var sendCommand = " + JWebBrowser.COMMAND_FUNCTION + ";" + LS + " var htmlContent;" + LS + " var htmlDirtyTracker;" + LS + " var isDirtyTrackingActive = true;" + LS + " function JH_checkDirty() {" + LS + " var oEditor = CKEDITOR.instances." + EDITOR_INSTANCE + ";" + LS + " if(htmlContent == null) {" + LS + " try {" + LS + " htmlContent = oEditor.getData();" + LS + " } catch(e) {" + LS + " }" + LS + " htmlDirtyTracker = setTimeout('JH_checkDirty()', 1000);" + LS + " } else {" + LS + " try {" + LS + " var newHtmlContent = oEditor.getData();" + LS + " if(newHtmlContent != htmlContent) {" + LS + " htmlContent = null;" + LS + " htmlDirtyTracker = null;" + LS + " sendCommand('[Chrriis]JH_setDirty');" + LS + " } else {" + LS + " htmlContent = newHtmlContent;" + LS + " htmlDirtyTracker = setTimeout('JH_checkDirty()', 1000);" + LS + " }" + LS + " } catch(e) {" + LS + " htmlDirtyTracker = setTimeout('JH_checkDirty()', 1000);" + LS + " }" + LS + " }" + LS + " }" + LS + " function JH_clearDirtyIndicator() {" + LS + " if(htmlDirtyTracker) {" + LS + " clearTimeout(htmlDirtyTracker);" + LS + " }" + LS + " htmlContent = null;" + LS + " if(isDirtyTrackingActive) {" + LS + " htmlDirtyTracker = setTimeout('JH_checkDirty()', 1000);" + LS + " }" + LS + " }" + LS + " function JH_setDirtyTrackingActive(isActive) {" + LS + " isDirtyTrackingActive = isActive;" + LS + " JH_clearDirtyIndicator();" + LS + " }" + LS + " function JH_setData(html) {" + LS + " var oEditor = CKEDITOR.instances." + EDITOR_INSTANCE + ";" + LS + " oEditor.setData(decodeURIComponent(html));" + LS + " JH_clearDirtyIndicator();" + LS + " }" + LS + " function JH_sendData() {" + LS + " document.jhtml_form.action = 'jhtml_sendData';" + LS + " document.jhtml_form.submit();" + LS + " document.jhtml_form.action = 'jhtml_save';" + LS + " return false;" + LS + " }" + LS + " function JH_doSave() {" + LS + " document.jhtml_form.action = 'jhtml_save';" + LS + " document.jhtml_form.submit();" + LS + " return false;" + LS + " }" + LS + " CKEDITOR.on('instanceReady'," + LS + " function(evt) {" + LS + " var editor = evt.editor;" + LS + " editor.execCommand('maximize');" + LS + " JH_clearDirtyIndicator();" + LS + " sendCommand('[Chrriis]JH_setLoaded');" + LS + " });" + LS + " </script>" + LS + " </head>" + LS + " <body>" + LS + " <iframe style=\"display:none;\" id=\"j_iframe\" name=\"j_iframe\"></iframe>" + LS + " <form name=\"jhtml_form\" method=\"POST\" target=\"j_iframe\" action=\"jhtml_save\">" + LS + " <textarea name=\"" + EDITOR_INSTANCE + "\"><p></p></textarea>" + LS + " <script type=\"text/javascript\">" + LS + " CKEDITOR.replace('" + EDITOR_INSTANCE + "'" + (customOptions != null? "," + "{" + LS + customOptions + LS + " });": ");") + LS + " </script>" + LS + " </form>" + LS + " </body>" + LS + "</html>"; return getInputStream(content); } }; } // if("customConfigurationScript.js".equals(resourcePath)) { // return new WebServerContent () { // @Override // public String getContentType () { // return getDefaultMimeType(".js"); // } // @Override // public InputStream getInputStream () { // return getInputStream(customJavascriptConfiguration); // } // }; // } if("jhtml_save".equals(resourcePath)) { SwingUtilities.invokeLater(new Runnable() { public void run() { String html = JHTMLEditor.convertLinksToLocal(httpRequest.getHTTPPostDataArray()[0].getHeaderMap().get(EDITOR_INSTANCE)); HTMLEditorSaveEvent e = null; for(HTMLEditorListener listener: htmlEditor.getHTMLEditorListeners()) { if(e == null) { e = new HTMLEditorSaveEvent(htmlEditor, html); } listener.saveHTML(e); } } }); return new WebServerContent() { @Override public InputStream getInputStream() { String content = "<html>" + LS + " <body>" + LS + " Save successful." + LS + " </body>" + LS + "</html>" + LS; return getInputStream(content); } }; } if("jhtml_sendData".equals(resourcePath)) { String data = httpRequest.getHTTPPostDataArray()[0].getHeaderMap().get(EDITOR_INSTANCE); tempResult = data; return new WebServerContent() { @Override public InputStream getInputStream() { String content = "<html>" + LS + " <body>" + LS + " Send data successful." + LS + " </body>" + LS + "</html>" + LS; return getInputStream(content); } }; } if("editor/filemanager/connectors/php/upload.php".equals(resourcePath)) { return new WebServerContent() { @Override public InputStream getInputStream() { String content = "<html>" + LS + " <head>" + LS + " <script type=\"text/javascript\">" + LS + " alert('upload to local system are not allowed...');" + LS + " </script>" + LS + " </head>" + LS + " <body>" + LS + " Upload successful." + LS + " </body>" + LS + "</html>" + LS; return getInputStream(content); } }; } if("editor/filemanager/connectors/php/connector.php".equals(resourcePath)) { Map<String, String> queryParameterMap = httpRequest.getQueryParameterMap(); String command = queryParameterMap.get("Command"); String content = null; if("GetFoldersAndFiles".equals(command) || "GetFolders".equals(command)) { final String type = queryParameterMap.get("Type"); String currentDir = queryParameterMap.get("CurrentFolder"); File[] roots = File.listRoots(); StringBuilder sb = new StringBuilder(); sb.append("<?xml version=\"1.0\" encoding=\"utf-8\" ?>"); sb.append("<Connector command=\"").append(command).append("\" resourceType=\"").append(type).append("\">"); sb.append("<CurrentFolder path=\"").append(currentDir).append("\" url=\"").append(WebServer.getDefaultWebServer().getResourcePathURL(currentDir, "")).append("\" />"); // try { // sb.append("<CurrentFolder path=\"").append(currentDir).append("\" url=\"").append(new File(currentDir).toURI().toURL()).append("\" />"); // } catch (MalformedURLException e) { // } if(("GetFoldersAndFiles".equals(command) || "GetFolders".equals(command)) && currentDir.equals("/") && roots.length > 1) { sb.append("<Folders>"); for(File file: roots) { String rootPath = file.getAbsolutePath(); if(Utils.IS_WINDOWS && rootPath.endsWith("\\")) { rootPath = rootPath.substring(0, rootPath.length() - 1); } sb.append("<Folder name=\"").append(Utils.escapeXML(rootPath)).append("\"/>"); } sb.append("</Folders>"); } else { if("GetFoldersAndFiles".equals(command) || "GetFolders".equals(command)) { sb.append("<Folders>"); for(File file: new File(currentDir).listFiles(new FileFilter() { public boolean accept(File pathname) { return !pathname.isFile() && !pathname.isHidden() && pathname.listFiles() != null; } })) { sb.append("<Folder name=\"").append(Utils.escapeXML(file.getName())).append("\"/>"); } sb.append("</Folders>"); } if("GetFoldersAndFiles".equals(command)) { sb.append("<Files>"); for(File file: new File(currentDir).listFiles(new FileFilter() { public boolean accept(File pathname) { if(!pathname.isFile()) { return false; } if("Image".equals(type)) { String name = pathname.getName().toLowerCase(); return name.endsWith(".bmp") || name.endsWith(".jpg") || name.endsWith(".gif") || name.endsWith(".png"); } if("Flash".equals(type)) { String name = pathname.getName().toLowerCase(); return name.endsWith(".swf"); } return true; } })) { sb.append("<File name=\"").append(Utils.escapeXML(file.getName())).append("\" size=\"").append(file.length() / 1000).append("\"/>"); } sb.append("</Files>"); } } sb.append("</Connector>"); content = sb.toString(); } final String content_ = content; return new WebServerContent() { @Override public String getContentType() { return "text/xml; charset=utf-8"; } @Override public InputStream getInputStream() { return getInputStream(content_); } }; } return WebServer.getDefaultWebServer ().getURLContent(WebServer.getDefaultWebServer ().getClassPathResourceURL(JHTMLEditor.class.getName(), PACKAGE_PREFIX + resourcePath)); } public void clearDirtyIndicator() { htmlEditor.getWebBrowser().executeJavascript("JH_clearDirtyIndicator();"); } public void setDirtyTrackingActive(boolean isDirtyTrackingActive) { htmlEditor.getWebBrowser().executeJavascript("JH_setDirtyTrackingActive(" + isDirtyTrackingActive + ");"); } private volatile Object tempResult; public String getHTMLContent() { JWebBrowser webBrowser = htmlEditor.getWebBrowser(); tempResult = this; webBrowser.executeJavascript("JH_sendData();"); int timeout = Integer.parseInt(NSSystemPropertySWT.HTMLEDITOR_GETHTMLCONTENT_TIMEOUT.get("1500")); long start = System.currentTimeMillis(); while(true) { EventDispatchUtils.sleepWithEventDispatch(new EventDispatchUtils.Condition() { public boolean getValue() { return tempResult != JHTMLEditorCKeditor.this; } }, 50); if(tempResult != this) { return (String)tempResult; } if(System.currentTimeMillis() - start > timeout) { break; } } return null; } public void setHTMLContent(String html) { // webBrowser.executeJavascript("JH_setData('" + Utils.encodeURL(html) + "');"); // There is a problem: IE crashes when it has the focus and is flooded with this message. // While waiting for an SWT fix, we use the workaround to disable the component which loses the focus. JWebBrowser webBrowser = htmlEditor.getWebBrowser(); NativeComponent nativeComponent = webBrowser.getNativeComponent(); boolean isEnabled = nativeComponent.isEnabled(); nativeComponent.setEnabled(false); new Message().syncSend(true); webBrowser.executeJavascript("JH_setData('" + Utils.encodeURL(html) + "');"); new Message().syncSend(true); nativeComponent.setEnabled(isEnabled); } }