Thursday, June 4, 2015

ADF: Smarter RichTextEditor (Default Styling)

Problem Description: 


ADF RichTextEditor (RTE) provides ability to style your content. Most of the time any content created using RTE needs to be displayed in some other page. We can definitely use OutputText (escape=false) to show exactly same content with same styling. But if you take customer's point of view, most of the time they know their content well but they don't know how to style it. What they want is content should get styled according to rest of the website automatically. For example let way I have a red box in my UI, which shows announcements. HR has edit button which launches a new popup with RTE , which allows him to create announcement text. As shown below




Now if we see base page is in red background and we want white text on it. If I open RTE by default it provides me white background and black text. Even if I style it I can make its background-color Red but I can't set text-color to be white (Atleast I could not do that). Similarly there could be other things for example any <h1> tag should leave 20px margin at top and 10px at bottom. And it should be a default behavior because that is how rest of website behaves. We can't expect customer to go in source mode and put style="margin-top: 20px; margin-bottom: 10px". He should just be adding <h1> tag thats it.
Basically what I am saying that there should be a mechanism to put default style for our html tags (background-color, text-color, ul, li, ol, h1-h6, table th etc.). Same style should be used on RTE and base page (where content actually needs to appear)


Why RTE is not showing styles of base page

If we try to see styles and components of RTE using chrome inspector (F12), we find that RTE has created an iframe with head/body. All our cotent is apearing below <body> tag. There is no css mentioned in head section, so all tags that we create here are taking styles directly from browser defaults. Because of that text-color is always black. Even you may find that few components are looking different in different browser based on browser specific settings. Definitely base page on which we are going to show content is having our application styles so they are different.




Possible solution

What we need is somehow all my tags should use same style on RTE and base page. Idea is to create a CSS, which will be applied on RTE and base page content. To apply a CSS on RTE we need to add css entry in head section of iframe-html.

To achieve this I will be using javascript to get handle of iframe-head section and set a <link> tag to my custom css.
Before that let us write our requirements
1. RTE should look in red background
2. Text-color automatically should become white
3. If I put h1 tag it should automatically leave 20px at top and 10px at bottom
4. RTE width should be adjusted as the width of base page section.


Steps to be followed:


1. Create a new CSS for your ADF application: 

First thing we want to create a new CSS for our ADF application. We need to add trinidad-skins.xml file and modify entry of trinidad-config.xml file.
You can go over blog http://sanjeev-technology.blogspot.in/2015/04/adf-do-you-know-oracle-provided-skins.html to see steps on how to create new css for your ADF application.

My projects trinidad-skins.xml entry is
<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- Copyright (c) 2008, 2012, Oracle and/or its affiliates. All rights reserved. -->
<skins xmlns="http://myfaces.apache.org/trinidad/skin">
<skin>
    <id>sanstyle-v1.desktop</id>
    <family>sanStyle-v1</family>
    <extends>fusionFx-v1.desktop</extends>
    <render-kit-id>org.apache.myfaces.trinidad.desktop</render-kit-id>
    <style-sheet-name>/css/RichTextEditor.css</style-sheet-name>
  </skin>
  </skins>


trinidad-config.xml enry is
<trinidad-config xmlns="http://myfaces.apache.org/trinidad/config">
  <skin-family>sanStyle-v1</skin-family>
</trinidad-config>

I also have /css/RichTextEditor.css file, which will have my styling.

2. Add Javascript on proper event to set css for iframe-head section:

To do this we need to capture iframe component. I wanted to run javascript on iframe load event but seems like iframe is construted locally without any url so load even is not firing. (Just my guess..) Because of this I just did a workaround to run my script after 10 milisecond and get handle of iframe object. It gives a flickering effect but OK for me. May be you want to investigate more on it and get correct time to run your javascript
Now my javascript code in /js/RichTextEditor.js file is
function styleRTE(evt){
     myVar=setTimeout(function(){setRTEStyle()},10)
}
    
    function setRTEStyle() {
           $('iframe').contents().find("head")
              .append($("<link rel=\"stylesheet\" type=\"text/css\" href=\"../css/RichTextEditor.css\">"));   
           $('iframe').contents().find("body").addClass("myRichTextEditor");

    }


Note: I am using jquery, you can write plain javascript also.
Note: I am using 'iframe' selector, it means all iframe will have following styles. You can use other jquery selector based on your requirement.

Here are the description what I am doing in this javascript
a. function sytleRTE runs setRTEStyle after 10 milliseconds
b. function setRTEStyle finds 'iframe' objects and append <link rel="stylesheet" type="text/css" href="../css/RichTextEditor.css"> in head section.
c. function setRTEStyle also tries to set style for body tag as myRichTextEditor.

Now I need to call styleRTE on proper event of ADF. In my case I am calling it on popup launch event but if your RTE is on base page directly, you may want to call it on pageload event.

To call it on popup launch event I have following code


3. Add style in your css file: 

I have added following styles in my css file (RichTextEditor.css)

4. Lets see RTE now in chrome inspector (F12):



5. Now lets set our base page

   I am using outputtext with escape=false to show base page content. This will make sure that I get complete content as it is on my ADF page but to make sure that any content in this also have styles from RichTextEditor.css I need to surround my outputtext with a panelgroup and then set style myRichTextEditor on that panel group.

Now let see it in chrome inspector


Similarly you can add default styling for other tags in RichTextEditor.css as
.myRichTextEditor h2 {

}

.myRichTextEditor ul {

}

.myRichTextEditor ul li {


}


With that I have achieved following
a. Set a default style for each tags in RTE
b. Same style is used to show tags on base page. 


NOTE: Step 1 where we configure trinidad-skin.xml and trinidad-config.xml can be optional. Over only aim is to add RichTextEditor.css on our base page. We can achieve that using <af:resource type="css" source="/css/RichTextEditor.css"/> also. But we need to add it on all jspx pages and who knows if you want to have style only to be done on one single instance of RTE you can try this approach.

No comments: