1////////////////////////////////////////////////////////////////////////////////
2//
3//  ADOBE SYSTEMS INCORPORATED
4//  Copyright 2005-2007 Adobe Systems Incorporated
5//  All Rights Reserved.
6//
7//  NOTICE: Adobe permits you to use, modify, and distribute this file
8//  in accordance with the terms of the license agreement accompanying it.
9//
10////////////////////////////////////////////////////////////////////////////////
11
12package mx.controls.textClasses
13{
14
15import flash.text.TextField;
16import flash.text.TextFormat;
17import mx.core.UIComponent;
18import mx.core.mx_internal;
19import mx.styles.StyleManager;
20import mx.utils.StringUtil;
21
22use namespace mx_internal;
23
24/**
25 *  The TextRange class provides properties that select and format a range of
26 *  text in the Label, Text, TextArea, TextEditor, and RichTextEditor controls.
27 *
28 *  @see mx.controls.Label
29 *  @see mx.controls.RichTextEditor
30 *  @see mx.controls.Text
31 *  @see mx.controls.TextArea
32 *  @see mx.controls.TextInput
33 *  @see flash.text.TextFormatAlign
34 *
35 *  @langversion 3.0
36 *  @playerversion Flash 9
37 *  @playerversion AIR 1.1
38 *  @productversion Flex 3
39 */
40public class TextRange
41{
42	include "../../core/Version.as";
43
44    //--------------------------------------------------------------------------
45    //
46    //  Class variables
47    //
48    //--------------------------------------------------------------------------
49
50	/**
51	 *  @private
52	 */
53	private static var htmlTextField:TextField;
54
55    //--------------------------------------------------------------------------
56    //
57    //  Constructor
58    //
59    //--------------------------------------------------------------------------
60
61	/**
62	 *  Create a new TextRange Object that represents a subset of the contents
63	 *  of a text control, including the formatting information.
64	 *
65	 *  @param owner The control that contains the text. The control must have
66	 *  a <code>textField</code> property, or, as is the case of the
67	 *  RichTextEditor control, a <code>textArea</code> property.
68	 *
69	 *  @param modifiesSelection Whether to select the text in the range.
70	 *  If you set this parameter to <code>true</code> and do not specify a
71	 *  begin or end index that corresponds to text in the control, Flex
72	 *  uses the begin or end index of the current text selection.
73	 *  If this parameter is <code>true</code>, you omit the
74	 *  <code>beginIndex</code> and <code>endIndex</code>
75	 *  parameters, and there is no selection, the TextRange object is empty.
76	 *
77	 *  @param beginIndex Zero-based index of the first character in the range.
78	 *  If the <code>modifiesSelection</code> parameter is <code>false</code>
79	 *  and you omit this parameter or specify a negative value,
80	 *  the range starts with the first text character.
81	 *
82	 *  @param endIndex Zero-based index of the position <i>after</i> the
83	 *  last character in the range.
84	 *  If the <code>modifiesSelection</code> parameter is <code>false</code>
85	 *  and you omit this parameter, specify a negative value, or specify
86	 *  a value past the end of the text, the range ends with the last
87	 *  text character.
88	 *
89	 *  @langversion 3.0
90	 *  @playerversion Flash 9
91	 *  @playerversion AIR 1.1
92	 *  @productversion Flex 3
93	 */
94	public function TextRange(owner:UIComponent,
95							  modifiesSelection:Boolean = false,
96							  beginIndex:int = -1, endIndex:int = -1)
97	{
98		super();
99
100		_owner = owner;
101
102		try
103		{
104			textField = _owner["textArea"].getTextField();
105		}
106		catch(e:Error)
107		{
108			textField = this["_owner"].getTextField();
109		}
110
111		_modifiesSelection = modifiesSelection;
112
113		if (!_modifiesSelection)
114		{
115			if (beginIndex < 0)
116				beginIndex = 0;
117
118			if (beginIndex > textField.length)
119				beginIndex = textField.length;
120
121			if (endIndex < 0 || endIndex > textField.length)
122				endIndex = textField.length;
123
124			_beginIndex = beginIndex;
125			_endIndex = endIndex;
126		}
127		else
128		{
129			if (beginIndex < 0 || beginIndex > textField.length)
130				beginIndex = textField.selectionBeginIndex;
131
132			if (endIndex < 0 || endIndex > textField.length)
133				endIndex = textField.selectionEndIndex;
134
135			textField.selectable = true;
136
137			if (beginIndex != textField.selectionBeginIndex ||
138				endIndex != textField.selectionEndIndex)
139			{
140				textField.setSelection(beginIndex, endIndex);
141			}
142		}
143	}
144
145    //--------------------------------------------------------------------------
146    //
147    //  Variables
148    //
149    //--------------------------------------------------------------------------
150
151	/**
152	 *  @private
153	 */
154	private var textField:TextField;
155
156    //--------------------------------------------------------------------------
157    //
158    //  Properties
159    //
160    //--------------------------------------------------------------------------
161
162    //----------------------------------
163	//  beginIndex
164    //----------------------------------
165
166	/**
167	 *  Storage for the beginIndex property.
168	 *
169	 *  @langversion 3.0
170	 *  @playerversion Flash 9
171	 *  @playerversion AIR 1.1
172	 *  @productversion Flex 3
173	 */
174	private var _beginIndex:int;
175
176	/**
177	 *  Zero-based index in the control's text field of the first
178	 *  character in the range.
179	 *  If the fifth character in the text is the first character in the
180	 *  range, this property has a value of 4.
181	 *
182	 *  @langversion 3.0
183	 *  @playerversion Flash 9
184	 *  @playerversion AIR 1.1
185	 *  @productversion Flex 3
186	 */
187	public function get beginIndex():int
188	{
189		if (_modifiesSelection)
190			return textField.selectionBeginIndex;
191		else
192			return _beginIndex;
193	}
194
195	/**
196	 *  @private
197	 */
198	public function set beginIndex(value:int):void
199	{
200		if (_modifiesSelection)
201			textField.setSelection(value, textField.selectionEndIndex);
202		else
203			_beginIndex = value;
204	}
205
206    //----------------------------------
207	//  bullet
208    //----------------------------------
209
210	/**
211	 *  Whether the text in the range is in a bulleted list.
212	 *  If only part of the range is in a bulleted list,
213	 *  this value is <code>false</code>.
214	 *
215	 *  @langversion 3.0
216	 *  @playerversion Flash 9
217	 *  @playerversion AIR 1.1
218	 *  @productversion Flex 3
219	 */
220	public function get bullet():Boolean
221	{
222		return getTextFormat().bullet;
223	}
224
225	/**
226	 *  @private
227	 */
228	public function set bullet(value:Boolean):void
229	{
230		var tf:TextFormat = getTextFormat();
231		tf.bullet = value;
232		setTextFormat(tf);
233	}
234
235    //----------------------------------
236	//  color
237    //----------------------------------
238
239	/**
240	 *  Color of the text in the range.
241	 *  You can set this value using any valid color identifier.
242	 *  The property returns the value as a numeric value.
243	 *  If the range has multiple colors, this value is <code>null</code>.
244	 *
245	 *  @langversion 3.0
246	 *  @playerversion Flash 9
247	 *  @playerversion AIR 1.1
248	 *  @productversion Flex 3
249	 */
250	public function get color():Object
251	{
252		return getTextFormat().color;
253	}
254
255	/**
256	 *  @private
257	 */
258	public function set color(value:Object):void
259	{
260		var tf:TextFormat = getTextFormat();
261		var colorNumber:uint = owner.styleManager.getColorName(value);
262		if (colorNumber != StyleManager.NOT_A_COLOR)
263			value = colorNumber;
264		else
265			value = 0;
266		tf.color = value;
267		setTextFormat(tf);
268	}
269
270    //----------------------------------
271	//  endIndex
272    //----------------------------------
273
274	/**
275	 *  Storage for the beginIndex property.
276	 *
277	 *  @langversion 3.0
278	 *  @playerversion Flash 9
279	 *  @playerversion AIR 1.1
280	 *  @productversion Flex 3
281	 */
282	private var _endIndex:int;
283
284	/**
285	 *  Zero-based index in the control's text field of the point
286	 *  immediately after the last character in the range; equivalent to
287	 *  the One-based index of the last character.
288	 *  If the fifth character in the text is the last character in the
289	 *  range, this property has a value of 5.
290	 *
291	 *  @langversion 3.0
292	 *  @playerversion Flash 9
293	 *  @playerversion AIR 1.1
294	 *  @productversion Flex 3
295	 */
296	public function get endIndex():int
297	{
298		if (_modifiesSelection)
299			return textField.selectionEndIndex;
300		else
301			return _endIndex;
302	}
303
304	/**
305	 *  @private
306	 */
307	public function set endIndex(value:int):void
308	{
309		if (_modifiesSelection)
310			textField.setSelection(textField.selectionBeginIndex, value);
311		else
312			_endIndex = value;
313	}
314
315    //----------------------------------
316	//  fontFamily
317    //----------------------------------
318
319	/**
320	 *  Name of the font for text in the range.
321	 *  If the range has multiple fonts, this value is <code>null</code>.
322	 *
323	 *  @langversion 3.0
324	 *  @playerversion Flash 9
325	 *  @playerversion AIR 1.1
326	 *  @productversion Flex 3
327	 */
328	public function get fontFamily():String
329	{
330		return getTextFormat().font;
331	}
332
333	/**
334	 *  @private
335	 */
336	public function set fontFamily(value:String):void
337	{
338		var tf:TextFormat = getTextFormat();
339		tf.font = StringUtil.trimArrayElements(value,",");
340		setTextFormat(tf);
341	}
342
343    //----------------------------------
344	//  fontSize
345    //----------------------------------
346
347	/**
348	 *  Point size of the text in the range.
349	 *  If the range has multiple sizes, this value is 0.
350	 *
351	 *  @langversion 3.0
352	 *  @playerversion Flash 9
353	 *  @playerversion AIR 1.1
354	 *  @productversion Flex 3
355	 */
356	public function get fontSize():int
357	{
358		return int(getTextFormat().size);
359	}
360
361	/**
362	 *  @private
363	 */
364	public function set fontSize(value:int):void
365	{
366		var tf:TextFormat = getTextFormat();
367		tf.size = value;
368		setTextFormat(tf);
369	}
370
371    //----------------------------------
372	//  fontStyle
373    //----------------------------------
374
375	/**
376	 *  Style of the font in the range, as "italic"
377	 *  or "normal". Setting the property to any other string results
378	 *  in normal style.
379	 *  If the range has multiple styles, this value is <code>null</code>.
380	 *
381	 *  @langversion 3.0
382	 *  @playerversion Flash 9
383	 *  @playerversion AIR 1.1
384	 *  @productversion Flex 3
385	 */
386	public function get fontStyle():String
387	{
388		return getTextFormat().italic ? "italic" : "normal";
389	}
390
391	/**
392	 *  @private
393	 */
394	public function set fontStyle(value:String):void
395	{
396		var tf:TextFormat = getTextFormat();
397		tf.italic = (value == "italic") ? true : false;
398		setTextFormat(tf);
399	}
400
401    //----------------------------------
402	//  fontWeight
403    //----------------------------------
404
405	/**
406	 *  Weight of the font in the range, as "bold"
407	 *  or "normal". Setting the property to any other string results
408	 *  in normal weight.
409	 *  If the range has multiple weights, this value is <code>null</code>.
410	 *
411	 *  @langversion 3.0
412	 *  @playerversion Flash 9
413	 *  @playerversion AIR 1.1
414	 *  @productversion Flex 3
415	 */
416	public function get fontWeight():String
417	{
418		return getTextFormat().bold ? "bold" : "normal";
419	}
420
421	/**
422	 *  @private
423	 */
424	public function set fontWeight(value:String):void
425	{
426		var tf:TextFormat = getTextFormat();
427		tf.bold = (value == "bold") ? true : false;
428		setTextFormat(tf);
429	}
430
431    //----------------------------------
432	//  htmlText
433    //----------------------------------
434
435	/**
436	 *  Contents of the range in the form of HTML text.
437	 *  This property returns all HTML markup for the range, including
438	 *  markup for formatting that is applied by Flex, not just
439	 *  HTML that you specify in using an <code>htmlText</code> property.
440	 *  This property is, therefore, a full HTML representation of the
441	 *  text as it appears in the control.
442	 *
443	 *  @langversion 3.0
444	 *  @playerversion Flash 9
445	 *  @playerversion AIR 1.1
446	 *  @productversion Flex 3
447	 */
448	public function get htmlText():String
449	{
450		if (beginIndex == endIndex)
451			return "";
452
453		if (!htmlTextField)
454		{
455			htmlTextField = new TextField();
456			htmlTextField.multiline = true;
457		}
458
459		htmlTextField.defaultTextFormat = textField.defaultTextFormat;
460		htmlTextField.htmlText = "";
461
462		htmlTextField.insertXMLText(
463			0, 0, textField.getXMLText(beginIndex, endIndex))
464
465		return htmlTextField.htmlText;
466	}
467
468	/**
469	 *  @private
470	 */
471	public function set htmlText(value:String):void
472	{
473
474		if (!htmlTextField)
475		{
476			htmlTextField = new TextField();
477			htmlTextField.multiline = true;
478		}
479
480		htmlTextField.defaultTextFormat = textField.defaultTextFormat;
481		htmlTextField.htmlText = value;
482		var length:int = htmlTextField.length;
483
484		var oldBeginIndex:int = beginIndex;
485		textField.insertXMLText(
486			beginIndex, endIndex, htmlTextField.getXMLText());
487		// workaround for what seems to be a player bug (#207147)
488		// where a zero length selection is shifted
489		// (if no bug, this code should never execute)
490		if ((oldBeginIndex == 0) && (beginIndex > 0))
491			beginIndex = 0;
492
493		endIndex = beginIndex + length;
494	}
495
496    //----------------------------------
497	//  kerning
498    //----------------------------------
499
500	/**
501	 *  A Boolean value that indicates whether kerning
502	 *  is enabled (<code>true</code>) or disabled (<code>false</code>).
503	 *  Kerning adjusts the pixels between certain character pairs
504	 *  to improve readability, and should be used only when necessary,
505	 *  such as with headings in large fonts.
506	 *  Kerning is supported for embedded fonts only.
507	 *  Certain fonts, such as Verdana, and monospaced fonts,
508	 *  such as Courier New, do not support kerning.
509	 *
510	 *  @default false
511	 *
512	 *  @langversion 3.0
513	 *  @playerversion Flash 9
514	 *  @playerversion AIR 1.1
515	 *  @productversion Flex 3
516	 */
517	public function get kerning():Boolean
518	{
519		return Boolean(getTextFormat().kerning);
520	}
521
522	/**
523	 *  @private
524	 */
525	public function set kerning(value:Boolean):void
526	{
527		var tf:TextFormat = getTextFormat();
528		tf.kerning = value;
529		setTextFormat(tf);
530	}
531
532    //----------------------------------
533	//  letterSpacing
534    //----------------------------------
535
536	/**
537	 *  The number of additional pixels to appear between each character.
538	 *  A positive value increases the character spacing
539	 *  beyond the normal spacing, while a negative value decreases it.
540	 *
541	 *  @default 0
542	 *
543	 *  @langversion 3.0
544	 *  @playerversion Flash 9
545	 *  @playerversion AIR 1.1
546	 *  @productversion Flex 3
547	 */
548	public function get letterSpacing():Number
549	{
550		return Number(getTextFormat().letterSpacing);
551	}
552
553	/**
554	 *  @private
555	 */
556	public function set letterSpacing(value:Number):void
557	{
558		var tf:TextFormat = getTextFormat();
559		tf.letterSpacing = value;
560		setTextFormat(tf);
561	}
562
563    //----------------------------------
564	//  modifiesSelection
565    //----------------------------------
566
567	/**
568	 *  @private
569	 *  Storage for the modifiesSelection property.
570	 */
571	private var _modifiesSelection:Boolean;
572
573	/**
574	 *  Whether the TextRange modifies the currenly selected text.
575	 *  Set by the constructor.
576	 *
577	 *  @langversion 3.0
578	 *  @playerversion Flash 9
579	 *  @playerversion AIR 1.1
580	 *  @productversion Flex 3
581	 */
582	public function get modifiesSelection():Boolean
583	{
584		return _modifiesSelection;
585	}
586
587    //----------------------------------
588	//  owner
589    //----------------------------------
590
591	/**
592	 *  @private
593	 *  Storage for the owner property.
594	 */
595	private var _owner:UIComponent;
596
597	/**
598	 *  The control that contains the text.
599	 *  The owner control must have a <code>textField</code> property,
600	 *  or, as is the case of the RichTextEditor control,
601	 *  a <code>textArea</code> property.
602	 *  The owner of the text in a RichTextEditor control is the
603	 *  RichTextEditor control, not its TextArea subcontrol.
604	 *  Initially set by the constructor.
605	 *
606	 *  @langversion 3.0
607	 *  @playerversion Flash 9
608	 *  @playerversion AIR 1.1
609	 *  @productversion Flex 3
610	 */
611	public function get owner():UIComponent
612	{
613		return _owner;
614	}
615
616	/**
617	 *  @private
618	 */
619	public function set owner(value:UIComponent):void
620	{
621		_owner = value;
622
623		try
624		{
625			textField = _owner["textArea"]["textField"];
626		}
627		catch(e:Error)
628		{
629			textField = _owner["textField"];
630		}
631	}
632
633    //----------------------------------
634	//  text
635    //----------------------------------
636
637	/**
638	 *  Plain-text contents of the range.
639	 *
640	 *  @langversion 3.0
641	 *  @playerversion Flash 9
642	 *  @playerversion AIR 1.1
643	 *  @productversion Flex 3
644	 */
645	public function get text():String
646	{
647		return textField.text.substring(beginIndex, endIndex);
648	}
649
650	/**
651	 *  @private
652	 */
653	public function set text(value:String):void
654	{
655		var oldBeginIndex:int = beginIndex;
656		textField.replaceText(beginIndex,endIndex,value);
657		// workaround for what seems to be a player bug (#207147)
658		// where a zero length selection is shifted
659		// (if no bug, this code should never execute)
660		if ((oldBeginIndex == 0) && (beginIndex > 0))
661			beginIndex = 0;
662		endIndex = beginIndex + value.length;
663	}
664
665    //----------------------------------
666	//  textAlign
667    //----------------------------------
668
669	/**
670	 *  Alignment of the text in the range.
671	 *  The flash.text.TextFormatAlign constants specify the valid values.
672	 *  Setting this property to any other value has no effect.
673	 *  If the range has multiple alignments, this value is <code>null</code>.
674	 *
675	 *  @see flash.text.TextFormatAlign
676	 *
677	 *  @langversion 3.0
678	 *  @playerversion Flash 9
679	 *  @playerversion AIR 1.1
680	 *  @productversion Flex 3
681	 */
682	public function get textAlign():String
683	{
684		return getTextFormat().align;
685	}
686
687	/**
688	 *  @private
689	 */
690	public function set textAlign(value:String):void
691	{
692		var tf:TextFormat = getTextFormat();
693		tf.align = value;
694		setTextFormat(tf);
695	}
696
697    //----------------------------------
698	//  textDecoration
699    //----------------------------------
700
701	/**
702	 *  Decoration of the font in the range, as "underline"
703	 *  or "normal". Setting the property to any other string results
704	 *  in normal text.
705	 *  If the range has multiple decoration settings, this value is
706	 *  <code>null</code>.
707	 *
708	 *  @langversion 3.0
709	 *  @playerversion Flash 9
710	 *  @playerversion AIR 1.1
711	 *  @productversion Flex 3
712	 */
713	public function get textDecoration():String
714	{
715		return getTextFormat().underline ? "underline" : "normal";
716	}
717
718	/**
719	 *  @private
720	 */
721	public function set textDecoration(value:String):void
722	{
723		var tf:TextFormat = getTextFormat();
724		tf.underline = (value == "underline") ? true : false;
725		setTextFormat(tf);
726	}
727
728    //----------------------------------
729	//  url
730    //----------------------------------
731
732	/**
733	 *  URL for a hypertext link in the range.
734	 *  If the range does not include a link, the value
735	 *  is the empty string.
736	 *  If the range includes multiple links, the value
737	 *  is <code>null</code>.
738	 *
739	 *  @langversion 3.0
740	 *  @playerversion Flash 9
741	 *  @playerversion AIR 1.1
742	 *  @productversion Flex 3
743	 */
744	public function get url():String
745	{
746		return getTextFormat().url;
747	}
748
749	/**
750	 *  @private
751	 */
752	public function set url(value:String):void
753	{
754		var tf:TextFormat = getTextFormat();
755
756		if (value != "")
757		{
758			tf.url = value;
759			tf.target = "_blank";
760		}
761		else if (tf.url != "")
762		{
763			tf.url = "";
764			tf.target = "";
765		}
766
767		setTextFormat(tf);
768	}
769
770    //--------------------------------------------------------------------------
771    //
772    //  Methods
773    //
774    //--------------------------------------------------------------------------
775
776	/**
777	 *  @private
778	 */
779	private function getTextFormat():TextFormat
780	{
781		var tf:TextFormat;
782		if (_modifiesSelection && beginIndex == endIndex)
783			tf = textField.defaultTextFormat;
784		else
785			tf = textField.getTextFormat(beginIndex, endIndex);
786		return tf;
787	}
788
789	/**
790	 *  @private
791	 */
792	private function setTextFormat(tf:TextFormat):void
793	{
794		if (_modifiesSelection && beginIndex == endIndex)
795			textField.defaultTextFormat = tf;
796		else
797			textField.setTextFormat(tf,beginIndex, endIndex);
798	}
799}
800
801}