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}">
90 ( function ( $, window, document, undefined ) {
91 $.widget( "tizen.multibuttonentry", $.mobile.widget, {
104 description : "+ {0}"
107 _create : function () {
109 $view = this.element,
110 role = $view.jqmData( "role" ),
111 option = this.options,
112 className = "ui-multibuttonentry-link",
113 inputbox = $( document.createElement( "input" ) ),
114 labeltag = $( document.createElement( "label" ) ),
115 moreBlock = $( document.createElement( "a" ) );
117 $view.hide().empty().addClass( "ui-" + role );
119 // create a label tag.
120 $( labeltag ).text( option.label ).addClass( "ui-multibuttonentry-label" );
121 $view.append( labeltag );
123 // create a input tag
124 $( inputbox ).text( option.label ).addClass( "ui-multibuttonentry-input" );
125 $view.append( inputbox );
127 // create a anchor tag.
128 if ( option.listId === null || $.trim( option.listId ).length < 1 || $( option.listId ).length === 0 ) {
131 $( moreBlock ).text( "+" ).attr( "href", $.trim( option.listId ) ).addClass( "ui-multibuttonentry-link-base" ).addClass( className );
133 // append default htmlelements to main widget.
134 $view.append( moreBlock );
138 self._focusStatus = "init";
141 $view.attr( "tabindex", -1 ).focusin( function ( e ) {
145 // assign global variables
146 self._viewWidth = $view.innerWidth();
147 self._reservedWidth += self._calcBlockWidth( moreBlock );
148 self._reservedWidth += self._calcBlockWidth( labeltag );
149 self._fontSize = parseInt( $( moreBlock ).css( "font-size" ), 10 );
150 self._currentWidth = self._reservedWidth;
154 _bindEvents : function () {
156 $view = self.element,
157 option = self.options,
158 inputbox = $view.find( ".ui-multibuttonentry-input" ),
159 moreBlock = $view.find( ".ui-multibuttonentry-link-base" ),
162 // delegate a event to HTMLDivElement(each block).
163 $view.delegate( "div", "vclick", function ( event ) {
164 if ( $( this ).hasClass( "ui-multibuttonentry-sblock" ) ) {
165 // If block is selected, it will be removed.
166 self._removeTextBlock();
169 var lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
170 if ( typeof lockBlock !== "undefined" ) {
171 lockBlock.removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
173 $( this ).removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
174 self._trigger( "select" );
177 inputbox.bind( "keyup", function ( event ) {
182 var keyValue = event.keyCode,
183 valueString = $( inputbox ).val(),
187 if ( keyValue === 8 ) {
188 if ( valueString.length === 0 ) {
189 self._validateTargetBlock();
191 } else if ( keyValue === 13 || keyValue === 186 || keyValue === 188 ) {
192 if ( valueString.length !== 0 ) {
193 // split content by separators(',', ';')
194 valueStrings = valueString.split ( /[,;]/ );
195 for ( index = 0; index < valueStrings.length; index++ ) {
196 if ( valueStrings[index].length !== 0 && valueStrings[index].replace( /\s/g, "" ).length !== 0 ) {
197 self._addTextBlock( valueStrings[index] );
204 self._unlockTextBlock();
210 moreBlock.click( function () {
211 if ( $( moreBlock ).hasClass( "ui-multibuttonentry-link-dim" ) ) {
215 $( inputbox ).hide();
217 $.mobile.changePage( option.listId, {
224 $( document ).bind( "pagechange.mbe", function ( event ) {
225 if ( $view.innerWidth() === 0 ) {
228 var inputBox = $view.find( ".ui-multibuttonentry-input" );
229 if ( self._labelWidth === 0 ) {
230 self._labelWidth = $view.find( ".ui-multibuttonentry-label" ).outerWidth( true );
231 self._anchorWidth = $view.find( ".ui-multibuttonentry-link-base" ).outerWidth( true );
232 self._marginWidth = parseInt( ( $( inputBox ).css( "margin-left" ) ), 10 );
233 self._marginWidth += parseInt( ( $( inputBox ).css( "margin-right" ) ), 10 );
234 self._viewWidth = $view.innerWidth();
236 self._modifyInputBoxWidth();
237 $( inputbox ).show();
240 $view.bind( "click", function ( event ) {
241 if ( self._focusStatus === "focusOut" ) {
247 // create a textbutton and append this button to parent layer.
248 // @param arg1 : string
249 // @param arg2 : index
250 _addTextBlock : function ( messages, blockIndex ) {
251 if ( arguments.length === 0 ) {
260 $view = self.element,
266 if ( self._viewWidth === 0 ) {
267 self._viewWidth = $view.innerWidth();
270 // Create a new text HTMLDivElement.
271 textBlock = $( document.createElement( 'div' ) );
273 textBlock.text( content ).addClass( "ui-multibuttonentry-block" );
274 textBlock.css( {'visibility': 'hidden'} );
276 blocks = $view.find( "div" );
277 if ( index !== null && index <= blocks.length ) {
278 $( blocks[index] ).before( textBlock );
280 $view.find( ".ui-multibuttonentry-input" ).before( textBlock );
283 textBlock = self._ellipsisTextBlock( textBlock );
284 textBlock.css( {'visibility': 'visible'} );
286 self._currentWidth += self._calcBlockWidth( textBlock );
287 self._modifyInputBoxWidth();
288 self._trigger( "add" );
291 _removeTextBlock : function () {
293 $view = this.element,
294 lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
296 if ( lockBlock !== null && lockBlock.length > 0 ) {
297 self._currentWidth -= self._calcBlockWidth( lockBlock );
299 self._modifyInputBoxWidth();
300 this._trigger( "remove" );
302 $view.find( "div:last" ).removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
306 _calcBlockWidth : function ( block ) {
307 return $( block ).outerWidth( true );
310 _unlockTextBlock : function () {
311 var $view = this.element,
312 lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
314 lockBlock.removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
318 // call when remove text block by backspace key.
319 _validateTargetBlock : function () {
321 $view = self.element,
322 lastBlock = $view.find( "div:last" ),
325 if ( lastBlock.hasClass( "ui-multibuttonentry-sblock" ) ) {
326 self._removeTextBlock();
328 tmpBlock = $view.find( "div.ui-multibuttonentry-sblock" );
329 tmpBlock.removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
330 lastBlock.removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
334 _ellipsisTextBlock : function ( textBlock ) {
336 $view = self.element,
337 maxWidth = $view.innerWidth() - ( self._labelWidth + self._anchorWidth ) * 2;
339 if ( self._calcBlockWidth( textBlock ) > maxWidth ) {
340 $( textBlock ).width( maxWidth - self._marginWidth );
346 _modifyInputBoxWidth : function () {
348 $view = self.element,
349 margin = self._marginWidth,
350 labelWidth = self._labelWidth,
351 anchorWidth = self._anchorWidth,
352 inputBoxWidth = self._viewWidth - labelWidth,
353 blocks = $view.find( "div" ),
356 inputBox = $view.find( ".ui-multibuttonentry-input" );
358 if ( $view.width() === 0 ) {
362 for ( index = 0; index < blocks.length; index += 1 ) {
363 blockWidth = self._calcBlockWidth( blocks[index] );
365 if ( blockWidth >= inputBoxWidth + anchorWidth ) {
366 if ( blockWidth >= inputBoxWidth ) {
367 inputBoxWidth = self._viewWidth - blockWidth;
369 inputBoxWidth = self._viewWidth;
372 if ( blockWidth >= inputBoxWidth ) {
373 inputBoxWidth = self._viewWidth - blockWidth;
375 inputBoxWidth -= blockWidth;
380 inputBoxWidth -= margin;
381 if ( inputBoxWidth < anchorWidth * 2 ) {
382 inputBoxWidth = self._viewWidth - margin;
384 $( inputBox ).width( inputBoxWidth - anchorWidth );
387 _stringFormat : function ( expression ) {
389 message = expression,
391 for ( i = 1; i < arguments.length; i += 1 ) {
392 pattern = "{" + ( i - 1 ) + "}";
393 message = message.replace( pattern, arguments[i] );
398 _resizeBlocks : function () {
400 $view = self.element,
401 blocks = $view.find( "div" ),
404 for ( index = 0 ; index < blocks.length ; index += 1 ) {
405 $( blocks[index] ).css( "width", "auto" );
406 blocks[index] = self._ellipsisTextBlock( blocks[index] );
410 //---------------------------------------------------- //
412 //----------------------------------------------------//
416 focusIn : function () {
417 if ( this._focusStatus === "focusIn" ) {
421 var $view = this.element;
423 $view.find( "label" ).show();
424 $view.find( ".ui-multibuttonentry-desclabel" ).remove();
425 $view.find( "div.ui-multibuttonentry-sblock" ).removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
426 $view.find( "div" ).show();
427 $view.find( ".ui-multibuttonentry-input" ).show();
428 $view.find( "a" ).show();
430 // change focus state.
431 this._modifyInputBoxWidth();
432 this._focusStatus = "focusIn";
433 $view.removeClass( "ui-multibuttonentry-focusout" ).addClass( "ui-multibuttonentry-focusin" );
436 focusOut : function () {
437 if ( this._focusStatus === "focusOut" ) {
442 $view = self.element,
447 label = $view.find( "label" ),
448 more = $view.find( "span" ),
449 blocks = $view.find( "div" ),
450 currentWidth = $view.outerWidth( true ) - more.outerWidth( true ) - label.outerWidth( true ),
453 $view.find( ".ui-multibuttonentry-input" ).hide();
454 $view.find( "a" ).hide();
457 currentWidth = currentWidth - self._reservedWidth;
459 for ( index = 0; index < blocks.length; index++ ) {
460 blockWidth = $( blocks[index] ).outerWidth( true );
461 if ( currentWidth - blockWidth <= 0 ) {
462 lastIndex = index - 1;
466 $( blocks[index] ).show();
467 currentWidth -= blockWidth;
470 if ( lastIndex !== blocks.length ) {
471 statement = self._stringFormat( self.options.description, blocks.length - lastIndex - 1 );
472 tempBlock = $( document.createElement( 'label' ) );
473 tempBlock.text( statement );
474 tempBlock.addClass( "ui-multibuttonentry-desclabel" ).addClass( "ui-multibuttonentry-desclabel" );
475 $( blocks[lastIndex] ).after( tempBlock );
478 // update focus state
479 this._focusStatus = "focusOut";
480 $view.removeClass( "ui-multibuttonentry-focusin" ).addClass( "ui-multibuttonentry-focusout" );
483 inputText : function ( message ) {
484 var $view = this.element;
486 if ( arguments.length === 0 ) {
487 return $view.find( ".ui-multibuttonentry-input" ).val();
489 $view.find( ".ui-multibuttonentry-input" ).val( message );
493 select : function ( index ) {
494 var $view = this.element,
498 if ( this._focusStatus === "focusOut" ) {
502 if ( arguments.length === 0 ) {
503 // return a selected block.
504 lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
506 return lockBlock.text();
510 // 1. unlock all blocks.
511 this._unlockTextBlock();
512 // 2. select pointed block.
513 blocks = $view.find( "div" );
514 if ( blocks.length > index ) {
515 $( blocks[index] ).removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
516 this._trigger( "select" );
521 add : function ( message, position ) {
522 if ( this._focusStatus === "focusOut" ) {
526 this._addTextBlock( message, position );
529 remove : function ( position ) {
531 $view = this.element,
532 blocks = $view.find( "div" ),
534 if ( this._focusStatus === "focusOut" ) {
538 if ( arguments.length === 0 ) {
540 this._trigger( "clear" );
541 } else if ( typeof position == "number" ) {
542 // remove selected button
543 index = ( ( position < blocks.length ) ? position : ( blocks.length - 1 ) );
544 $( blocks[index] ).remove();
545 this._trigger( "remove" );
547 self._modifyInputBoxWidth();
550 length : function () {
551 return this.element.find( "div" ).length;
554 refresh : function () {
556 $view = this.element;
558 self._viewWidth = $view.innerWidth();
559 self._resizeBlocks();
560 self._modifyInputBoxWidth();
563 destroy : function () {
564 var $view = this.element;
566 $view.find( "label" ).remove();
567 $view.find( "div" ).undelegate( "vclick" ).remove();
568 $view.find( "a" ).remove();
569 $view.find( ".ui-multibuttonentry-input" ).unbind( "keyup" ).remove();
571 this._trigger( "destroy" );
575 $( document ).bind( "pagecreate create", function () {
576 $( ":jqmData(role='multibuttonentry')" ).multibuttonentry();
579 $( window ).bind( "resize", function () {
580 $( ":jqmData(role='multibuttonentry')" ).multibuttonentry( "refresh" );
582 } ( jQuery, window, document ) );