1 /* ***************************************************************************
2 * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
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:
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
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 * ***************************************************************************
23 * Author: Kangsik Kim <kangsik81.kim@samsung.com>
24 * Minkyeong Kim <minkyeong.kim@samsung.com>
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.
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}')
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.
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.
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)
64 * : Get a number of buttons.
66 * : This method change a status to 'focusin'.
67 * This status is able to manage a widget.
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.
74 * : Remove all of the new DOM elements for the current widget that you created.
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.
85 * <div data-role="tokentextarea" data-label="To : " data-link:"#addressbook" data-description="+ {0}">
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.
95 To add a token text area widget to the application, use the following code:
97 <div data-role="tokentextarea" data-label="To : " data-link="pageId">
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.
108 @property {String} data-decription
109 Manages the message format.
110 The message is displayed when the widget status changes to focus out
113 @property {String} data-link
114 Sets the ID of the page to which the button links.
118 The create event is fired when the token text area widget is created:
120 <div data-role="tokentextarea">
123 $(".selector").tokentextarea
125 create: function(event, ui)
127 // Handle the create event
131 $(".selector").bind("create", function(event, ui)
133 // Handle the create event
138 The select event is fired when a token text area widget button is selected:
140 <div data-role="tokentextarea">
143 $(".selector").tokentextarea
145 select: function(event, ui)
147 // Handle the select event
151 $(".selector").bind("select", function(event, ui)
153 // Handle the select event
158 The add event is fired when a token text area widget button is created:
160 <div data-role="tokentextarea">
163 $(".selector").tokentextarea
165 add: function(event, ui)
167 // Handle the add event
171 $(".selector").bind("add", function(event, ui)
173 // Handle the add event
178 The remove event is fired when a token text area widget button is removed:
180 <div data-role="tokentextarea">
183 $(".selector").tokentextarea
185 remove: function(event, ui)
187 // Handle the remove event
191 $(".selector").bind("remove", function(event, ui)
193 // Handle the remove event
198 The destroy method is used to remove in the current widget all the new DOM elements that you have created.
200 <div data-role="tokentextarea">
202 $(".selector").tokentextarea("destroy");
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.
210 <div data-role="tokentextarea">
212 $(".selector").tokentextarea("inputText", [text]);
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.
218 <div data-role="tokentextarea">
220 $(".selector").tokentextarea("select", [index]);
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.
226 <div data-role="tokentextarea">
228 $(".selector").tokentextarea("add", [text], [index]);
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.
234 <div data-role="tokentextarea">
236 $(".selector").tokentextarea("remove", [index]);
240 The length method is used to retrieve the number of buttons in the token text area widget:
241 <div data-role="tokentextarea">
243 $(".selector").tokentextarea("length");
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.
249 <div data-role="tokentextarea">
251 $(".selector").tokentextarea("focusIn");
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.
257 <div data-role="tokentextarea">
259 $(".selector").tokentextarea("focusOut");
261 ( function ( $, window, document, undefined ) {
262 $.widget( "tizen.tokentextarea", $.mobile.widget, {
275 description : "+ {0}"
278 _create : function () {
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" ) );
288 $view.hide().empty().addClass( "ui-" + role );
290 // create a label tag.
291 $( labeltag ).text( option.label ).addClass( "ui-tokentextarea-label" );
292 $view.append( labeltag );
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 );
298 // create a anchor tag.
299 if ( option.link === null || $.trim( option.link ).length < 1 || $( option.link ).length === 0 ) {
302 $( moreBlock ).attr( "data-role", "button" )
308 .attr( "href", $.trim( option.link ) )
309 .addClass( "ui-tokentextarea-link-base" )
310 .addClass( className );
312 // append default htmlelements to main widget.
313 $view.append( moreBlock );
317 self._focusStatus = "init";
320 $view.attr( "tabindex", -1 ).focusin( function ( e ) {
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();
334 _bindEvents : function () {
336 $view = self.element,
337 option = self.options,
338 inputbox = $view.find( ".ui-tokentextarea-input" ),
339 moreBlock = $view.find( ".ui-tokentextarea-link-base" ),
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();
349 var lockBlock = $view.find( "div.ui-tokentextarea-sblock" );
350 if ( typeof lockBlock !== "undefined" ) {
351 lockBlock.removeClass( "ui-tokentextarea-sblock" ).addClass( "ui-tokentextarea-block" );
353 $( this ).removeClass( "ui-tokentextarea-block" ).addClass( "ui-tokentextarea-sblock" );
354 self._trigger( "select" );
357 inputbox.bind( "keyup", function ( event ) {
362 var keyValue = event.keyCode,
363 valueString = $( inputbox ).val(),
367 if ( keyValue === 8 ) {
368 if ( valueString.length === 0 ) {
369 self._validateTargetBlock();
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] );
384 self._unlockTextBlock();
390 moreBlock.click( function () {
391 if ( $( moreBlock ).hasClass( "ui-tokentextarea-link-dim" ) ) {
395 $( inputbox ).removeClass( "ui-tokentextarea-input-visible" ).addClass( "ui-tokentextarea-input-invisible" );
397 $.mobile.changePage( option.link, {
404 $( document ).bind( "pagechange.mbe", function ( event ) {
405 if ( $view.innerWidth() === 0 ) {
408 var inputBox = $view.find( ".ui-tokentextarea-input" );
409 self._modifyInputBoxWidth();
410 $( inputbox ).removeClass( "ui-tokentextarea-input-invisible" ).addClass( "ui-tokentextarea-input-visible" );
413 $view.bind( "click", function ( event ) {
414 if ( self._focusStatus === "focusOut" ) {
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 ) {
433 $view = self.element,
439 if ( self._viewWidth === 0 ) {
440 self._viewWidth = $view.innerWidth();
443 // Create a new text HTMLDivElement.
444 textBlock = $( document.createElement( 'div' ) );
446 textBlock.text( content ).addClass( "ui-tokentextarea-block" );
447 textBlock.css( {'visibility': 'hidden'} );
449 blocks = $view.find( "div" );
450 if ( index !== null && index <= blocks.length ) {
451 $( blocks[index] ).before( textBlock );
453 $view.find( ".ui-tokentextarea-input" ).before( textBlock );
456 textBlock = self._ellipsisTextBlock( textBlock );
457 textBlock.css( {'visibility': 'visible'} );
459 self._currentWidth += self._calcBlockWidth( textBlock );
460 self._modifyInputBoxWidth();
461 self._trigger( "add" );
464 _removeTextBlock : function () {
466 $view = this.element,
467 lockBlock = $view.find( "div.ui-tokentextarea-sblock" );
469 if ( lockBlock !== null && lockBlock.length > 0 ) {
470 self._currentWidth -= self._calcBlockWidth( lockBlock );
472 self._modifyInputBoxWidth();
473 this._trigger( "remove" );
475 $view.find( "div:last" ).removeClass( "ui-tokentextarea-block" ).addClass( "ui-tokentextarea-sblock" );
479 _calcBlockWidth : function ( block ) {
480 return $( block ).outerWidth( true );
483 _unlockTextBlock : function () {
484 var $view = this.element,
485 lockBlock = $view.find( "div.ui-tokentextarea-sblock" );
487 lockBlock.removeClass( "ui-tokentextarea-sblock" ).addClass( "ui-tokentextarea-block" );
491 // call when remove text block by backspace key.
492 _validateTargetBlock : function () {
494 $view = self.element,
495 lastBlock = $view.find( "div:last" ),
498 if ( lastBlock.hasClass( "ui-tokentextarea-sblock" ) ) {
499 self._removeTextBlock();
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" );
507 _ellipsisTextBlock : function ( textBlock ) {
509 $view = self.element,
510 maxWidth = self._viewWidth - ( self._labelWidth + self._anchorWidth ) * 2;
512 if ( self._calcBlockWidth( textBlock ) > maxWidth ) {
513 $( textBlock ).width( maxWidth - self._marginWidth );
519 _modifyInputBoxWidth : function () {
521 $view = self.element,
526 blocks = $view.find( "div" ),
530 inputBox = $view.find( ".ui-tokentextarea-input" );
532 if ( $view.width() === 0 ) {
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();
544 margin = self._marginWidth;
545 labelWidth = self._labelWidth;
546 anchorWidth = self._anchorWidth;
547 inputBoxWidth = self._viewWidth - labelWidth;
549 for ( index = 0; index < blocks.length; index += 1 ) {
550 blockWidth = self._calcBlockWidth( blocks[index] );
552 if ( blockWidth >= inputBoxWidth + anchorWidth ) {
553 if ( blockWidth >= inputBoxWidth ) {
554 inputBoxWidth = self._viewWidth - blockWidth;
556 inputBoxWidth = self._viewWidth;
559 if ( blockWidth >= inputBoxWidth ) {
560 inputBoxWidth = self._viewWidth - blockWidth;
562 inputBoxWidth -= blockWidth;
567 inputBoxWidth -= margin;
568 if ( inputBoxWidth < anchorWidth * 2 ) {
569 inputBoxWidth = self._viewWidth - margin;
571 $( inputBox ).width( inputBoxWidth - anchorWidth - inputBoxMargin );
574 _stringFormat : function ( expression ) {
576 message = expression,
578 for ( i = 1; i < arguments.length; i += 1 ) {
579 pattern = "{" + ( i - 1 ) + "}";
580 message = message.replace( pattern, arguments[i] );
585 _resizeBlocks : function () {
587 $view = self.element,
588 blocks = $view.find( "div" ),
591 for ( index = 0 ; index < blocks.length ; index += 1 ) {
592 $( blocks[index] ).css( "width", "auto" );
593 blocks[index] = self._ellipsisTextBlock( blocks[index] );
597 //---------------------------------------------------- //
599 //----------------------------------------------------//
603 focusIn : function () {
604 if ( this._focusStatus === "focusIn" ) {
608 var $view = this.element;
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();
617 // change focus state.
618 this._modifyInputBoxWidth();
619 this._focusStatus = "focusIn";
620 $view.removeClass( "ui-tokentextarea-focusout" ).addClass( "ui-tokentextarea-focusin" );
623 focusOut : function () {
624 if ( this._focusStatus === "focusOut" ) {
629 $view = self.element,
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 ),
640 $view.find( ".ui-tokentextarea-input" ).removeClass( "ui-tokentextarea-input-visible" ).addClass( "ui-tokentextarea-input-invisible" );
641 $view.find( "a" ).hide();
644 currentWidth = currentWidth - self._reservedWidth;
646 for ( index = 0; index < blocks.length; index++ ) {
647 blockWidth = $( blocks[index] ).outerWidth( true );
648 if ( currentWidth - blockWidth <= 0 ) {
649 lastIndex = index - 1;
653 $( blocks[index] ).show();
654 currentWidth -= blockWidth;
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 );
665 // update focus state
666 this._focusStatus = "focusOut";
667 $view.removeClass( "ui-tokentextarea-focusin" ).addClass( "ui-tokentextarea-focusout" );
670 inputText : function ( message ) {
671 var $view = this.element;
673 if ( arguments.length === 0 ) {
674 return $view.find( ".ui-tokentextarea-input" ).val();
676 $view.find( ".ui-tokentextarea-input" ).val( message );
680 select : function ( index ) {
681 var $view = this.element,
685 if ( this._focusStatus === "focusOut" ) {
689 if ( arguments.length === 0 ) {
690 // return a selected block.
691 lockBlock = $view.find( "div.ui-tokentextarea-sblock" );
693 return lockBlock.text();
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" );
708 add : function ( message, position ) {
709 if ( this._focusStatus === "focusOut" ) {
713 this._addTextBlock( message, position );
716 remove : function ( position ) {
718 $view = this.element,
719 blocks = $view.find( "div" ),
721 if ( this._focusStatus === "focusOut" ) {
725 if ( arguments.length === 0 ) {
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" );
734 self._modifyInputBoxWidth();
737 length : function () {
738 return this.element.find( "div" ).length;
741 refresh : function () {
743 $view = this.element;
745 self._viewWidth = $view.innerWidth();
746 self._resizeBlocks();
747 self._modifyInputBoxWidth();
750 destroy : function () {
751 var $view = this.element;
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();
758 this._trigger( "destroy" );
762 $( document ).bind( "pagecreate create", function () {
763 $( ":jqmData(role='tokentextarea')" ).tokentextarea();
766 $( window ).bind( "resize", function () {
767 $( ":jqmData(role='tokentextarea')" ).tokentextarea( "refresh" );
769 } ( jQuery, window, document ) );