Thursday, July 5, 2012

Modifying an extJS Authoring Interface in CQ5

I wanted to share this piece of code with the community. It was developed by one of the other developers, Braxton Diggs, on my team integrating the new travel.state.gov site and I really felt like this was some cool code that I hadn’t seen anything like anywhere on the net. One of the customer needs was to have a precise control over the size of the content being entered by their authors. As you might imagine, an organization like the State Department of United States has a lot of authors and lot of sometimes arcane rules and operating procedures. To accommodate this need to control content size, we developed this counter code as well as a limiter. Here is code for how we count the characters being entered in a field of xtype richtext as well as display the counter on the top right corner of the richtext dialog. I think that this is a pretty slick integration, because it controls the content entry right at the point of content entry, rather than just truncating the user input or displaying an error message after the fact. To begin, we will need a design_dialog in our over ridden text component with a property called maxchar, which will be used to dynamically limit the input size for our richtext field. You could limit the text somewhere in the code we’re showing here or you could as we have done, allow a user with access to the Design mode to control the size of the input, making this a very flexible piece of code. Then as part of our authoring dialog, we need 2 particular items; a richtext field called text and an nt:unstructured node that we called listeners. The listeners node has a property called render, which this has bit of javascript as its value:
function(){ var dialog = this.findParentByType('dialog');//find dialog var rte = dialog.findBy(function(p){ return (p.getXType() == "richtext");})[0];//get dialog that contains richttext rte.on({ activate:function(textarea){//fires when user clicks inside textarea processContent(textarea.getId(), textarea.getValue()); } }); maxchar = MaxChar();//get maxmimum character dialog.on({ beforesubmit:function(textarea){//before sumbit/"OK" button if(ValidateText(textarea.getId(), maxchar)){//if passes return true; }else{ CQ.Ext.Msg.show({"title":"Houston we have a problem!", "msg":"You have too many characters in the textarea, please delete some characters!
You are only allowed "+maxchar+" characters."}).setIcon(CQ.Ext.Msg.WARNING);//custom dialog alert return false; } }, deactivate:function(textarea){ResetCounter(textarea.getId(), maxchar);//On close run reset function } }); }
The javascript in the text component code listed below catches each keystroke action and records if a character has been entered into the richtext area and increments or decrements the counter and cuts the user off from entering text beyond the limit specified in the maxchar property in the design_dialog.
function ResetCounter(id, maxchar){//Function fired when dialog is closed if (maxchar > 0) {//zero or less than disables limiter var txtlen = $("#"+id).find('iframe').contents().find('body').text().length;//Get Text Length $("#"+id+"_txtcount").html(txtlen+"/"+maxchar).hide();//Whenever dialog is re-opened it will have correct length } } function processContent(id, val){//Function initalizes text limit. id - open dialog unique ID. val - Not used var rte = "#" + id; var maxchar = <%=currentStyle.get("maxchar", 0)%>;//custom max characters if (maxchar > 0) {//zero or less than disables limiter if ($(rte+"_txtcount").length != 0) { $(rte+"_txtcount").html("");//show nothing if nothing is in textarea }else { $(rte).prev('div').find('table td[class*=right]').prepend('
');//disabled text limit } $(rte).next('div').find('iframe').contents().find('body').bind("keydown keyup keypress", function(e) {//attach event handler to textarea var char_code = e.which;//capture keystrokes var txtlen = $(this).text().length;//textarea length $(rte+"_txtcount").html(txtlen+"/"+maxchar).show();//show text if (!(txtlen < maxchar || char_code == 46 || char_code == 8)) return false;//do not allow typing if greater than limit, backspace and delete if (txtlen/maxchar >= 0 && txtlen/maxchar < 0.25) {//0-25% $(rte+"_txtcount").css("color","#008000");//change colors }else if (txtlen/maxchar >= 0.25 && txtlen/maxchar < 0.5) {//25-50% $(rte+"_txtcount").css("color","#cccc00"); }else if (txtlen/maxchar >= 0.5 && txtlen/maxchar < 0.75) { $(rte+"_txtcount").css("color","#ee7700"); }else if (txtlen/maxchar >= 0.75) { $(rte+"_txtcount").css("color","#FF0000"); } }); } } function ValidateText(id, maxchar) {//function fires when user hits ok button on dialog var txtlen = $("#"+id).find('iframe').contents().find('body').text().length; if (txtlen<=maxchar || maxchar == 0) { return true; }else{ return false; } } function MaxChar() {//get max character return <%=currentStyle.get("maxchar", 0)%>; }

3 comments:

  1. Hi! Thanks a lot for this post, It's very useful!
    I have a question, where should a put the second js code that you mentioned above?
    I don't know where to put it. I am using CQ 5.6.1.
    Thanks in advance!

    ReplyDelete
  2. Add the JavaScript to the listeners node, as described above.

    ReplyDelete
  3. This comment has been removed by a blog administrator.

    ReplyDelete