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-listUrl : Represents the page id.
36 * The page contains data for the user, for example, an address book.(Default : null)
37 * data-label: Provide a label for a user-guide. (Default : 'To : ')
38 * data-descMessage : This attribute is managing message format.
39 * This message is displayed when widget status was changed to 'focusout'. (Default : '{0} & {1} more')
43 * inputtext ( [string] )
44 * : If argument is not exist, will get a string from inputbox.
45 * If argument is exist, will set a string to inputbox.
47 * : If no argument exists, gets a string of the selected block.
48 * If any button isn't selected on a multibuttonentry widget, this method returns "null" value.
49 * When a user call this method with an argument which is a number type,
50 * this method selects the button which is matched with the argument.
51 * add ( text, [number] )
52 * : If second argument does not exist, will insert to a new button at last position.
53 * Insert a new button at indexed position. The position is decided by the second argument.
54 * "index of position" means that the position of inserting a new button is decided by the second argument on "add" method.
55 * For example, if a user call the method like this "add("Tizen", 2)",
56 * new button labed "Tizen" will be inserted on the third position.
58 * : If no argument exists, all buttons are removed.
59 * Remove a button at indexed position.
60 * The position is decided by the second argument. (index: index of button)
62 * : Get a number of buttons.
64 * : This method change a status to 'focusin'.
65 * This status is able to manage a widget.
67 * : Changes the focus status to 'focus out'.
68 * The status is not able to manage a widget.
69 * All buttons that contained in the widget are removed and
70 * summarized message is displayed.
75 * create : Occur when create MultiButtonEntry widget.
76 * select : Occur when a button is selected.
77 * add : Occur when new button is inserted.
78 * remove : Occur when a button is removed.
82 * <div data-role="multibuttonentry" data-label="To : " data-listUrl:"#addressbook" data-descMessage="{0} & {1} more...">
87 ( function ( $, window, document, undefined ) {
88 $.widget( "tizen.multibuttonentry", $.mobile.widget, {
100 listUrl : "#addressbook",
101 descMessage : "{0} & {1} more..."
103 _create : function () {
105 $view = this.element,
106 role = $view.jqmData( "role" ),
107 option = this.options,
108 inputbox = $( document.createElement( "input" ) ),
109 labeltag = $( document.createElement( "label" ) ),
110 moreBlock = $( document.createElement( "a" ) );
112 $view.hide().empty().addClass( "ui-" + role );
114 // create a label tag.
115 $( labeltag ).text( this.options.label ).addClass( "ui-multibuttonentry-label" );
116 $view.append( labeltag );
118 // create a input tag
119 $( inputbox ).text( option.label ).addClass( "ui-multibuttonentry-input" );
120 $view.append( inputbox );
122 // create a anchor tag.
123 $( moreBlock ).text( "+" ).attr( "href", option.listUrl ).addClass( "ui-multibuttonentry-link" );
125 // append default htmlelements to main widget.
126 $view.append( moreBlock );
130 self._focusStatus = "init";
133 $view.attr( "tabindex", -1 ).focusin( function ( e ) {
137 // assign global variables
138 self._viewWidth = $view.innerWidth();
139 self._reservedWidth += self._calcBlockWidth( moreBlock );
140 self._reservedWidth += self._calcBlockWidth( labeltag );
141 self._fontSize = parseInt( $( moreBlock ).css( "font-size" ), 10 );
142 self._currentWidth = self._reservedWidth;
145 _bindEvents : function () {
147 $view = self.element,
148 option = self.options,
149 inputbox = $view.find( ".ui-multibuttonentry-input" ),
150 moreBlock = $view.find( ".ui-multibuttonentry-link" );
152 inputbox.bind( "keydown", function ( event ) {
155 var keyValue = event.keyCode,
156 valueString = $( inputbox ).val();
158 if ( keyValue == 8 ) {
159 if ( valueString.length === 0 ) {
160 self._validateTargetBlock();
162 } else if ( keyValue == 13 ) {
163 if ( valueString.length !== 0 ) {
164 self._addTextBlock( valueString );
168 self._unlockTextBlock();
172 moreBlock.click( function () {
175 $.mobile.changePage( option.listUrl, {
182 $( document ).bind( "pagechange.mbe", function ( event ) {
183 if ( $view.innerWidth() === 0 ) {
186 var inputBox = $view.find( ".ui-multibuttonentry-input" );
187 if ( self._labelWidth === 0 ) {
188 self._labelWidth = $view.find( ".ui-multibuttonentry-label" ).outerWidth( true );
189 self._anchorWidth = $view.find( ".ui-multibuttonentry-link" ).outerWidth( true );
190 self._marginWidth = parseInt( ( $( inputBox ).css( "margin-left" ) ), 10 );
191 self._marginWidth += parseInt( ( $( inputBox ).css( "margin-right" ) ), 10 );
192 self._viewWidth = $view.innerWidth();
194 self._modifyInputBoxWidth();
199 // create a textbutton and append this button to parent layer.
200 // @param arg1 : string
201 // @param arg2 : index
202 _addTextBlock : function ( messages, blcokIndex ) {
203 if ( arguments.length === 0 ) {
212 $view = self.element,
220 if ( self._viewWidth === 0 ) {
221 self._viewWidth = $view.innerWidth();
224 dataBlock = $( document.createElement( 'input' ) );
225 dataBlock.attr( "value", content ).addClass( "ui-multibuttonentry-data" ).hide();
227 // Create a new text HTMLDivElement.
228 textBlock = $( document.createElement( 'div' ) );
229 displayText = self._ellipsisTextBlock( content ) ;
230 textBlock.text( displayText ).addClass( "ui-multibuttonentry-block" );
231 textBlock.append( dataBlock );
232 // bind a event to HTMLDivElement.
233 textBlock.bind( "vclick", function ( event ) {
234 if ( self._focusStatus === "focusOut" ) {
239 if ( $( this ).hasClass( "ui-multibuttonentry-sblock" ) ) {
240 // If block is selected, it will be removed.
241 self._removeTextBlock();
244 var lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
245 if ( typeof lockBlock != "undefined" ) {
246 lockBlock.removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
248 $( this ).removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
249 self._trigger( "select" );
252 blocks = $view.find( "div" );
253 if ( index !== null && index <= blocks.length ) {
254 $( blocks[index] ).before( textBlock );
256 $view.find( ".ui-multibuttonentry-input" ).before( textBlock );
259 self._currentWidth += self._calcBlockWidth( textBlock );
260 self._modifyInputBoxWidth();
261 self._trigger( "add" );
264 _removeTextBlock : function () {
266 $view = this.element,
268 lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
270 if ( lockBlock !== null && lockBlock.length > 0 ) {
271 self._currentWidth -= self._calcBlockWidth( lockBlock );
273 self._modifyInputBoxWidth();
274 this._trigger( "remove" );
276 $view.find( "div:last" ).removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
280 _calcBlockWidth : function ( block ) {
282 blockWidth = $( block ).outerWidth( true );
285 _unlockTextBlock : function () {
286 var $view = this.element,
287 lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
288 if ( lockBlock !== null ) {
289 lockBlock.removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
293 // call when remove text block by backspace key.
294 _validateTargetBlock : function () {
296 $view = self.element,
297 lastBlock = $view.find( "div:last" ),
300 if ( lastBlock.hasClass( "ui-multibuttonentry-sblock" ) ) {
301 self._removeTextBlock();
303 tmpBlock = $view.find( "div.ui-multibuttonentry-sblock" );
304 tmpBlock.removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
305 lastBlock.removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
309 _ellipsisTextBlock : function ( text ) {
313 maxWidth = self._viewWidth,
314 maxCharCnt = parseInt( ( self._viewWidth / self._fontSize ), 10 ) - 5,
317 length = str.length ;
318 if ( length > maxCharCnt ) {
319 ellipsisStr = str.substring( 0, maxCharCnt );
320 ellipsisStr += "...";
327 _modifyInputBoxWidth : function () {
329 $view = self.element,
330 labelWidth = self._labelWidth,
331 anchorWidth = self._anchorWidth,
332 inputBoxWidth = self._viewWidth - labelWidth - anchorWidth,
333 blocks = $view.find( "div" ),
336 margin = self._marginWidth,
337 inputBox = $view.find( ".ui-multibuttonentry-input" );
339 if ( $view.width() === 0 ) {
343 for ( index = 0; index < blocks.length; index += 1 ) {
344 blockWidth = self._calcBlockWidth( blocks[index] );
345 inputBoxWidth = inputBoxWidth - blockWidth;
346 if ( inputBoxWidth <= 0 ) {
347 if ( inputBoxWidth + anchorWidth >= 0 ) {
348 inputBoxWidth = self._viewWidth - anchorWidth;
350 inputBoxWidth = self._viewWidth - blockWidth - anchorWidth;
354 $( inputBox ).width( inputBoxWidth - margin - 1 );
356 _stringFormat : function ( expression ) {
358 message = expression,
360 for ( i = 1; i < arguments.length; i += 1 ) {
361 pattern = "{" + ( i - 1 ) + "}";
362 message = message.replace( pattern, arguments[i] );
366 _resizeBlock : function () {
368 $view = self.element,
369 dataBlocks = $( ".ui-multibuttonentry-data" ),
370 blocks = $view.find( "div" ),
375 for ( index = 0 ; index < dataBlocks.length ; index += 1 ) {
376 srcTexts[index] = $( dataBlocks[index] ).val();
377 self._addTextBlock( srcTexts[index] );
383 //----------------------------------------------------//
385 //----------------------------------------------------//
389 focusIn : function () {
390 if ( this._focusStatus === "focusIn" ) {
394 var $view = this.element;
396 $view.find( "label" ).show();
397 $view.find( ".ui-multibuttonentry-desclabel" ).remove();
398 $view.find( "div.ui-multibuttonentry-sblock" ).removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
399 $view.find( "div" ).show();
400 $view.find( ".ui-multibuttonentry-input" ).show();
401 $view.find( "a" ).show();
403 // change focus state.
404 this._modifyInputBoxWidth();
405 this._focusStatus = "focusIn";
407 focusOut : function () {
408 if ( this._focusStatus === "focusOut" ) {
413 $view = self.element,
418 label = $view.find( "label" ),
419 more = $view.find( "span" ),
420 blocks = $view.find( "div" ),
421 currentWidth = $view.outerWidth( true ) - more.outerWidth( true ) - label.outerWidth( true ),
422 textWidth = currentWidth;
424 $view.find( ".ui-multibuttonentry-input" ).hide();
425 $view.find( "a" ).hide();
429 currentWidth = currentWidth - self._reservedWidth;
430 for ( index = 0; index < blocks.length; index += 1 ) {
431 currentWidth = currentWidth - $( blocks[index] ).outerWidth( true );
432 statement += ", " + $( blocks[index] ).text();
433 if ( currentWidth <= 0 ) {
434 statement = "," + $( blocks[0] ).text();
435 statement = self._stringFormat( self.options.descMessage, statement, blocks.length - 1 );
438 lastIndex = statement.length;
440 tempBlock = $( document.createElement( 'input' ) );
441 tempBlock.val( statement.substr( 1, statement.length ) );
442 tempBlock.addClass( "ui-multibuttonentry-desclabel" ).addClass( "ui-multibuttonentry-desclabel" );
443 tempBlock.width( textWidth - ( self._reservedWidth ) );
444 tempBlock.attr( "disabled", true );
445 $view.find( "label" ).after( tempBlock );
446 // update foucs state
447 this._focusStatus = "focusOut";
449 inputText : function ( message ) {
450 var $view = this.element;
452 if ( arguments.length === 0 ) {
453 return $view.find( ".ui-multibuttonentry-input" ).val();
455 $view.find( ".ui-multibuttonentry-input" ).val( message );
458 select : function ( index ) {
459 var $view = this.element,
463 if ( this._focusStatus === "focusOut" ) {
467 if ( arguments.length === 0 ) {
468 // return a selected block.
469 lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
471 return lockBlock.text();
475 // 1. unlock all blocks.
476 this._unlockTextBlock();
477 // 2. select pointed block.
478 blocks = $view.find( "div" );
479 if ( blocks.length > index ) {
480 $( blocks[index] ).removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
481 this._trigger( "select" );
485 add : function ( message, position ) {
486 if ( this._focusStatus === "focusOut" ) {
490 this._addTextBlock( message, position );
492 remove : function ( position ) {
494 $view = this.element,
495 blocks = $view.find( "div" ),
497 if ( this._focusStatus === "focusOut" ) {
501 if ( arguments.length === 0 ) {
503 this._trigger( "clear" );
504 } else if ( typeof position == "number" ) {
505 // remove selected button
506 index = ( ( position < blocks.length ) ? position : ( blocks.length - 1 ) );
507 $( blocks[index] ).remove();
508 this._trigger( "remove" );
510 self._modifyInputBoxWidth();
512 length : function () {
513 return this.element.find( "div" ).length;
515 refresh : function () {
520 destory : function () {
521 var $view = this.element;
523 $view.find( "label" ).remove();
524 $view.find( "div" ).unbind( "vclick" ).remove();
525 $view.find( "a" ).remove();
526 $view.find( ".ui-multibuttonentry-input" ).unbind( "keydown" ).remove();
528 this._trigger( "destory" );
532 $( document ).bind( "pagecreate create", function () {
533 $( ":jqmData(role='multibuttonentry')" ).multibuttonentry();
536 $( window ).bind( "resize", function () {
537 $( ":jqmData(role='multibuttonentry')" ).multibuttonentry( "refresh" );
539 } ( jQuery, window, document ) );