richtexteditarea.lzx
<library>
<include href="scrollrichedittext.lzx"/>
<class name="richtexteditarea" extends="scrollrichedittext">
<attribute name="toolbarplacement" value="null
"/>
<attribute name="toolbar" value="null
"/>
<attribute name="linkpanel" value="null
"/>
<attribute name="defaultfontsize" type="number" value="11
"/>
<attribute name="defaultfontname" type="string" value="Verdana
"/>
<attribute name="defaultfontcolor" value="0x000000
"/>
<attribute name="isHTML" value="false
"/>
<attribute name="editortype" type="string" value="flash-html
"/>
<attribute name="currentformat" setter="setCurrentFormat(currentformat)"/>
<attribute name="startformat"/>
<attribute name="insertionpoint" value="0
"/>
<attribute name="_ssel" value="0
"/>
<attribute name="_esel" value="0
"/>
<attribute name="_shouldresettoolbar" value="true
"/>
<attribute name="indentsize" value="35
"/>
<attribute name="_mousedownorigin" value="null
"/>
<attribute name="_fieldhasfocus" value="false
"/>
<attribute name="_ignorenextfocus" value="false
"/>
<event name="oncurrentformat"/>
<method name="init">
super.init();
// Create a currentformat object, and initialize it to the format
// specified by the various defaultfontsize/defaultfontcolor/defaultfontname
// attributes.
this.currentformat = new lz.textformat();
this._resetFormatToDefaults();
this.startformat = this.currentformat;
this.inp.setDefaultFormat(this.currentformat);
</method>
<method name="reset">
this.clearText(true);
this._resetFormatToDefaults();
this.inp.setDefaultFormat(this.currentformat);
this.rollback();
if (this.linkpanel) this.linkpanel.hide();
if (this.toolbar) this.toolbar.reset();
this.isHTML = false;
this._shouldresettoolbar = true;
this._ssel = this._esel = 0;
</method>
<method name="setToolbar" args="tb">
this.toolbar = tb;
if (this.isinited) this.toolbar.matchformat(this.currentformat);
</method>
<method name="setLinkPanel" args="lp">
this.linkpanel = lp;
</method>
<handler name="onkeyup" args="kc" reference="this._field">
if (isHTML) {
switch (kc) {
case 8: // backspace key
case 46: // delete key
if (this.text.length == 0) {
this.reset();
}
case 35: // home
case 36: // end
case 37: // left arrow
case 38: // up arrow
case 39: // right arrow
case 40: // down arrow
this._caretmove();
break;
}
}
</handler>
<handler name="onfocus">
this._fieldhasfocus = true;
if (this._containsOnlyWhitespace()) {
this.clearText(true);
this._ssel = this._esel = 0;
this.setSelection(0,0);
this.inp.setDefaultFormat(this.currentformat);
}
// For when setSelection is called explicitly, for example in the
// linkdialog.
if (this._ignorenextfocus) {
this._ignorenextfocus = false;
return;
}
if (this._shouldresettoolbar) {
if (! this['_caretMoveDel']) {
this._caretMoveDel = new lz.Delegate(this, '_caretmove');
}
//------------------------------------------------------------
// Let the caretmove method figure out how to make the formats
// match. Need to wait an idle frame before calling
// _caretmove() because caret focus hasn't been set in
// richinput area yet.
//------------------------------------------------------------
lz.Idle.callOnIdle(this._caretMoveDel);
if ((! this.isHTML) && this._field.getSelectionSize() == -1)
{
// Selection length of -1 means this field doesn't have focus.
// _caretmove() handles this for HTML text only, not plain text
delegateInitSelection();
}
}
</handler>
<handler name="onmousedown" reference="lz.GlobalMouse" args="who">
if ((who != null) && (this._containsView(who))) {
// the mouse went down on a child of me
// Debug.info("mousedown on child of me, this is %w, who is %#w", this, who);
this._mousedownorigin = who;
} else if ((who == null) && (inp.containsPt(this.getMouse("x"), this.getMouse("y")))) {
// the mouse went down on me
// Debug.write("mousedown on me");
this._mousedownorigin = this;
} else {
// the mouse went down on someone other than me,
// Debug.info("mousedown on someone else: %w", who);
this._mousedownorigin = null;
}
</handler>
<handler name="onmouseup" reference="lz.GlobalMouse" args="who">
if (this._fieldhasfocus) {
// Debug.write("onmouseup, field has focus, who is ", who);
if (this._mousedownorigin != null) {
// We have focus, and there _is_ a mousedown/mouseup pair in progress.
if (this._mousedownorigin == this || this._mousedownorigin == this.inp) {
// The mouse click started on the richtext editor.
// If the mouse click also ended on the rich text editor,
// it's just a caret move.
if (inp.containsPt(this.getMouse("x"), this.getMouse("y"))) {
// Debug.info("mouse click starts and ends on RTE");
this._caretmove();
} else {
// Debug.write("handleLostMouseDown");
// If the mouseclick ended anywhere but the rich text editor,
// it's a lost mousedown.
this._handleLostMouseDown();
}
} else {
// We have focus, and there's a mousedown/mouseup pair in progress,
// and it started on one of the subviews of the RTE.
}
} else {
// We have focus, but there's no mousedown/mouseup pair in progress.
// Therefore, do nothing.
// Debug.write("No mousedown origin. The mousedown didn't start on an RTE component.");
}
}
// Clear the mousedown attribute; a mouseup means that whatever
// the mousedown was, it's gone now.
this._mousedownorigin = null;
</handler>
<method name="saveSelectionRegion">
if (lz.Focus.getFocus() == this._field) {
var selpos = this._field.getSelectionPosition();
var selsize = this._field.getSelectionSize();
if( selpos >= 0 && selsize >= 0){
this.setAttribute('_ssel', selpos);
this.setAttribute('_esel', selpos + selsize );
// Debug.write("fascinating, storing selection of %d-%d", this._ssel, this._esel);
}
}
</method>
<method name="setCurrentFormat" args="fmt, inCaretMove">
// Store the start and end of the selection in temporary local
// variables, for performance.
// These member data were set in saveSelectionRegion.
var ssel = this._ssel;
var esel = this._esel;
// Record the change to the current format
this.currentformat = fmt;
// Apply the new current format to the selection, *unless*
// this method was called from a caret motion event. We never
// want to format text in response to a caret motion event.
if (!inCaretMove) {
// We've now applied formatting, so we've made the text have some
// HTML-ness.
this.isHTML = true;
// if no region is selected, apply format at insertion point
if (ssel == esel) {
this._formatAtInsertion(ssel);
} else {
// there is a region selected, so apply this format to it.
this.setTextFormat(this.currentformat, ssel, esel);
}
}
// Inform listeners that the format has changed.
this.oncurrentformat.sendEvent(this.currentformat);
</method>
<method name="setFormatAttribute" args="attr, val">
var ssel = this._ssel;
var esel = this._esel;
// Debug.write("setFormatAttribute, sel is %d to %d", ssel, esel);
if (this['currentformat'])
this.currentformat[attr] = val;
this.isHTML = true;
// if no region is selected, apply format at insertion point
if (ssel == esel) {
this._formatAtInsertion(ssel);
} else {
// there is a region selected, so apply this format to it.
var tinyformat = new Object();
tinyformat[attr] = val;
this.setTextFormat(tinyformat, ssel, esel);
}
</method>
<method name="delegateRestoreSelection">
// Delegate to restore selection after an edit or focus is lost.
if (! this['_selectiondel']) {
this._selectiondel = new lz.Delegate(this, '_restoreSelection');
}
// After the formatting has been completed, need to wait a frame to
// restore the selection.
lz.Idle.callOnIdle(this._selectiondel);
</method>
<method name="delegateInitSelection">
// Let's send an idle event to call _focussel() in a minute; we'll have focus then
if (! this['_focusseldel']) {
this._focusseldel = new lz.Delegate(this, '_focussel');
}
lz.Idle.callOnIdle(this._focusseldel);
</method>
<method name="setSelection" args="ns,ne=null">
this._ssel = ns;
this._esel = ne;
super.setSelection(ns,ne);
</method>
<method name="_caretmove" args="ignore=null">
// Don't do any of this if this field isn't already rich-text!
if (!this.isHTML) return;
//------------------------------------------------------------
// Analyze the selection position, location, and text length.
//
// We should never change the format of the selected text
// in response to a caretmove event.
//
// Note: when we tab into this field, the entire field contents are
// selected, but we're given selpos of -1, sellen of -1.
// Note: we don't get caretmove called when the selection changes by
// mouseout then mouseup not over richinputtext.
//------------------------------------------------------------
var selstart = this._field.getSelectionPosition();
var sellen = this._field.getSelectionSize();
var text = this.text;
var textlen = text.length;
// If there's some text here, not just an empty field...
if (textlen > 0) {
if (sellen == 0) { // zero-length selection, which means, just an insertion point.
// Where are we in the field?
if (selstart == 0) {
// we're in the beginning of the field.
// get the format of the first character in the field
this.setCurrentFormat( this.getTextFormat(0, 1), true);
} else if (selstart == textlen) {
// Insertion point is at the end of the current text, so,
// set the format at the insertion point to be the format
// of the toolbar.
//-------------------------------------------------------------
// These next two lines are a workaround for a flash bug.
// -ben/pkang
//-------------------------------------------------------------
this.setSelection(textlen-1, textlen-1);
this.setSelection(textlen, textlen);
this._ssel = this._esel = textlen;
this.setCurrentFormat( this.getTextFormat(textlen-1, textlen));
} else {
// we're in the middle or at the end of the field
// get the format of the character immediately before the insertion point
this.setCurrentFormat( this.getTextFormat(selstart, selstart + 1), true);
}
} else if (sellen == -1) {
// Selection length of -1 means this field doesn't have focus.
// Let's send an idle event to call us in a minute; we'll have focus then
delegateInitSelection();
} else {
// selection length is greater than zero, that is, we have some characters selected
// Desired behavior:
// Get format of the entire selection.
var theformat = this.getTextFormat(selstart, selstart + sellen);
// Update the toolbar with whatever format attributes
// are common to the entire selection.
// Show - - (n/a) for any format attributes which are different
// within the selection.
this.setCurrentFormat(theformat, true);
// This will leave some of the characteristics of the currentformat undefined.
}
} else {
// Debug.write("zero-length field; formatting it.");
this._ssel = this._esel = 0;
// Set the format of the text we're going to add to be the format currently
// specified by currentformat!
this._formatAtInsertion(0);
this.setSelection(0, 0);
}
</method>
<method name="_focussel" args="ignore=null">
// We really should have focus now!
var havelzfocus = (lz.Focus.getFocus() == this._field);
if (havelzfocus) {
var len = this.text.length;
// move the insertion point to the beginning of the text
this.setSelection(len, len);
this._caretmove();
}
</method>
<method name="_formatAtInsertion" args="ip">
if (this.isHTML) {
if (this.text.length == ip){
this.inp.setDefaultFormat(this['currentformat']);
}else{
if (this.text.charAt(ip) != ' ') {
this.replaceText(ip, ip,' ');
}
this.setTextFormat(this['currentformat'], ip, ip+1);
}
}
</method>
<method name="_handleLostMouseDown">
var selstart = this._field.getSelectionPosition();
var selend = selstart + this._field.getSelectionSize();
this.setCurrentFormat( this.getTextFormat(selstart, selend), true);
</method>
<method name="_restoreSelection" args="ignore=null">
//------------------------------------------------------------
// FIXME [2005-08-16 pkang]: Need to know in which direction
// selection was done so as to not make scrollbar jump.
//------------------------------------------------------------
this._shouldresettoolbar = false;
this.setSelection(this._ssel, this._esel);
this._shouldresettoolbar = true;
</method>
<method name="_resetFormatToDefaults" args="fmt">
this.currentformat.color = this.defaultfontcolor;
this.currentformat.font = this.defaultfontname;
this.currentformat.size = this.defaultfontsize;
this.currentformat.bold = false;
this.currentformat.italic = false;
this.currentformat.underline = false;
this.currentformat.bullet = false;
this.currentformat.url = "";
</method>
<method name="_containsView" args="v">
return (v.searchParents("name", this.name) || v.searchParents("name", this.toolbar.name));
</method>
<method name="_containsOnlyWhitespace">
var str = this.text;
var len = str.length;
var ws = " \t\r\n";
for (var i=0; i < len; i++) {
if (ws.indexOf(str.charAt(i)) < 0) {
return false;
}
}
return true;
</method>
</class>
</library>
Cross References
Includes
Classes