A comment I received while developing a WaveMaker application was that WaveMaker TextArea Editors do not retain line breaks (\n). Apparently when the TextArea is in read-only mode, the line breaks are lost. When the editor is active (editable) the line breaks appear to be still there:

The left hand side of the picture shows the WaveMaker TextArea Editor in read-only mode, the right hand side shows the editor in editable mode. What we really want is this:

If the editor is in read-only mode (left hand side), the line breaks should be preserved. Also, a scrollbar should appear when the editor cannot display the entire contents.
Creating a custom WaveMaker editor
The reason line breaks are lost in WaveMaker’s TextArea editor is that a ‘\n’ character has no meaning in HTML. In read-only mode, the display value (including new-line characters) of a WaveMaker editor is put in a <div>-tag. Of course, a new-line character in a <div> tag does not translate to a line break in your browser.
WaveMaker allows you to create your own components. It is explained on the WaveMaker developer pages and forums. This post describes how I applied that information to create a custom WaveMaker editor. This NewLineTextArea editor replaces new-line characters with HTML line breaks (<br>) when it is in read-only mode. Additionally, a scroll bar is added in case the read-only mode editor cannot show the entire value.
Creating WaveMaker components is usually done by manually editing files. Since WaveMaker 5 there appear to be visual ways to create new, custom components. However, that approach is not feasible for our custom TextArea Editor. It is much simpler to inherit from WaveMaker’s default TextArea component, modify its behavior where necessary and then register it with the studio. WaveMaker is built on dojo, so we will be editing dojo classes.
WaveMaker Editor design

