UnitTC: Additional unit testcases have been added
[platform/framework/web/web-ui-fw.git] / src / widgets / tokentextarea / js / jquery.mobile.tizen.tokentextarea.js
1 /* ***************************************************************************
2  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
3  *
4  * Permission is hereby granted, free of charge, to any person obtaining a
5  * copy of this software and associated documentation files (the "Software"),
6  * to deal in the Software without restriction, including without limitation
7  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8  * and/or sell copies of the Software, and to permit persons to whom the
9  * Software is furnished to do so, subject to the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20  * DEALINGS IN THE SOFTWARE.
21  * ***************************************************************************
22  *
23  *      Author: Kangsik Kim <kangsik81.kim@samsung.com>
24  *                              Minkyeong Kim <minkyeong.kim@samsung.com>
25 */
26
27 /**
28  *      The TokenTextArea widget changes a text item to a button. It can be comprised of a number of button widgets. 
29  *      When a user types text and the text gets a specific event to change from a text to a button, 
30  *      the input text is changed to a TokenTextArea widget.
31  *      A user can add the TokenTextArea widget to a contact list, email list, or another list.
32  *      The typical use of this widget is composing a number of contacts or phone numbers in a specific area of the screen.
33  *
34  *      HTML Attributes:
35  *
36  *              data-link : Represents the page id.
37  *                              The page contains data for the user, for example, an address book.
38  *                              If the value is null, anchor button doesn't work. (Default : null)
39  *              data-label:     Provide a label for a user-guide. (Default : 'To : ')
40  *              data-description : This attribute is managing message format.
41  *                              This message is displayed when widget status was changed to 'focusout'. (Default : '+ {0}')
42  *
43  *      APIs:
44  *
45  *              inputtext (  [string]  )
46  *                      : If argument is not exist, will get a string from inputbox.
47  *                      If argument is exist, will set a string to inputbox.
48  *              select (  [number]  )
49  *                      : If no argument exists, gets a string of the selected block.
50  *                      If any button isn't selected on a token text area widget, this method returns "null" value.
51  *                      When a user call this method with an argument which is a number type,
52  *                      this method selects the button which is matched with the argument.
53  *              add ( text, [number] )
54  *                      :  If second argument does not exist, will insert to a new button at last position.
55  *                      Insert a new button at indexed position. The position is decided by the second argument.
56  *                      "index of position" means that the position of inserting a new button is decided by the second argument on "add" method.
57  *                      For example, if a user call the method like this "add("Tizen", 2)",
58  *                      new button labed "Tizen" will be inserted on the third position.
59  *              remove ( [number] )
60  *                      : If no argument exists, all buttons are removed.
61  *                      Remove a button at indexed position.
62  *                      The position is decided by the second argument. (index: index of button)
63  *              length ( void )
64  *                      : Get a number of buttons.
65  *              foucsIn ( void )
66  *                      : This method change a status to 'focusin'.
67  *                      This status is able to manage a widget.
68  *              focusOut ( void )
69  *                      : Changes the focus status to 'focus out'.
70  *                      The status is not able to manage a widget.
71  *                      All buttons that contained in the widget are removed and
72  *                      summarized message is displayed.
73  *              destroy ( void )
74  *                      : Remove all of the new DOM elements for the current widget that you created.
75  *
76  *      Events:
77  *
78  *              create : Occur when create TokenTextArea widget.
79  *              select : Occur when a button is selected.
80  *              add : Occur when new button is inserted.
81  *              remove : Occur when a button is removed.
82  *
83  *      Examples:
84  *
85  *              <div data-role="tokentextarea" data-label="To : " data-link:"#addressbook" data-description="+ {0}">
86  *              </div>
87  *
88  */
89
90
91 /**
92         @class TokenTextArea
93         The TokenTextArea widget enables the user to enter text and convert it to a button. Each button that is created from entered text as a result of a change event forms a token text area widget. This widget is useful in composing an e-mail or SMS message to a group of addresses, each of which is a clickable item for more actions, such as copying, editing, or removing the address.
94
95         To add a token text area widget to the application, use the following code:
96
97                 <div data-role="tokentextarea" data-label="To : " data-link="pageId">
98                 </div>
99 */
100
101 /**
102         @property {String}  data-label
103         Sets a label as a guide for the user.
104         For example, while composing an e-mail message, the 'To : ' label is a guide for the user to enter e-mail addresses.
105 */
106
107 /**
108         @property {String} data-decription
109         Manages the message format.
110         The message is displayed when the widget status changes to focus out
111  */
112 /**
113         @property {String} data-link
114         Sets the ID of the page to which the button links.
115 */
116 /**
117         @event create
118         The create event is fired when the token text area widget is created:
119
120                 <div data-role="tokentextarea">
121                 </div>
122                 // Option 01
123                 $(".selector").tokentextarea
124                 ({
125                         create: function(event, ui)
126                         {
127                                 // Handle the create event
128                         }
129                 });
130                 // Option 02
131                 $(".selector").bind("create", function(event, ui)
132                 {
133                         // Handle the create event
134                 });
135 **/
136 /**
137         @event select
138         The select event is fired when a token text area widget button is selected:
139
140                 <div data-role="tokentextarea">
141                 </div>
142                 // Option 01
143                 $(".selector").tokentextarea
144                 ({
145                         select: function(event, ui)
146                         {
147                         // Handle the select event
148                         }
149                 });
150                 // Option 02
151                 $(".selector").bind("select", function(event, ui)
152                 {
153                         // Handle the select event
154                 });     
155 */
156 /**
157         @event add
158         The add event is fired when a token text area widget button is created:
159
160                 <div data-role="tokentextarea">
161                 </div>
162                 // Option 01
163                 $(".selector").tokentextarea
164                 ({
165                         add: function(event, ui)
166                         {
167                                 // Handle the add event
168                         }
169                 });
170                 // Option 02
171                 $(".selector").bind("add", function(event, ui)
172                 {
173                 // Handle the add event
174                 });
175 */
176 /**
177         @event remove
178         The remove event is fired when a token text area widget button is removed:
179
180                 <div data-role="tokentextarea">
181                 </div>
182                 // Option 01
183                 $(".selector").tokentextarea
184                 ({
185                         remove: function(event, ui)
186                         {
187                         // Handle the remove event
188                         }
189                 });
190                 // Option 02
191                 $(".selector").bind("remove", function(event, ui)
192                 {
193                         // Handle the remove event
194                 });
195 */
196 /**
197         @method destroy
198         The destroy method is used to remove in the current widget all the new DOM elements that you have created.
199         
200                 <div data-role="tokentextarea">
201                 </div>
202                 $(".selector").tokentextarea("destroy");
203         
204         @since Tizen2.0
205 */
206 /**
207         @method inputText
208         The inputText method is used to manage the widget input box text. If no parameter is set, the method gets the input box text. If a parameter is set, the parameter text is set in the input box.
209         
210                 <div data-role="tokentextarea">
211                 </div>
212                 $(".selector").tokentextarea("inputText", [text]);
213 */
214 /**
215         @method select
216         The select method is used to select a token text area widget button based on its index value. If no index value is defined, the method returns the string of the selected block. If there are no buttons present in the widget, the method returns null.
217
218                 <div data-role="tokentextarea">
219                 </div>
220                 $(".selector").tokentextarea("select", [index]);
221 */
222 /**
223         @method add
224         The add method is used to add a new token text area widget button with the specified label text at the specified index position. If the index parameter is not defined, the widget button is added at the bottom of the list. For example, the $(".selector").tokentextarea("add", "Tizen", 2) method call creates a new widget button labeled 'Tizen' at the third position in the widget.
225
226                 <div data-role="tokentextarea">
227                 </div>
228                 $(".selector").tokentextarea("add", [text], [index]);
229 */
230 /**
231         @method remove
232         The remove method is used to remove a token text area widget button at the specified index position. If the parameter is not defined, all the widget buttons are removed.
233
234                 <div data-role="tokentextarea">
235                 </div>
236                 $(".selector").tokentextarea("remove", [index]);
237 */
238 /**
239         @method length
240         The length method is used to retrieve the number of buttons in the token text area widget:
241                 <div data-role="tokentextarea">
242                 </div>
243                 $(".selector").tokentextarea("length");
244 */
245 /**
246         @method focusIn
247         The focusIn method is used to set the focus status to "focus in". This focus state enables the user to add or remove buttons in the token text area widget.
248
249                 <div data-role="tokentextarea">
250                 </div>
251                 $(".selector").tokentextarea("focusIn");
252 */
253 /**
254         @method focusOut
255         The focusOut method is used to set the focus status to "focus out". In this focus state, the user cannot manage the buttons in the widget, all the buttons are removed, and a message is displayed.
256
257                 <div data-role="tokentextarea">
258                 </div>
259                 $(".selector").tokentextarea("focusOut");
260 */
261 ( function ( $, window, document, undefined ) {
262         $.widget( "tizen.tokentextarea", $.mobile.widget, {
263                 _focusStatus : null,
264                 _items : null,
265                 _viewWidth : 0,
266                 _reservedWidth : 0,
267                 _currentWidth : 0,
268                 _fontSize : 0,
269                 _anchorWidth : 0,
270                 _labelWidth : 0,
271                 _marginWidth : 0,
272                 options : {
273                         label : "To : ",
274                         link : null,
275                         description : "+ {0}"
276                 },
277
278                 _create : function () {
279                         var self = this,
280                                 $view = this.element,
281                                 role = $view.jqmData( "role" ),
282                                 option = this.options,
283                                 className = "ui-tokentextarea-link",
284                                 inputbox = $( document.createElement( "input" ) ),
285                                 labeltag = $( document.createElement( "label" ) ),
286                                 moreBlock = $( document.createElement( "a" ) );
287
288                         $view.hide().empty().addClass( "ui-" + role );
289
290                         // create a label tag.
291                         $( labeltag ).text( option.label ).addClass( "ui-tokentextarea-label" );
292                         $view.append( labeltag );
293
294                         // create a input tag
295                         $( inputbox ).addClass( "ui-tokentextarea-input ui-tokentextarea-input-visible ui-input-text ui-body-s" );
296                         $view.append( inputbox );
297
298                         // create a anchor tag.
299                         if ( option.link === null || $.trim( option.link ).length < 1 || $( option.link ).length === 0 ) {
300                                 className += "-dim";
301                         }
302                         $( moreBlock ).attr( "data-role", "button" )
303                                 .buttonMarkup( {
304                                         inline: true,
305                                         icon: "plus",
306                                         style: "circle"
307                                 })
308                                 .attr( "href", $.trim( option.link ) )
309                                 .addClass( "ui-tokentextarea-link-base" )
310                                 .addClass( className );
311
312                         // append default htmlelements to main widget.
313                         $view.append( moreBlock );
314
315                         // bind a event
316                         this._bindEvents();
317                         self._focusStatus = "init";
318                         // display widget
319                         $view.show();
320                         $view.attr( "tabindex", -1 ).focusin( function ( e ) {
321                                 self.focusIn();
322                         });
323
324                         // assign global variables
325                         self._viewWidth = $view.innerWidth();
326                         self._reservedWidth += self._calcBlockWidth( moreBlock );
327                         self._reservedWidth += self._calcBlockWidth( labeltag );
328                         self._fontSize = parseInt( $( moreBlock ).css( "font-size" ), 10 );
329                         self._currentWidth = self._reservedWidth;
330                         self._modifyInputBoxWidth();
331                 },
332
333                 // bind events
334                 _bindEvents : function () {
335                         var self = this,
336                                 $view = self.element,
337                                 option = self.options,
338                                 inputbox = $view.find( ".ui-tokentextarea-input" ),
339                                 moreBlock = $view.find( ".ui-tokentextarea-link-base" ),
340                                 isSeparator = false;
341
342                         // delegate a event to HTMLDivElement(each block).
343                         $view.delegate( "div", "click", function ( event ) {
344                                 if ( $( this ).hasClass( "ui-tokentextarea-sblock" ) ) {
345                                         // If block is selected, it will be removed.
346                                         self._removeTextBlock();
347                                 }
348
349                                 var lockBlock = $view.find( "div.ui-tokentextarea-sblock" );
350                                 if ( typeof lockBlock !== "undefined" ) {
351                                         lockBlock.removeClass( "ui-tokentextarea-sblock" ).addClass( "ui-tokentextarea-block" );
352                                 }
353                                 $( this ).removeClass( "ui-tokentextarea-block" ).addClass( "ui-tokentextarea-sblock" );
354                                 self._trigger( "select" );
355                         });
356
357                         inputbox.bind( "keyup", function ( event ) {
358                                 // 8  : backspace
359                                 // 13 : Enter
360                                 // 186 : semi-colon
361                                 // 188 : comma
362                                 var keyValue = event.keyCode,
363                                         valueString = $( inputbox ).val(),
364                                         valueStrings = [],
365                                         index;
366
367                                 if ( keyValue === 8 ) {
368                                         if ( valueString.length === 0 ) {
369                                                 self._validateTargetBlock();
370                                         }
371                                 } else if ( keyValue === 13 || keyValue === 186 || keyValue === 188 ) {
372                                         if ( valueString.length !== 0 ) {
373                                                 // split content by separators(',', ';')
374                                                 valueStrings = valueString.split ( /[,;]/ );
375                                                 for ( index = 0; index < valueStrings.length; index++ ) {
376                                                         if ( valueStrings[index].length !== 0 && valueStrings[index].replace( /\s/g, "" ).length !== 0 ) {
377                                                                 self._addTextBlock( valueStrings[index] );
378                                                         }
379                                                 }
380                                         }
381                                         inputbox.val( "" );
382                                         isSeparator = true;
383                                 } else {
384                                         self._unlockTextBlock();
385                                 }
386
387                                 return !isSeparator;
388                         });
389
390                         moreBlock.click( function () {
391                                 if ( $( moreBlock ).hasClass( "ui-tokentextarea-link-dim" ) ) {
392                                         return;
393                                 }
394
395                                 $( inputbox ).removeClass( "ui-tokentextarea-input-visible" ).addClass( "ui-tokentextarea-input-invisible" );
396
397                                 $.mobile.changePage( option.link, {
398                                         transition: "slide",
399                                         reverse: false,
400                                         changeHash: false
401                                 });
402                         });
403
404                         $( document ).bind( "pagechange.mbe", function ( event ) {
405                                 if ( $view.innerWidth() === 0 ) {
406                                         return ;
407                                 }
408                                 var inputBox = $view.find( ".ui-tokentextarea-input" );
409                                 self._modifyInputBoxWidth();
410                                 $( inputbox ).removeClass( "ui-tokentextarea-input-invisible" ).addClass( "ui-tokentextarea-input-visible" );
411                         });
412
413                         $view.bind( "click", function ( event ) {
414                                 if ( self._focusStatus === "focusOut" ) {
415                                         self.focusIn();
416                                 }
417                         });
418                 },
419
420                 // create a textbutton and append this button to parent layer.
421                 // @param arg1 : string
422                 // @param arg2 : index
423                 _addTextBlock : function ( messages, blockIndex ) {
424                         if ( arguments.length === 0 ) {
425                                 return;
426                         }
427
428                         if ( !messages ) {
429                                 return ;
430                         }
431
432                         var self = this,
433                                 $view = self.element,
434                                 content = messages,
435                                 index = blockIndex,
436                                 blocks = null,
437                                 textBlock = null;
438
439                         if ( self._viewWidth === 0 ) {
440                                 self._viewWidth = $view.innerWidth();
441                         }
442
443                         // Create a new text HTMLDivElement.
444                         textBlock = $( document.createElement( 'div' ) );
445
446                         textBlock.text( content ).addClass( "ui-tokentextarea-block" );
447                         textBlock.css( {'visibility': 'hidden'} );
448
449                         blocks = $view.find( "div" );
450                         if ( index !== null && index <= blocks.length ) {
451                                 $( blocks[index] ).before( textBlock );
452                         } else {
453                                 $view.find( ".ui-tokentextarea-input" ).before( textBlock );
454                         }
455
456                         textBlock = self._ellipsisTextBlock( textBlock );
457                         textBlock.css( {'visibility': 'visible'} );
458
459                         self._currentWidth += self._calcBlockWidth( textBlock );
460                         self._modifyInputBoxWidth();
461                         self._trigger( "add" );
462                 },
463
464                 _removeTextBlock : function () {
465                         var self = this,
466                                 $view = this.element,
467                                 lockBlock = $view.find( "div.ui-tokentextarea-sblock" );
468
469                         if ( lockBlock !== null && lockBlock.length > 0 ) {
470                                 self._currentWidth -= self._calcBlockWidth( lockBlock );
471                                 lockBlock.remove();
472                                 self._modifyInputBoxWidth();
473                                 this._trigger( "remove" );
474                         } else {
475                                 $view.find( "div:last" ).removeClass( "ui-tokentextarea-block" ).addClass( "ui-tokentextarea-sblock" );
476                         }
477                 },
478
479                 _calcBlockWidth : function ( block ) {
480                         return $( block ).outerWidth( true );
481                 },
482
483                 _unlockTextBlock : function () {
484                         var $view = this.element,
485                                 lockBlock = $view.find( "div.ui-tokentextarea-sblock" );
486                         if ( lockBlock ) {
487                                 lockBlock.removeClass( "ui-tokentextarea-sblock" ).addClass( "ui-tokentextarea-block" );
488                         }
489                 },
490
491                 // call when remove text block by backspace key.
492                 _validateTargetBlock : function () {
493                         var self = this,
494                                 $view = self.element,
495                                 lastBlock = $view.find( "div:last" ),
496                                 tmpBlock = null;
497
498                         if ( lastBlock.hasClass( "ui-tokentextarea-sblock" ) ) {
499                                 self._removeTextBlock();
500                         } else {
501                                 tmpBlock = $view.find( "div.ui-tokentextarea-sblock" );
502                                 tmpBlock.removeClass( "ui-tokentextarea-sblock" ).addClass( "ui-tokentextarea-block" );
503                                 lastBlock.removeClass( "ui-tokentextarea-block" ).addClass( "ui-tokentextarea-sblock" );
504                         }
505                 },
506
507                 _ellipsisTextBlock : function ( textBlock ) {
508                         var self = this,
509                                 $view = self.element,
510                                 maxWidth = self._viewWidth - ( self._labelWidth + self._anchorWidth ) * 2;
511
512                         if ( self._calcBlockWidth( textBlock ) > maxWidth ) {
513                                 $( textBlock ).width( maxWidth - self._marginWidth );
514                         }
515
516                         return textBlock;
517                 },
518
519                 _modifyInputBoxWidth : function () {
520                         var self = this,
521                                 $view = self.element,
522                                 margin = 0,
523                                 labelWidth = 0,
524                                 anchorWidth = 0,
525                                 inputBoxWidth = 0,
526                                 blocks = $view.find( "div" ),
527                                 blockWidth = 0,
528                                 index = 0,
529                                 inputBoxMargin = 10,
530                                 inputBox = $view.find( ".ui-tokentextarea-input" );
531
532                         if ( $view.width() === 0 ) {
533                                 return;
534                         }
535
536                         if ( self._labelWidth === 0 ) {
537                                 self._labelWidth = $view.find( ".ui-tokentextarea-label" ).outerWidth( true );
538                                 self._anchorWidth = $view.find( ".ui-tokentextarea-link-base" ).outerWidth( true );
539                                 self._marginWidth = parseInt( ( $( inputBox ).css( "margin-left" ) ), 10 );
540                                 self._marginWidth += parseInt( ( $( inputBox ).css( "margin-right" ) ), 10 );
541                                 self._viewWidth = $view.innerWidth();
542                         }
543
544                         margin = self._marginWidth;
545                         labelWidth = self._labelWidth;
546                         anchorWidth = self._anchorWidth;
547                         inputBoxWidth = self._viewWidth - labelWidth;
548
549                         for ( index = 0; index < blocks.length; index += 1 ) {
550                                 blockWidth = self._calcBlockWidth( blocks[index] );
551
552                                 if ( blockWidth >= inputBoxWidth + anchorWidth ) {
553                                         if ( blockWidth >= inputBoxWidth ) {
554                                                 inputBoxWidth = self._viewWidth - blockWidth;
555                                         } else {
556                                                 inputBoxWidth = self._viewWidth;
557                                         }
558                                 } else {
559                                         if ( blockWidth >= inputBoxWidth ) {
560                                                 inputBoxWidth = self._viewWidth - blockWidth;
561                                         } else {
562                                                 inputBoxWidth -= blockWidth;
563                                         }
564                                 }
565                         }
566
567                         inputBoxWidth -= margin;
568                         if ( inputBoxWidth < anchorWidth * 2 ) {
569                                 inputBoxWidth = self._viewWidth - margin;
570                         }
571                         $( inputBox ).width( inputBoxWidth - anchorWidth - inputBoxMargin );
572                 },
573
574                 _stringFormat : function ( expression ) {
575                         var pattern = null,
576                                 message = expression,
577                                 i = 0;
578                         for ( i = 1; i < arguments.length; i += 1 ) {
579                                 pattern = "{" + ( i - 1 ) + "}";
580                                 message = message.replace( pattern, arguments[i] );
581                         }
582                         return message;
583                 },
584
585                 _resizeBlocks : function () {
586                         var self = this,
587                                 $view = self.element,
588                                 blocks = $view.find( "div" ),
589                                 index = 0;
590
591                         for ( index = 0 ; index < blocks.length ; index += 1 ) {
592                                 $( blocks[index] ).css( "width", "auto" );
593                                 blocks[index] = self._ellipsisTextBlock( blocks[index] );
594                         }
595                 },
596
597                 //---------------------------------------------------- //
598                 //                                      Public Method   //
599                 //----------------------------------------------------//
600                 //
601                 // Focus In Event
602                 //
603                 focusIn : function () {
604                         if ( this._focusStatus === "focusIn" ) {
605                                 return;
606                         }
607
608                         var $view = this.element;
609
610                         $view.find( "label" ).show();
611                         $view.find( ".ui-tokentextarea-desclabel" ).remove();
612                         $view.find( "div.ui-tokentextarea-sblock" ).removeClass( "ui-tokentextarea-sblock" ).addClass( "ui-tokentextarea-block" );
613                         $view.find( "div" ).show();
614                         $view.find( ".ui-tokentextarea-input" ).removeClass( "ui-tokentextarea-input-invisible" ).addClass( "ui-tokentextarea-input-visible" );
615                         $view.find( "a" ).show();
616
617                         // change focus state.
618                         this._modifyInputBoxWidth();
619                         this._focusStatus = "focusIn";
620                         $view.removeClass( "ui-tokentextarea-focusout" ).addClass( "ui-tokentextarea-focusin" );
621                 },
622
623                 focusOut : function () {
624                         if ( this._focusStatus === "focusOut" ) {
625                                 return;
626                         }
627
628                         var self = this,
629                                 $view = self.element,
630                                 tempBlock = null,
631                                 statement = "",
632                                 index = 0,
633                                 lastIndex = 10,
634                                 label = $view.find( "label" ),
635                                 more = $view.find( "span" ),
636                                 blocks = $view.find( "div" ),
637                                 currentWidth = $view.outerWidth( true ) - more.outerWidth( true ) - label.outerWidth( true ),
638                                 blockWidth = 0;
639
640                         $view.find( ".ui-tokentextarea-input" ).removeClass( "ui-tokentextarea-input-visible" ).addClass( "ui-tokentextarea-input-invisible" );
641                         $view.find( "a" ).hide();
642                         blocks.hide();
643
644                         currentWidth = currentWidth - self._reservedWidth;
645
646                         for ( index = 0; index < blocks.length; index++ ) {
647                                 blockWidth = $( blocks[index] ).outerWidth( true );
648                                 if ( currentWidth - blockWidth <= 0 ) {
649                                         lastIndex = index - 1;
650                                         break;
651                                 }
652
653                                 $( blocks[index] ).show();
654                                 currentWidth -= blockWidth;
655                         }
656
657                         if ( lastIndex !== blocks.length ) {
658                                 statement = self._stringFormat( self.options.description, blocks.length - lastIndex - 1 );
659                                 tempBlock = $( document.createElement( 'label' ) );
660                                 tempBlock.text( statement );
661                                 tempBlock.addClass( "ui-tokentextarea-desclabel" ).addClass( "ui-tokentextarea-desclabel" );
662                                 $( blocks[lastIndex] ).after( tempBlock );
663                         }
664
665                         // update focus state
666                         this._focusStatus = "focusOut";
667                         $view.removeClass( "ui-tokentextarea-focusin" ).addClass( "ui-tokentextarea-focusout" );
668                 },
669
670                 inputText : function ( message ) {
671                         var $view = this.element;
672
673                         if ( arguments.length === 0 ) {
674                                 return $view.find( ".ui-tokentextarea-input" ).val();
675                         }
676                         $view.find( ".ui-tokentextarea-input" ).val( message );
677                         return message;
678                 },
679
680                 select : function ( index ) {
681                         var $view = this.element,
682                                 lockBlock = null,
683                                 blocks = null;
684
685                         if ( this._focusStatus === "focusOut" ) {
686                                 return;
687                         }
688
689                         if ( arguments.length === 0 ) {
690                                 // return a selected block.
691                                 lockBlock = $view.find( "div.ui-tokentextarea-sblock" );
692                                 if ( lockBlock ) {
693                                         return lockBlock.text();
694                                 }
695                                 return null;
696                         }
697                         // 1. unlock all blocks.
698                         this._unlockTextBlock();
699                         // 2. select pointed block.
700                         blocks = $view.find( "div" );
701                         if ( blocks.length > index ) {
702                                 $( blocks[index] ).removeClass( "ui-tokentextarea-block" ).addClass( "ui-tokentextarea-sblock" );
703                                 this._trigger( "select" );
704                         }
705                         return null;
706                 },
707
708                 add : function ( message, position ) {
709                         if ( this._focusStatus === "focusOut" ) {
710                                 return;
711                         }
712
713                         this._addTextBlock( message, position );
714                 },
715
716                 remove : function ( position ) {
717                         var self = this,
718                                 $view = this.element,
719                                 blocks = $view.find( "div" ),
720                                 index = 0;
721                         if ( this._focusStatus === "focusOut" ) {
722                                 return;
723                         }
724
725                         if ( arguments.length === 0 ) {
726                                 blocks.remove();
727                                 this._trigger( "clear" );
728                         } else if ( !isNaN( position ) ) {
729                                 // remove selected button
730                                 index = ( ( position < blocks.length ) ? position : ( blocks.length - 1 ) );
731                                 $( blocks[index] ).remove();
732                                 this._trigger( "remove" );
733                         }
734                         self._modifyInputBoxWidth();
735                 },
736
737                 length : function () {
738                         return this.element.find( "div" ).length;
739                 },
740
741                 refresh : function () {
742                         var self = this,
743                                 $view = this.element;
744
745                         self._viewWidth = $view.innerWidth();
746                         self._resizeBlocks();
747                         self._modifyInputBoxWidth();
748                 },
749
750                 destroy : function () {
751                         var $view = this.element;
752
753                         $view.find( "label" ).remove();
754                         $view.find( "div" ).undelegate( "click" ).remove();
755                         $view.find( "a" ).remove();
756                         $view.find( ".ui-tokentextarea-input" ).unbind( "keyup" ).remove();
757
758                         this._trigger( "destroy" );
759                 }
760         });
761
762         $( document ).bind( "pagecreate create", function () {
763                 $( ":jqmData(role='tokentextarea')" ).tokentextarea();
764         });
765
766         $( window ).bind( "resize", function () {
767                 $( ":jqmData(role='tokentextarea')" ).tokentextarea( "refresh" );
768         });
769 } ( jQuery, window, document ) );