1 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
2 //>>description: Make words to selectable tokens
3 //>>label: Token textarea
4 //>>group: Tizen:Widgets
6 define( [ '../jquery.mobile.tizen.core' ], function ( ) {
7 //>>excludeEnd("jqmBuildExclude");
9 /* ***************************************************************************
10 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
12 * Permission is hereby granted, free of charge, to any person obtaining a
13 * copy of this software and associated documentation files (the "Software"),
14 * to deal in the Software without restriction, including without limitation
15 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
16 * and/or sell copies of the Software, and to permit persons to whom the
17 * Software is furnished to do so, subject to the following conditions:
19 * The above copyright notice and this permission notice shall be included in
20 * all copies or substantial portions of the Software.
22 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
23 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
24 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
25 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
26 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
27 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
28 * DEALINGS IN THE SOFTWARE.
29 * ***************************************************************************
31 * Author: Kangsik Kim <kangsik81.kim@samsung.com>
32 * Minkyeong Kim <minkyeong.kim@samsung.com>
36 * The TokenTextArea widget changes a text item to a button. It can be comprised of a number of button widgets.
37 * When a user types text and the text gets a specific event to change from a text to a button,
38 * the input text is changed to a TokenTextArea widget.
39 * A user can add the TokenTextArea widget to a contact list, email list, or another list.
40 * The typical use of this widget is composing a number of contacts or phone numbers in a specific area of the screen.
44 * data-link : Represents the page id.
45 * The page contains data for the user, for example, an address book.
46 * If the value is null, anchor button doesn't work. (Default : null)
47 * data-label: Provide a label for a user-guide. (Default : 'To : ')
48 * data-description : This attribute is managing message format.
49 * This message is displayed when widget status was changed to 'focusout'. (Default : '+ {0}')
53 * inputtext ( [string] )
54 * : If argument is not exist, will get a string from inputbox.
55 * If argument is exist, will set a string to inputbox.
57 * : If no argument exists, gets a string of the selected block.
58 * If any button isn't selected on a token text area widget, this method returns "null" value.
59 * When a user call this method with an argument which is a number type,
60 * this method selects the button which is matched with the argument.
61 * add ( text, [number] )
62 * : If second argument does not exist, will insert to a new button at last position.
63 * Insert a new button at indexed position. The position is decided by the second argument.
64 * "index of position" means that the position of inserting a new button is decided by the second argument on "add" method.
65 * For example, if a user call the method like this "add("Tizen", 2)",
66 * new button labed "Tizen" will be inserted on the third position.
68 * : If no argument exists, all buttons are removed.
69 * Remove a button at indexed position.
70 * The position is decided by the second argument. (index: index of button)
72 * : Get a number of buttons.
74 * : This method change a status to 'focusin'.
75 * This status is able to manage a widget.
77 * : Changes the focus status to 'focus out'.
78 * The status is not able to manage a widget.
79 * All buttons that contained in the widget are removed and
80 * summarized message is displayed.
82 * : Remove all of the new DOM elements for the current widget that you created.
86 * create : Occur when create TokenTextArea widget.
87 * select : Occur when a button is selected.
88 * add : Occur when new button is inserted.
89 * remove : Occur when a button is removed.
93 * <div data-role="tokentextarea" data-label="To : " data-link:"#addressbook" data-description="+ {0}">
101 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.
103 To add a token text area widget to the application, use the following code:
105 <div data-role="tokentextarea" data-label="To : " data-link="pageId">
110 @property {String} data-label
111 Sets a label as a guide for the user.
112 For example, while composing an e-mail message, the 'To : ' label is a guide for the user to enter e-mail addresses.
116 @property {String} data-decription
117 Manages the message format.
118 The message is displayed when the widget status changes to focus out
121 @property {String} data-link
122 Sets the ID of the page to which the button links.
126 The create event is fired when the token text area widget is created:
128 <div data-role="tokentextarea">
130 $(".selector").tokentextarea
132 create: function(event, ui)
134 // Handle the create event
140 The select event is fired when a token text area widget button is selected:
142 <div data-role="tokentextarea">
144 $(".selector").bind("select", function(event, ui)
146 // Handle the select event
151 The add event is fired when a token text area widget button is created:
153 <div data-role="tokentextarea">
155 $(".selector").bind("add", function(event, ui)
157 // Handle the add event
162 The remove event is fired when a token text area widget button is removed:
164 <div data-role="tokentextarea">
166 $(".selector").bind("remove", function(event, ui)
168 // Handle the remove event
173 The destroy method is used to remove in the current widget all the new DOM elements that you have created.
175 <div data-role="tokentextarea">
177 $(".selector").tokentextarea("destroy");
183 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.
185 <div data-role="tokentextarea">
187 $(".selector").tokentextarea("inputText", [text]);
191 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.
193 <div data-role="tokentextarea">
195 $(".selector").tokentextarea("select", [index]);
199 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.
201 <div data-role="tokentextarea">
203 $(".selector").tokentextarea("add", [text], [index]);
207 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.
209 <div data-role="tokentextarea">
211 $(".selector").tokentextarea("remove", [index]);
215 The length method is used to retrieve the number of buttons in the token text area widget:
216 <div data-role="tokentextarea">
218 $(".selector").tokentextarea("length");
222 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.
224 <div data-role="tokentextarea">
226 $(".selector").tokentextarea("focusIn");
230 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.
232 <div data-role="tokentextarea">
234 $(".selector").tokentextarea("focusOut");
236 ( function ( $, window, document, undefined ) {
237 $.widget( "tizen.tokentextarea", $.mobile.widget, {
250 description : "+ {0}"
253 _create : function () {
255 $view = this.element,
256 role = $view.jqmData( "role" ),
257 option = this.options,
258 className = "ui-tokentextarea-link",
259 inputbox = $( document.createElement( "input" ) ),
260 labeltag = $( document.createElement( "label" ) ),
261 moreBlock = $( document.createElement( "a" ) );
263 $view.hide().empty().addClass( "ui-" + role );
265 // create a label tag.
266 $( labeltag ).text( option.label ).addClass( "ui-tokentextarea-label" );
267 $view.append( labeltag );
269 // create a input tag
270 $( inputbox ).addClass( "ui-tokentextarea-input ui-tokentextarea-input-visible ui-input-text ui-body-s" );
271 $view.append( inputbox );
273 // create a anchor tag.
274 if ( option.link === null || $.trim( option.link ).length < 1 || $( option.link ).length === 0 ) {
277 $( moreBlock ).attr( "data-role", "button" )
283 .attr( "href", $.trim( option.link ) )
284 .addClass( "ui-tokentextarea-link-base" )
285 .addClass( className );
287 // append default htmlelements to main widget.
288 $view.append( moreBlock );
292 self._focusStatus = "init";
295 $view.attr( "tabindex", -1 ).focusin( function ( e ) {
299 // assign global variables
300 self._viewWidth = $view.innerWidth();
301 self._reservedWidth += self._calcBlockWidth( moreBlock );
302 self._reservedWidth += self._calcBlockWidth( labeltag );
303 self._fontSize = parseInt( $( moreBlock ).css( "font-size" ), 10 );
304 self._currentWidth = self._reservedWidth;
305 self._modifyInputBoxWidth();
309 _bindEvents : function () {
311 $view = self.element,
312 option = self.options,
313 inputbox = $view.find( ".ui-tokentextarea-input" ),
314 moreBlock = $view.find( ".ui-tokentextarea-link-base" ),
317 // delegate a event to HTMLDivElement(each block).
318 $view.delegate( "div", "click", function ( event ) {
319 if ( $( this ).hasClass( "ui-tokentextarea-sblock" ) ) {
320 // If block is selected, it will be removed.
321 self._removeTextBlock();
324 var lockBlock = $view.find( "div.ui-tokentextarea-sblock" );
325 if ( typeof lockBlock !== "undefined" ) {
326 lockBlock.removeClass( "ui-tokentextarea-sblock" ).addClass( "ui-tokentextarea-block" );
328 $( this ).removeClass( "ui-tokentextarea-block" ).addClass( "ui-tokentextarea-sblock" );
329 $view.trigger( "select" );
332 inputbox.bind( "keyup", function ( event ) {
337 var keyValue = event.keyCode,
338 valueString = $( inputbox ).val(),
342 if ( keyValue === 8 ) {
343 if ( valueString.length === 0 ) {
344 self._validateTargetBlock();
346 } else if ( keyValue === 13 || keyValue === 186 || keyValue === 188 ) {
347 if ( valueString.length !== 0 ) {
348 // split content by separators(',', ';')
349 valueStrings = valueString.split ( /[,;]/ );
350 for ( index = 0; index < valueStrings.length; index++ ) {
351 if ( valueStrings[index].length !== 0 && valueStrings[index].replace( /\s/g, "" ).length !== 0 ) {
352 self._addTextBlock( valueStrings[index] );
359 self._unlockTextBlock();
365 moreBlock.click( function () {
366 if ( $( moreBlock ).hasClass( "ui-tokentextarea-link-dim" ) ) {
370 $( inputbox ).removeClass( "ui-tokentextarea-input-visible" ).addClass( "ui-tokentextarea-input-invisible" );
372 $.mobile.changePage( option.link, {
379 $( document ).bind( "pagechange.mbe", function ( event ) {
380 if ( $view.innerWidth() === 0 ) {
383 self._modifyInputBoxWidth();
384 $( inputbox ).removeClass( "ui-tokentextarea-input-invisible" ).addClass( "ui-tokentextarea-input-visible" );
387 $view.bind( "click", function ( event ) {
388 if ( self._focusStatus === "focusOut" ) {
394 // create a textbutton and append this button to parent layer.
395 // @param arg1 : string
396 // @param arg2 : index
397 _addTextBlock : function ( messages, blockIndex ) {
398 if ( arguments.length === 0 ) {
407 $view = self.element,
413 if ( self._viewWidth === 0 ) {
414 self._viewWidth = $view.innerWidth();
417 // Create a new text HTMLDivElement.
418 textBlock = $( document.createElement( 'div' ) );
420 textBlock.text( content ).addClass( "ui-tokentextarea-block" );
421 textBlock.css( {'visibility': 'hidden'} );
423 blocks = $view.find( "div" );
424 if ( index !== null && index <= blocks.length ) {
425 $( blocks[index] ).before( textBlock );
427 $view.find( ".ui-tokentextarea-input" ).before( textBlock );
430 textBlock = self._ellipsisTextBlock( textBlock );
431 textBlock.css( {'visibility': 'visible'} );
433 self._modifyInputBoxWidth();
436 textBlock.fadeIn( "fast", function() {
437 self._currentWidth += self._calcBlockWidth( textBlock );
438 $view.trigger( "add" );
442 _removeTextBlock : function () {
444 $view = this.element,
445 lockBlock = $view.find( "div.ui-tokentextarea-sblock" ),
447 _dummy = function () {};
449 if ( lockBlock !== null && lockBlock.length > 0 ) {
450 self._currentWidth -= self._calcBlockWidth( lockBlock );
452 lockBlock.fadeOut( "fast", function() {
454 self._modifyInputBoxWidth();
457 this._eventRemoveCall = true;
458 if ( $view[0].remove ) {
459 _temp = $view[0].remove;
460 $view[0].remove = _dummy;
462 $view.triggerHandler( "remove" );
464 $view[0].remove = _temp;
466 this._eventRemoveCall = false;
468 $view.find( "div:last" ).removeClass( "ui-tokentextarea-block" ).addClass( "ui-tokentextarea-sblock" );
472 _calcBlockWidth : function ( block ) {
473 return $( block ).outerWidth( true );
476 _unlockTextBlock : function () {
477 var $view = this.element,
478 lockBlock = $view.find( "div.ui-tokentextarea-sblock" );
480 lockBlock.removeClass( "ui-tokentextarea-sblock" ).addClass( "ui-tokentextarea-block" );
484 // call when remove text block by backspace key.
485 _validateTargetBlock : function () {
487 $view = self.element,
488 lastBlock = $view.find( "div:last" ),
491 if ( lastBlock.hasClass( "ui-tokentextarea-sblock" ) ) {
492 self._removeTextBlock();
494 tmpBlock = $view.find( "div.ui-tokentextarea-sblock" );
495 tmpBlock.removeClass( "ui-tokentextarea-sblock" ).addClass( "ui-tokentextarea-block" );
496 lastBlock.removeClass( "ui-tokentextarea-block" ).addClass( "ui-tokentextarea-sblock" );
500 _ellipsisTextBlock : function ( textBlock ) {
502 $view = self.element,
503 maxWidth = self._viewWidth / 2;
505 if ( self._calcBlockWidth( textBlock ) > maxWidth ) {
506 $( textBlock ).width( maxWidth - self._marginWidth );
512 _modifyInputBoxWidth : function () {
514 $view = self.element,
519 blocks = $view.find( "div" ),
523 inputBox = $view.find( ".ui-tokentextarea-input" );
525 if ( $view.width() === 0 ) {
529 if ( self._labelWidth === 0 ) {
530 self._labelWidth = $view.find( ".ui-tokentextarea-label" ).outerWidth( true );
531 self._anchorWidth = $view.find( ".ui-tokentextarea-link-base" ).outerWidth( true );
532 self._marginWidth = parseInt( ( $( inputBox ).css( "margin-left" ) ), 10 );
533 self._marginWidth += parseInt( ( $( inputBox ).css( "margin-right" ) ), 10 );
534 self._viewWidth = $view.innerWidth();
537 margin = self._marginWidth;
538 labelWidth = self._labelWidth;
539 anchorWidth = self._anchorWidth;
540 inputBoxWidth = self._viewWidth - labelWidth;
542 for ( index = 0; index < blocks.length; index += 1 ) {
543 blockWidth = self._calcBlockWidth( blocks[index] );
545 if ( blockWidth >= inputBoxWidth + anchorWidth ) {
546 if ( blockWidth >= inputBoxWidth ) {
547 inputBoxWidth = self._viewWidth - blockWidth;
549 inputBoxWidth = self._viewWidth;
552 if ( blockWidth > inputBoxWidth ) {
553 inputBoxWidth = self._viewWidth - blockWidth;
555 inputBoxWidth -= blockWidth;
560 inputBoxWidth -= margin;
561 if ( inputBoxWidth < anchorWidth * 2 ) {
562 inputBoxWidth = self._viewWidth - margin;
564 $( inputBox ).width( inputBoxWidth - anchorWidth - inputBoxMargin );
567 _stringFormat : function ( expression ) {
569 message = expression,
571 for ( i = 1; i < arguments.length; i += 1 ) {
572 pattern = "{" + ( i - 1 ) + "}";
573 message = message.replace( pattern, arguments[i] );
578 _resizeBlocks : function () {
580 $view = self.element,
581 blocks = $view.find( "div" ),
584 for ( index = 0 ; index < blocks.length ; index += 1 ) {
585 $( blocks[index] ).css( "width", "auto" );
586 blocks[index] = self._ellipsisTextBlock( blocks[index] );
590 //---------------------------------------------------- //
592 //----------------------------------------------------//
596 focusIn : function () {
597 if ( this._focusStatus === "focusIn" ) {
601 var $view = this.element;
603 $view.find( "label" ).show();
604 $view.find( ".ui-tokentextarea-desclabel" ).remove();
605 $view.find( "div.ui-tokentextarea-sblock" ).removeClass( "ui-tokentextarea-sblock" ).addClass( "ui-tokentextarea-block" );
606 $view.find( "div" ).show();
607 $view.find( ".ui-tokentextarea-input" ).removeClass( "ui-tokentextarea-input-invisible" ).addClass( "ui-tokentextarea-input-visible" );
608 $view.find( "a" ).show();
610 // change focus state.
611 this._modifyInputBoxWidth();
612 this._focusStatus = "focusIn";
613 $view.removeClass( "ui-tokentextarea-focusout" ).addClass( "ui-tokentextarea-focusin" );
616 focusOut : function () {
617 if ( this._focusStatus === "focusOut" ) {
622 $view = self.element,
627 label = $view.find( "label" ),
628 more = $view.find( "span" ),
629 blocks = $view.find( "div" ),
630 currentWidth = $view.outerWidth( true ) - more.outerWidth( true ) - label.outerWidth( true ),
633 $view.find( ".ui-tokentextarea-input" ).removeClass( "ui-tokentextarea-input-visible" ).addClass( "ui-tokentextarea-input-invisible" );
634 $view.find( "a" ).hide();
637 currentWidth = currentWidth - self._reservedWidth;
639 for ( index = 0; index < blocks.length; index++ ) {
640 blockWidth = $( blocks[index] ).outerWidth( true );
641 if ( currentWidth - blockWidth <= 0 ) {
642 lastIndex = index - 1;
646 $( blocks[index] ).show();
647 currentWidth -= blockWidth;
650 if ( lastIndex !== blocks.length ) {
651 statement = self._stringFormat( self.options.description, blocks.length - lastIndex - 1 );
652 tempBlock = $( document.createElement( 'label' ) );
653 tempBlock.text( statement );
654 tempBlock.addClass( "ui-tokentextarea-desclabel" );
655 $( blocks[lastIndex] ).after( tempBlock );
658 // update focus state
659 this._focusStatus = "focusOut";
660 $view.removeClass( "ui-tokentextarea-focusin" ).addClass( "ui-tokentextarea-focusout" );
663 inputText : function ( message ) {
664 var $view = this.element;
666 if ( arguments.length === 0 ) {
667 return $view.find( ".ui-tokentextarea-input" ).val();
669 $view.find( ".ui-tokentextarea-input" ).val( message );
673 select : function ( index ) {
674 var $view = this.element,
678 if ( this._focusStatus === "focusOut" ) {
682 if ( arguments.length === 0 ) {
683 // return a selected block.
684 lockBlock = $view.find( "div.ui-tokentextarea-sblock" );
686 return lockBlock.text();
690 // 1. unlock all blocks.
691 this._unlockTextBlock();
692 // 2. select pointed block.
693 blocks = $view.find( "div" );
694 if ( blocks.length > index ) {
695 $( blocks[index] ).removeClass( "ui-tokentextarea-block" ).addClass( "ui-tokentextarea-sblock" );
696 $view.trigger( "select" );
701 add : function ( message, position ) {
702 if ( this._focusStatus === "focusOut" ) {
706 this._addTextBlock( message, position );
709 remove : function ( position ) {
711 $view = this.element,
712 blocks = $view.find( "div" ),
715 _dummy = function () {};
717 if ( this._focusStatus === "focusOut" ) {
721 if ( arguments.length === 0 ) {
722 blocks.fadeOut( "fast", function() {
724 self._modifyInputBoxWidth();
725 this._trigger( "clear" );
727 } else if ( !isNaN( position ) ) {
728 // remove selected button
729 index = ( ( position < blocks.length ) ? position : ( blocks.length - 1 ) );
731 $( blocks[index] ).fadeOut( "fast", function() {
732 $( blocks[index] ).remove();
733 self._modifyInputBoxWidth();
736 this._eventRemoveCall = true;
737 if ( $view[0].remove ) {
738 _temp = $view[0].remove;
739 $view[0].remove = _dummy;
741 $view.triggerHandler( "remove" );
743 $view[0].remove = _temp;
745 this._eventRemoveCall = false;
749 length : function () {
750 return this.element.find( "div" ).length;
753 refresh : function () {
755 $view = this.element;
757 self._viewWidth = $view.innerWidth();
758 self._resizeBlocks();
759 self._modifyInputBoxWidth();
762 destroy : function () {
763 var $view = this.element,
765 _dummy = function () {};
767 if ( this._eventRemoveCall ) {
771 $view.find( "label" ).remove();
772 $view.find( "div" ).undelegate( "click" ).remove();
773 $view.find( "a" ).remove();
774 $view.find( ".ui-tokentextarea-input" ).unbind( "keyup" ).remove();
776 this._eventRemoveCall = true;
777 if ( $view[0].remove ) {
778 _temp = $view[0].remove;
779 $view[0].remove = _dummy;
783 $view[0].remove = _temp;
785 this._eventRemoveCall = false;
787 this._trigger( "destroy" );
791 $( document ).bind( "pagecreate create", function () {
792 $( ":jqmData(role='tokentextarea')" ).tokentextarea();
795 $( window ).bind( "resize", function () {
796 $( ":jqmData(role='tokentextarea')" ).tokentextarea( "refresh" );
798 } ( jQuery, window, document ) );
800 //>>excludeStart("jqmBuildExclude", pragmas.jqmBuildExclude);
802 //>>excludeEnd("jqmBuildExclude");