The figure shows a simplified UML model of WaveMaker’s editor implementation. WaveMaker editors are all subclasses of wm.Editor. This class aggregates the label (captionLabel) and the actual editor implementation (editor). The editor implementation is the actual editor and is displayed next to the label. The type of editor that is instantiated is always a subclass of wm._BaseEditor. The exact type that is instantiated is determined by the value of the display attribute in the wm.Editor subclass.
In the studio, you create an editor by dragging it from the palette to the canvas. You can always change the type of editor by modifying the display attribute using a drop down box. Possible editor implementations are simple text boxes, text areas, checkboxes, currency editors, etc.
The NewLineTextArea Editor implementation
To create a custom WaveMaker editor, you create a subclass of wm._BaseEditor, and plug it in to your WaveMaker studio. This is the source code for my new line aware text editor:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
| dojo.provide("common.packages.ppwcode.NewLineTextArea");
dojo.require("wm.base.widget.Editor");
dojo.require("wm.base.widget.Editors.Text");
wm.editors.push("NewLineTextArea");
dojo.declare("wm._NewLineTextAreaEditor", wm._TextAreaEditor, {
autoScroll: true,
setReadonly: function(readonly) {
this.autoScroll = readonly;
this.inherited(arguments);
},
_getReadonlyValue: function() {
var t = this.inherited(arguments);
var rex = new RegExp("[\\n\\r]", "gi");
return t.replace(rex, "<br>");
},
sizeEditor: function() {
// sizeEditor is an exact copy of wm._TextAreaEditor with one
// modification: the width is reset to zero if it appears no be
// negative. A negative width is returned from getContentBounds
// in IE8 when autoScroll is turned on. The DOM model seems to
// have a problem with a negative width.
if (this._cupdating)
return;
var e = this.editor;
if (e) {
var
bounds = this.getContentBounds(),
// note, subtract 2 from bounds for dijit editor border/margin
height = bounds.h ? bounds.h - 2 + "px" : "",
width = bounds.w ? bounds.w - 4 + "px" : "",
d = e && e.domNode,
s = d.style,
fc = d && d.firstChild;
// Keep IE(8) happy
if (parseInt(width) < 0) width = "";
if (!this.editorBorder) s.border = 0;
s.backgroundColor = this.editorBorder ? "" : "transparent";
s.backgroundImage = this.editorBorder ? "" : "none";
s.width = width;
if (height) {
if (fc) {
dojo.forEach(fc.childNodes, function(c) {
if (c.style) {
c.style.height = height;
}
});
}
if (e.focusNode && e.focusNode.style)
e.focusNode.style.height = height;
}
}
}
});
dojo.declare("common.packages.ppwcode.NewLineTextAreaEditor", wm.Editor, {
display: "NewLineTextArea"
}); |
The first 3 lines of code are mandatory dojo dependency management. We will be extending WaveMaker’s editor components, so they must be loaded before we can use them.
Line 6 adds our NewLineTextAreaEditor to the list of supported editors. wm.editors is an array of strings that contains the list of supported editors. This list is shown in the display drop down box in the studio.
Line 8 declares the editor’s class. The name of your custom editor is important. The class name of the editor implementation is determined by prepending wm._ and appending Editor to the value of the display attribute. So if we add NewLineTextArea to wm.editors, we must declare a class wm._NewLineTextAreaEditor. The class inherits from wm._TextAreaEditor because the class effectively behaves like WaveMaker’s TextArea editor. Except in read only mode, we want line breaks to be preserved.
The autoScroll property on line 9 is an inherited property that determines the scroll behavior of the HTML element that shows the value in the editor. In read-only mode, this element is a simple <div> node. The autoScroll value is initialized to true. This corresponds to the value we want the autoScroll property to have when the editor is in read-only mode; scroll bars will added to the <div> node if necessary. When the editor is in write mode, the <div> is replaced by a <textarea> node in which line breaks are already preserved. A <textarea> node also adds scrollbars automatically when necessary. autoScroll is set to false when the editor is not in read-only mode. The setReadOnly method (line 10) is overridden to alter the autoScroll property depending on the value of the readonly parameter.
The _getReadonlyValue method (line 14) is probably the most important method in this class. It transforms newline characters in a string to <br> tags. This preserves the line breaks when the editor shows the value in read only mode. The method is normally seen as a private method because it starts with an underscore. In principle, this method hence should not be overridden. On the other hand, other editor implementations override it too. So I guess it can be considered as a friend function or a package visible function.
The sizeEditor method (line 19) implementation is the same as the method in the superclass, with one modification. We verify if the width of the editor is smaller than zero. If you application runs in Internet Explorer and scrollbars are present, the calculated editor width can be negative. The DOM model however can not cope with negative widths. The overridden sizeEditor method works around this problem: if the width is negative – this only occurs when the page is being loaded, and the editor is not yet shown on the page – we reset it to zero (line 39).
Last but definitely not least, on line 60 the WaveMaker editor class that encapsulates the NewLineTextArea editor is declared. This is done by creating a subclass of wm.Editor and assigning the string “NewLineTextArea” to the dispay property.
Adding the editor to WaveMaker Studio
Having an implementation of an editor is one thing. You must still tell WaveMaker studio where to pick up your code. This is also explained in WaveMaker’s documentation on creating Custom Widgets.
Custom code is located in your WaveMaker workspace. The workspace is in a directory you created the first time you installed WaveMaker. Custom code should be placed in the commons/packages directory in the WaveMaker workspace:

Our editor provides common.packages.ppwcode.NewLineTextArea, so all our code goes into common/packages/ppwcode/NewLineTextArea.js in the WaveMaker workspace. You then need to modify lib.js en packages.js to load your code into the studio, and eventually your WaveMaker application.
In lib.js you must add dojo.require statements for the extra javascript files you want to load:
dojo.require("common.packages.ppwcode.NewLineTextArea");
In packages.js you must add an array with information about your custom widget. This will add your custom widget to the Palette in WaveMaker studio.
[ "PeopleWare",
"NewLineTextArea",
"common.packages.ppwcode.NewLineTextAreaEditor",
"common.packages.ppwcode.NewLineTextArea",
"images/wm/edit.png",
"A Text Area that supports new lines."
]
The first element in the array is the name of the widget category in the palette. In our example, we create a new category called PeopleWare. The second element is the name of the widget, which we call NewLineTextArea. The third element is the dojo class name of the widget. The fourth element is the name of the module in which that class is located. This is the name of the module you’ve added to lib.js. The fifth element is an icon for your widget; here we use a standard editor icon. The last element in the array is a short description of the widget.
If everything is in place, the palette of your studio looks like this:

From the palette, you can drag your custom widget to the application canvas. This widget will behave like WaveMaker’s TextArea Editor, but in read-only mode line breaks will be preserved.