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>
27 * The MultiButtonEntry widget changes a text item to a button. It can be comprised of a number of button widgets.
28 * When a user types text and the text gets a specific event to change from a text to a button,
29 * the input text is changed to a MultiButtonEntry widget.
30 * A user can add the MultiButtonEntry widget to a contact list, email list, or another list.
31 * The typical use of this widget is composing a number of contacts or phone numbers in a specific area of the screen.
35 * data-list-id : Represents the page id.
36 * The page contains data for the user, for example, an address book.
37 * If the value is null, anchor button doesn't work. (Default : null)
38 * data-label: Provide a label for a user-guide. (Default : 'To : ')
39 * data-description : This attribute is managing message format.
40 * This message is displayed when widget status was changed to 'focusout'. (Default : '+ {0}')
44 * inputtext ( [string] )
45 * : If argument is not exist, will get a string from inputbox.
46 * If argument is exist, will set a string to inputbox.
48 * : If no argument exists, gets a string of the selected block.
49 * If any button isn't selected on a multibuttonentry widget, this method returns "null" value.
50 * When a user call this method with an argument which is a number type,
51 * this method selects the button which is matched with the argument.
52 * add ( text, [number] )
53 * : If second argument does not exist, will insert to a new button at last position.
54 * Insert a new button at indexed position. The position is decided by the second argument.
55 * "index of position" means that the position of inserting a new button is decided by the second argument on "add" method.
56 * For example, if a user call the method like this "add("Tizen", 2)",
57 * new button labed "Tizen" will be inserted on the third position.
59 * : If no argument exists, all buttons are removed.
60 * Remove a button at indexed position.
61 * The position is decided by the second argument. (index: index of button)
63 * : Get a number of buttons.
65 * : This method change a status to 'focusin'.
66 * This status is able to manage a widget.
68 * : Changes the focus status to 'focus out'.
69 * The status is not able to manage a widget.
70 * All buttons that contained in the widget are removed and
71 * summarized message is displayed.
73 * : Remove all of the new DOM elements for the current widget that you created.
77 * create : Occur when create MultiButtonEntry widget.
78 * select : Occur when a button is selected.
79 * add : Occur when new button is inserted.
80 * remove : Occur when a button is removed.
84 * <div data-role="multibuttonentry" data-label="To : " data-list-id:"#addressbook" data-description="+ {0}">
89 ( function ( $, window, document, undefined ) {
90 $.widget( "tizen.multibuttonentry", $.mobile.widget, {
103 description : "+ {0}"
106 _create : function () {
108 $view = this.element,
109 role = $view.jqmData( "role" ),
110 option = this.options,
111 className = "ui-multibuttonentry-link",
112 inputbox = $( document.createElement( "input" ) ),
113 labeltag = $( document.createElement( "label" ) ),
114 moreBlock = $( document.createElement( "a" ) );
116 $view.hide().empty().addClass( "ui-" + role );
118 // create a label tag.
119 $( labeltag ).text( option.label ).addClass( "ui-multibuttonentry-label" );
120 $view.append( labeltag );
122 // create a input tag
123 $( inputbox ).text( option.label ).addClass( "ui-multibuttonentry-input" );
124 $view.append( inputbox );
126 // create a anchor tag.
127 if ( option.listId === null || $.trim(option.listId).length < 1 ) {
130 $( moreBlock ).text( "+" ).attr( "href", $.trim(option.listId)).addClass( "ui-multibuttonentry-link-base" ).addClass( className );
132 // append default htmlelements to main widget.
133 $view.append( moreBlock );
137 self._focusStatus = "init";
140 $view.attr( "tabindex", -1 ).focusin( function ( e ) {
144 // assign global variables
145 self._viewWidth = $view.innerWidth();
146 self._reservedWidth += self._calcBlockWidth( moreBlock );
147 self._reservedWidth += self._calcBlockWidth( labeltag );
148 self._fontSize = parseInt( $( moreBlock ).css( "font-size" ), 10 );
149 self._currentWidth = self._reservedWidth;
153 _bindEvents : function () {
155 $view = self.element,
156 option = self.options,
157 inputbox = $view.find( ".ui-multibuttonentry-input" ),
158 moreBlock = $view.find( ".ui-multibuttonentry-link-base" ),
161 inputbox.bind( "keyup", function ( event ) {
166 var keyValue = event.keyCode,
167 valueString = $( inputbox ).val(),
171 if ( keyValue === 8 ) {
172 if ( valueString.length === 0 ) {
173 self._validateTargetBlock();
175 } else if ( keyValue === 13 || keyValue === 186 || keyValue === 188 ) {
176 if ( valueString.length !== 0 ) {
177 // split content by separators(',', ';')
178 valueStrings = valueString.split ( /[,;]/ );
179 for ( index = 0; index < valueStrings.length; index++ ) {
180 if ( valueStrings[index].length !== 0 && valueStrings[index].replace( /\s/g, "" ).length !== 0 ) {
181 self._addTextBlock( valueStrings[index] );
188 self._unlockTextBlock();
194 moreBlock.click( function () {
195 if ( $( moreBlock ).hasClass( "ui-multibuttonentry-link-dim" ) ) {
201 $.mobile.changePage( option.listId, {
208 $( document ).bind( "pagechange.mbe", function ( event ) {
209 if ( $view.innerWidth() === 0 ) {
212 var inputBox = $view.find( ".ui-multibuttonentry-input" );
213 if ( self._labelWidth === 0 ) {
214 self._labelWidth = $view.find( ".ui-multibuttonentry-label" ).outerWidth( true );
215 self._anchorWidth = $view.find( ".ui-multibuttonentry-link-base" ).outerWidth( true );
216 self._marginWidth = parseInt( ( $( inputBox ).css( "margin-left" ) ), 10 );
217 self._marginWidth += parseInt( ( $( inputBox ).css( "margin-right" ) ), 10 );
218 self._viewWidth = $view.innerWidth();
220 self._modifyInputBoxWidth();
224 $view.bind( "click", function ( event ) {
225 if ( self._focusStatus === "focusOut" ) {
231 // create a textbutton and append this button to parent layer.
232 // @param arg1 : string
233 // @param arg2 : index
234 _addTextBlock : function ( messages, blockIndex ) {
235 if ( arguments.length === 0 ) {
244 $view = self.element,
252 if ( self._viewWidth === 0 ) {
253 self._viewWidth = $view.innerWidth();
257 dataBlock = $( document.createElement( 'input' ) );
258 dataBlock.attr( "value", content ).addClass( "ui-multibuttonentry-data" ).hide();
260 // Create a new text HTMLDivElement.
261 textBlock = $( document.createElement( 'div' ) );
262 displayText = self._ellipsisTextBlock( content ) ;
263 textBlock.text( displayText ).addClass( "ui-multibuttonentry-block" );
264 textBlock.append( dataBlock );
266 // bind a event to HTMLDivElement.
267 textBlock.bind( "vclick", function ( event ) {
268 if ( $( this ).hasClass( "ui-multibuttonentry-sblock" ) ) {
269 // If block is selected, it will be removed.
270 self._removeTextBlock();
273 var lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
274 if ( typeof lockBlock != "undefined" ) {
275 lockBlock.removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
277 $( this ).removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
278 self._trigger( "select" );
281 blocks = $view.find( "div" );
282 if ( index !== null && index <= blocks.length ) {
283 $( blocks[index] ).before( textBlock );
285 $view.find( ".ui-multibuttonentry-input" ).before( textBlock );
288 self._currentWidth += self._calcBlockWidth( textBlock );
289 self._modifyInputBoxWidth();
290 self._trigger( "add" );
293 _removeTextBlock : function () {
295 $view = this.element,
297 lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
299 if ( lockBlock !== null && lockBlock.length > 0 ) {
300 self._currentWidth -= self._calcBlockWidth( lockBlock );
302 self._modifyInputBoxWidth();
303 this._trigger( "remove" );
305 $view.find( "div:last" ).removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
309 _calcBlockWidth : function ( block ) {
311 blockWidth = $( block ).outerWidth( true );
315 _unlockTextBlock : function () {
316 var $view = this.element,
317 lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
318 if ( lockBlock !== null ) {
319 lockBlock.removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
323 // call when remove text block by backspace key.
324 _validateTargetBlock : function () {
326 $view = self.element,
327 lastBlock = $view.find( "div:last" ),
330 if ( lastBlock.hasClass( "ui-multibuttonentry-sblock" ) ) {
331 self._removeTextBlock();
333 tmpBlock = $view.find( "div.ui-multibuttonentry-sblock" );
334 tmpBlock.removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
335 lastBlock.removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
339 _ellipsisTextBlock : function ( text ) {
343 maxWidth = self._viewWidth,
344 maxCharCnt = parseInt( ( self._viewWidth / self._fontSize ), 10 ) - 5,
347 length = str.length ;
348 if ( length > maxCharCnt ) {
349 ellipsisStr = str.substring( 0, maxCharCnt );
350 ellipsisStr += "...";
358 _modifyInputBoxWidth : function () {
360 $view = self.element,
361 labelWidth = self._labelWidth,
362 anchorWidth = self._anchorWidth,
363 inputBoxWidth = self._viewWidth - labelWidth - anchorWidth,
364 blocks = $view.find( "div" ),
367 margin = self._marginWidth,
368 inputBox = $view.find( ".ui-multibuttonentry-input" );
370 if ( $view.width() === 0 ) {
374 for ( index = 0; index < blocks.length; index += 1 ) {
375 blockWidth = self._calcBlockWidth( blocks[index] );
376 inputBoxWidth = inputBoxWidth - blockWidth;
377 if ( inputBoxWidth <= 0 ) {
378 if ( inputBoxWidth + anchorWidth >= 0 ) {
379 inputBoxWidth = self._viewWidth - anchorWidth;
381 inputBoxWidth = self._viewWidth - blockWidth - anchorWidth;
385 $( inputBox ).width( inputBoxWidth - margin - 1 );
388 _stringFormat : function ( expression ) {
390 message = expression,
392 for ( i = 1; i < arguments.length; i += 1 ) {
393 pattern = "{" + ( i - 1 ) + "}";
394 message = message.replace( pattern, arguments[i] );
399 _resizeBlock : function () {
401 $view = self.element,
402 dataBlocks = $( ".ui-multibuttonentry-data" ),
403 blocks = $view.find( "div" ),
408 for ( index = 0 ; index < dataBlocks.length ; index += 1 ) {
409 srcTexts[index] = $( dataBlocks[index] ).val();
410 self._addTextBlock( srcTexts[index] );
416 //---------------------------------------------------- //
418 //----------------------------------------------------//
422 focusIn : function () {
423 if ( this._focusStatus === "focusIn" ) {
427 var $view = this.element;
429 $view.find( "label" ).show();
430 $view.find( ".ui-multibuttonentry-desclabel" ).remove();
431 $view.find( "div.ui-multibuttonentry-sblock" ).removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
432 $view.find( "div" ).show();
433 $view.find( ".ui-multibuttonentry-input" ).show();
434 $view.find( "a" ).show();
436 // change focus state.
437 this._modifyInputBoxWidth();
438 this._focusStatus = "focusIn";
439 $view.removeClass( "ui-multibuttonentry-focusout" ).addClass( "ui-multibuttonentry-focusin" );
442 focusOut : function () {
443 if ( this._focusStatus === "focusOut" ) {
448 $view = self.element,
453 label = $view.find( "label" ),
454 more = $view.find( "span" ),
455 blocks = $view.find( "div" ),
456 currentWidth = $view.outerWidth( true ) - more.outerWidth( true ) - label.outerWidth( true ),
459 $view.find( ".ui-multibuttonentry-input" ).hide();
460 $view.find( "a" ).hide();
463 currentWidth = currentWidth - self._reservedWidth;
465 for ( index = 0; index < blocks.length; index++ ) {
466 blockWidth = $( blocks[index] ).outerWidth( true );
467 if ( currentWidth - blockWidth <= 0 ) {
468 lastIndex = index - 1;
472 $( blocks[index] ).show();
473 currentWidth -= blockWidth;
476 if ( lastIndex !== blocks.length ) {
477 statement = self._stringFormat( self.options.description, blocks.length - lastIndex - 1 );
478 tempBlock = $( document.createElement( 'label' ));
479 tempBlock.text( statement );
480 tempBlock.addClass( "ui-multibuttonentry-desclabel" ).addClass( "ui-multibuttonentry-desclabel" );
481 $( blocks[lastIndex] ).after( tempBlock );
484 // update foucs state
485 this._focusStatus = "focusOut";
486 $view.removeClass( "ui-multibuttonentry-focusin" ).addClass( "ui-multibuttonentry-focusout" );
489 inputText : function ( message ) {
490 var $view = this.element;
492 if ( arguments.length === 0 ) {
493 return $view.find( ".ui-multibuttonentry-input" ).val();
495 $view.find( ".ui-multibuttonentry-input" ).val( message );
499 select : function ( index ) {
500 var $view = this.element,
504 if ( this._focusStatus === "focusOut" ) {
508 if ( arguments.length === 0 ) {
509 // return a selected block.
510 lockBlock = $view.find( "div.ui-multibuttonentry-sblock" ).children( ".ui-multibuttonentry-data" );
512 return lockBlock.attr( "value" );
516 // 1. unlock all blocks.
517 this._unlockTextBlock();
518 // 2. select pointed block.
519 blocks = $view.find( "div" );
520 if ( blocks.length > index ) {
521 $( blocks[index] ).removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
522 this._trigger( "select" );
527 add : function ( message, position ) {
528 if ( this._focusStatus === "focusOut" ) {
532 this._addTextBlock( message, position );
535 remove : function ( position ) {
537 $view = this.element,
538 blocks = $view.find( "div" ),
540 if ( this._focusStatus === "focusOut" ) {
544 if ( arguments.length === 0 ) {
546 this._trigger( "clear" );
547 } else if ( typeof position == "number" ) {
548 // remove selected button
549 index = ( ( position < blocks.length ) ? position : ( blocks.length - 1 ) );
550 $( blocks[index] ).remove();
551 this._trigger( "remove" );
553 self._modifyInputBoxWidth();
556 length : function () {
557 return this.element.find( "div" ).length;
560 refresh : function () {
566 destroy : function () {
567 var $view = this.element;
569 $view.find( "label" ).remove();
570 $view.find( "div" ).unbind( "vclick" ).remove();
571 $view.find( "a" ).remove();
572 $view.find( ".ui-multibuttonentry-input" ).unbind( "keyup" ).remove();
574 this._trigger( "destroy" );
578 $( document ).bind( "pagecreate create", function () {
579 $( ":jqmData(role='multibuttonentry')" ).multibuttonentry();
582 $( window ).bind( "resize", function () {
583 $( ":jqmData(role='multibuttonentry')" ).multibuttonentry( "refresh" );
585 } ( jQuery, window, document ) );