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 MultiButtonEntry 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 MultiButtonEntry widget.
31 * A user can add the MultiButtonEntry 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-list-id : 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 multibuttonentry 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 MultiButtonEntry 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="multibuttonentry" data-label="To : " data-list-id:"#addressbook" data-description="+ {0}">
92 @class MultiButtonEntry
93 The multi-button entry 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 multi-button entry 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 multi-button entry widget to the application, use the following code:
97 <div data-role="multibuttonentry" data-label="To:" data-theme="#addressbook" data-list-id="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-list-id
114 Sets the ID of the page to which the button links.
118 The create event is fired when the multi-button view widget is created:
120 <div data-role="multibuttonentry" data-label="To:" data-theme="#addressbook">
123 $(".selector").multibuttonentry
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 multi-button view widget button is selected:
140 <div data-role="multibuttonentry" data-label="To:" data-theme="#addressbook">
143 $(".selector").multibuttonentry
145 select: function(event, ui)
147 // Handle the select event
151 $(".selector").bind("multibuttonentryselect", function(event, ui)
153 // Handle the select event
158 The add event is fired when a multi-button view widget button is created:
160 <div data-role="multibuttonentry" data-label="To:" data-theme="#addressbook">
163 $(".selector").multibuttonentry
165 add: function(event, ui)
167 // Handle the add event
171 $(".selector").bind("multibuttonentryadd", function(event, ui)
173 // Handle the add event
178 The remove event is fired when a multi-button view widget button is removed:
180 <div data-role="multibuttonentry" data-label="To:" data-theme="#addressbook">
183 $(".selector").multibuttonentry
185 remove: function(event, ui)
187 // Handle the remove event
191 $(".selector").bind("multibuttonentryremove", 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="multibuttonentry" data-label="To:" data-theme="#addressbook">
202 $(".selector").multibuttonentry("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="multibuttonentry" data-label="To:" data-theme="#addressbook">
212 $(".selector").multibuttonentry("inputText", [text]);
216 The select method is used to select a multi-button entry 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="multibuttonentry" data-label="To:" data-theme="#addressbook">
220 $(".selector").multibuttonentry("select", [index]);
224 The add method is used to add a new multi-button entry 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").multibuttonentry("add", "Tizen", 2) method call creates a new widget button labeled 'Tizen' at the third position in the widget.
226 <div data-role="multibuttonentry" data-label="To:" data-theme="#addressbook">
228 $(".selector").multibuttonentry("add", [text], [index]);
232 The remove method is used to remove a multi-button entry widget button at the specified index position. If the parameter is not defined, all the widget buttons are removed.
234 <div data-role="multibuttonentry" data-label="To:" data-theme="#addressbook">
236 $(".selector").multibuttonentry("remove", [index]);
240 The length method is used to retrieve the number of buttons in the multi-button entry widget:
241 <div data-role="multibuttonentry" data-label="To:" data-theme="#addressbook">
243 $(".selector").multibuttonentry("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 multi-button entry widget.
249 <div data-role="multibuttonentry" data-label="To:" data-theme="#addressbook">
251 $(".selector").multibuttonentry("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="multibuttonentry" data-label="To:" data-theme="#addressbook">
259 $(".selector").multibuttonentry("focusOut");
261 ( function ( $, window, document, undefined ) {
262 $.widget( "tizen.multibuttonentry", $.mobile.widget, {
275 description : "+ {0}"
278 _create : function () {
280 $view = this.element,
281 role = $view.jqmData( "role" ),
282 option = this.options,
283 className = "ui-multibuttonentry-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-multibuttonentry-label" );
292 $view.append( labeltag );
294 // create a input tag
295 $( inputbox ).text( option.label ).addClass( "ui-multibuttonentry-input" );
296 $view.append( inputbox );
298 // create a anchor tag.
299 if ( option.listId === null || $.trim( option.listId ).length < 1 || $( option.listId ).length === 0 ) {
302 $( moreBlock ).attr( "href", $.trim( option.listId ) ).attr( "data-role", "button" ).attr( "data-inline", "true" ).attr( "data-icon", "plus" ).attr( "data-style", "circle" ).addClass( "ui-multibuttonentry-link-base" ).addClass( className );
304 // append default htmlelements to main widget.
305 $view.append( moreBlock );
309 self._focusStatus = "init";
312 $view.attr( "tabindex", -1 ).focusin( function ( e ) {
316 // assign global variables
317 self._viewWidth = $view.innerWidth();
318 self._reservedWidth += self._calcBlockWidth( moreBlock );
319 self._reservedWidth += self._calcBlockWidth( labeltag );
320 self._fontSize = parseInt( $( moreBlock ).css( "font-size" ), 10 );
321 self._currentWidth = self._reservedWidth;
325 _bindEvents : function () {
327 $view = self.element,
328 option = self.options,
329 inputbox = $view.find( ".ui-multibuttonentry-input" ),
330 moreBlock = $view.find( ".ui-multibuttonentry-link-base" ),
333 // delegate a event to HTMLDivElement(each block).
334 $view.delegate( "div", "vclick", function ( event ) {
335 if ( $( this ).hasClass( "ui-multibuttonentry-sblock" ) ) {
336 // If block is selected, it will be removed.
337 self._removeTextBlock();
340 var lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
341 if ( typeof lockBlock !== "undefined" ) {
342 lockBlock.removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
344 $( this ).removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
345 self._trigger( "select" );
348 inputbox.bind( "keyup", function ( event ) {
353 var keyValue = event.keyCode,
354 valueString = $( inputbox ).val(),
358 if ( keyValue === 8 ) {
359 if ( valueString.length === 0 ) {
360 self._validateTargetBlock();
362 } else if ( keyValue === 13 || keyValue === 186 || keyValue === 188 ) {
363 if ( valueString.length !== 0 ) {
364 // split content by separators(',', ';')
365 valueStrings = valueString.split ( /[,;]/ );
366 for ( index = 0; index < valueStrings.length; index++ ) {
367 if ( valueStrings[index].length !== 0 && valueStrings[index].replace( /\s/g, "" ).length !== 0 ) {
368 self._addTextBlock( valueStrings[index] );
375 self._unlockTextBlock();
381 moreBlock.click( function () {
382 if ( $( moreBlock ).hasClass( "ui-multibuttonentry-link-dim" ) ) {
386 $( inputbox ).hide();
388 $.mobile.changePage( option.listId, {
395 $( document ).bind( "pagechange.mbe", function ( event ) {
396 if ( $view.innerWidth() === 0 ) {
399 var inputBox = $view.find( ".ui-multibuttonentry-input" );
400 if ( self._labelWidth === 0 ) {
401 self._labelWidth = $view.find( ".ui-multibuttonentry-label" ).outerWidth( true );
402 self._anchorWidth = $view.find( ".ui-multibuttonentry-link-base" ).outerWidth( true );
403 self._marginWidth = parseInt( ( $( inputBox ).css( "margin-left" ) ), 10 );
404 self._marginWidth += parseInt( ( $( inputBox ).css( "margin-right" ) ), 10 );
405 self._viewWidth = $view.innerWidth();
407 self._modifyInputBoxWidth();
408 $( inputbox ).show();
411 $view.bind( "click", function ( event ) {
412 if ( self._focusStatus === "focusOut" ) {
418 // create a textbutton and append this button to parent layer.
419 // @param arg1 : string
420 // @param arg2 : index
421 _addTextBlock : function ( messages, blockIndex ) {
422 if ( arguments.length === 0 ) {
431 $view = self.element,
437 if ( self._viewWidth === 0 ) {
438 self._viewWidth = $view.innerWidth();
441 // Create a new text HTMLDivElement.
442 textBlock = $( document.createElement( 'div' ) );
444 textBlock.text( content ).addClass( "ui-multibuttonentry-block" );
445 textBlock.css( {'visibility': 'hidden'} );
447 blocks = $view.find( "div" );
448 if ( index !== null && index <= blocks.length ) {
449 $( blocks[index] ).before( textBlock );
451 $view.find( ".ui-multibuttonentry-input" ).before( textBlock );
454 textBlock = self._ellipsisTextBlock( textBlock );
455 textBlock.css( {'visibility': 'visible'} );
457 self._currentWidth += self._calcBlockWidth( textBlock );
458 self._modifyInputBoxWidth();
459 self._trigger( "add" );
462 _removeTextBlock : function () {
464 $view = this.element,
465 lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
467 if ( lockBlock !== null && lockBlock.length > 0 ) {
468 self._currentWidth -= self._calcBlockWidth( lockBlock );
470 self._modifyInputBoxWidth();
471 this._trigger( "remove" );
473 $view.find( "div:last" ).removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
477 _calcBlockWidth : function ( block ) {
478 return $( block ).outerWidth( true );
481 _unlockTextBlock : function () {
482 var $view = this.element,
483 lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
485 lockBlock.removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
489 // call when remove text block by backspace key.
490 _validateTargetBlock : function () {
492 $view = self.element,
493 lastBlock = $view.find( "div:last" ),
496 if ( lastBlock.hasClass( "ui-multibuttonentry-sblock" ) ) {
497 self._removeTextBlock();
499 tmpBlock = $view.find( "div.ui-multibuttonentry-sblock" );
500 tmpBlock.removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
501 lastBlock.removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
505 _ellipsisTextBlock : function ( textBlock ) {
507 $view = self.element,
508 maxWidth = $view.innerWidth() - ( self._labelWidth + self._anchorWidth ) * 2;
510 if ( self._calcBlockWidth( textBlock ) > maxWidth ) {
511 $( textBlock ).width( maxWidth - self._marginWidth );
517 _modifyInputBoxWidth : function () {
519 $view = self.element,
520 margin = self._marginWidth,
521 labelWidth = self._labelWidth,
522 anchorWidth = self._anchorWidth,
523 inputBoxWidth = self._viewWidth - labelWidth,
524 blocks = $view.find( "div" ),
528 inputBox = $view.find( ".ui-multibuttonentry-input" );
530 if ( $view.width() === 0 ) {
534 for ( index = 0; index < blocks.length; index += 1 ) {
535 blockWidth = self._calcBlockWidth( blocks[index] );
537 if ( blockWidth >= inputBoxWidth + anchorWidth ) {
538 if ( blockWidth >= inputBoxWidth ) {
539 inputBoxWidth = self._viewWidth - blockWidth;
541 inputBoxWidth = self._viewWidth;
544 if ( blockWidth >= inputBoxWidth ) {
545 inputBoxWidth = self._viewWidth - blockWidth;
547 inputBoxWidth -= blockWidth;
552 inputBoxWidth -= margin;
553 if ( inputBoxWidth < anchorWidth * 2 ) {
554 inputBoxWidth = self._viewWidth - margin;
556 $( inputBox ).width( inputBoxWidth - anchorWidth - inputBoxMargin );
559 _stringFormat : function ( expression ) {
561 message = expression,
563 for ( i = 1; i < arguments.length; i += 1 ) {
564 pattern = "{" + ( i - 1 ) + "}";
565 message = message.replace( pattern, arguments[i] );
570 _resizeBlocks : function () {
572 $view = self.element,
573 blocks = $view.find( "div" ),
576 for ( index = 0 ; index < blocks.length ; index += 1 ) {
577 $( blocks[index] ).css( "width", "auto" );
578 blocks[index] = self._ellipsisTextBlock( blocks[index] );
582 //---------------------------------------------------- //
584 //----------------------------------------------------//
588 focusIn : function () {
589 if ( this._focusStatus === "focusIn" ) {
593 var $view = this.element;
595 $view.find( "label" ).show();
596 $view.find( ".ui-multibuttonentry-desclabel" ).remove();
597 $view.find( "div.ui-multibuttonentry-sblock" ).removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
598 $view.find( "div" ).show();
599 $view.find( ".ui-multibuttonentry-input" ).show();
600 $view.find( "a" ).show();
602 // change focus state.
603 this._modifyInputBoxWidth();
604 this._focusStatus = "focusIn";
605 $view.removeClass( "ui-multibuttonentry-focusout" ).addClass( "ui-multibuttonentry-focusin" );
608 focusOut : function () {
609 if ( this._focusStatus === "focusOut" ) {
614 $view = self.element,
619 label = $view.find( "label" ),
620 more = $view.find( "span" ),
621 blocks = $view.find( "div" ),
622 currentWidth = $view.outerWidth( true ) - more.outerWidth( true ) - label.outerWidth( true ),
625 $view.find( ".ui-multibuttonentry-input" ).hide();
626 $view.find( "a" ).hide();
629 currentWidth = currentWidth - self._reservedWidth;
631 for ( index = 0; index < blocks.length; index++ ) {
632 blockWidth = $( blocks[index] ).outerWidth( true );
633 if ( currentWidth - blockWidth <= 0 ) {
634 lastIndex = index - 1;
638 $( blocks[index] ).show();
639 currentWidth -= blockWidth;
642 if ( lastIndex !== blocks.length ) {
643 statement = self._stringFormat( self.options.description, blocks.length - lastIndex - 1 );
644 tempBlock = $( document.createElement( 'label' ) );
645 tempBlock.text( statement );
646 tempBlock.addClass( "ui-multibuttonentry-desclabel" ).addClass( "ui-multibuttonentry-desclabel" );
647 $( blocks[lastIndex] ).after( tempBlock );
650 // update focus state
651 this._focusStatus = "focusOut";
652 $view.removeClass( "ui-multibuttonentry-focusin" ).addClass( "ui-multibuttonentry-focusout" );
655 inputText : function ( message ) {
656 var $view = this.element;
658 if ( arguments.length === 0 ) {
659 return $view.find( ".ui-multibuttonentry-input" ).val();
661 $view.find( ".ui-multibuttonentry-input" ).val( message );
665 select : function ( index ) {
666 var $view = this.element,
670 if ( this._focusStatus === "focusOut" ) {
674 if ( arguments.length === 0 ) {
675 // return a selected block.
676 lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
678 return lockBlock.text();
682 // 1. unlock all blocks.
683 this._unlockTextBlock();
684 // 2. select pointed block.
685 blocks = $view.find( "div" );
686 if ( blocks.length > index ) {
687 $( blocks[index] ).removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
688 this._trigger( "select" );
693 add : function ( message, position ) {
694 if ( this._focusStatus === "focusOut" ) {
698 this._addTextBlock( message, position );
701 remove : function ( position ) {
703 $view = this.element,
704 blocks = $view.find( "div" ),
706 if ( this._focusStatus === "focusOut" ) {
710 if ( arguments.length === 0 ) {
712 this._trigger( "clear" );
713 } else if ( typeof position == "number" ) {
714 // remove selected button
715 index = ( ( position < blocks.length ) ? position : ( blocks.length - 1 ) );
716 $( blocks[index] ).remove();
717 this._trigger( "remove" );
719 self._modifyInputBoxWidth();
722 length : function () {
723 return this.element.find( "div" ).length;
726 refresh : function () {
728 $view = this.element;
730 self._viewWidth = $view.innerWidth();
731 self._resizeBlocks();
732 self._modifyInputBoxWidth();
735 destroy : function () {
736 var $view = this.element;
738 $view.find( "label" ).remove();
739 $view.find( "div" ).undelegate( "vclick" ).remove();
740 $view.find( "a" ).remove();
741 $view.find( ".ui-multibuttonentry-input" ).unbind( "keyup" ).remove();
743 this._trigger( "destroy" );
747 $( document ).bind( "pagecreate create", function () {
748 $( ":jqmData(role='multibuttonentry')" ).multibuttonentry();
751 $( window ).bind( "resize", function () {
752 $( ":jqmData(role='multibuttonentry')" ).multibuttonentry( "refresh" );
754 } ( jQuery, window, document ) );