Export 0.1.41
[platform/framework/web/web-ui-fw.git] / src / widgets / multibuttonentry / js / jquery.mobile.tizen.multibuttonentry.js
1 /* ***************************************************************************
2  * Copyright (c) 2000 - 2011 Samsung Electronics Co., Ltd.
3  *
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:
10  *
11  * The above copyright notice and this permission notice shall be included in
12  * all copies or substantial portions of the Software.
13  *
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  * ***************************************************************************
22  *
23  *      Author: Kangsik Kim <kangsik81.kim@samsung.com>
24 */
25
26 /**
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.
32  *
33  *      HTML Attributes:
34  *
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}')
41  *
42  *      APIs:
43  *
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.
47  *              select (  [number]  )
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.
58  *              remove ( [number] )
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)
62  *              length ( void )
63  *                      : Get a number of buttons.
64  *              foucsIn ( void )
65  *                      : This method change a status to 'focusin'.
66  *                      This status is able to manage a widget.
67  *              focusOut ( void )
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.
72  *              destroy ( void )
73  *                      : Remove all of the new DOM elements for the current widget that you created.
74  *
75  *      Events:
76  *
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.
81  *
82  *      Examples:
83  *
84  *              <div data-role="multibuttonentry" data-label="To : " data-list-id:"#addressbook" data-description="+ {0}">
85  *              </div>
86  *
87  */
88
89 ( function ( $, window, document, undefined ) {
90         $.widget( "tizen.multibuttonentry", $.mobile.widget, {
91                 _focusStatus : null,
92                 _items : null,
93                 _viewWidth : 0,
94                 _reservedWidth : 0,
95                 _currentWidth : 0,
96                 _fontSize : 0,
97                 _anchorWidth : 0,
98                 _labelWidth : 0,
99                 _marginWidth : 0,
100                 options : {
101                         label : "To : ",
102                         listId : null,
103                         description : "+ {0}"
104                 },
105
106                 _create : function () {
107                         var self = this,
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" ) );
115
116                         $view.hide().empty().addClass( "ui-" + role );
117
118                         // create a label tag.
119                         $( labeltag ).text( option.label ).addClass( "ui-multibuttonentry-label" );
120                         $view.append( labeltag );
121
122                         // create a input tag
123                         $( inputbox ).text( option.label ).addClass( "ui-multibuttonentry-input" );
124                         $view.append( inputbox );
125
126                         // create a anchor tag.
127                         if ( option.listId === null ||  $.trim(option.listId).length < 1  ) {
128                                 className += "-dim";
129                         }
130                         $( moreBlock ).text( "+" ).attr( "href",  $.trim(option.listId)).addClass( "ui-multibuttonentry-link-base" ).addClass( className );
131
132                         // append default htmlelements to main widget.
133                         $view.append( moreBlock );
134
135                         // bind a event
136                         this._bindEvents();
137                         self._focusStatus = "init";
138                         // display widget
139                         $view.show();
140                         $view.attr( "tabindex", -1 ).focusin( function ( e ) {
141                                 self.focusIn();
142                         });
143
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;
150                 },
151
152                 // bind events
153                 _bindEvents : function () {
154                         var self = this,
155                                 $view = self.element,
156                                 option = self.options,
157                                 inputbox = $view.find( ".ui-multibuttonentry-input" ),
158                                 moreBlock = $view.find( ".ui-multibuttonentry-link-base" ),
159                                 isSeparator = false;
160
161                         inputbox.bind( "keyup", function ( event ) {
162                                 // 8  : backspace
163                                 // 13 : Enter
164                                 // 186 : semi-colon
165                                 // 188 : comma
166                                 var keyValue = event.keyCode,
167                                         valueString = $( inputbox ).val(),
168                                         valueStrings = [],
169                                         index;
170
171                                 if ( keyValue === 8 ) {
172                                         if ( valueString.length === 0 ) {
173                                                 self._validateTargetBlock();
174                                         }
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] );
182                                                         }
183                                                 }
184                                         }
185                                         inputbox.val( "" );
186                                         isSeparator = true;
187                                 } else {
188                                         self._unlockTextBlock();
189                                 }
190
191                                 return !isSeparator;
192                         });
193
194                         moreBlock.click( function () {
195                                 if ( $( moreBlock ).hasClass( "ui-multibuttonentry-link-dim" ) ) {
196                                         return ;
197                                 }
198
199                                 $(inputbox).hide();
200
201                                 $.mobile.changePage( option.listId, {
202                                         transition: "slide",
203                                         reverse: false,
204                                         changeHash: false
205                                 } );
206                         } );
207
208                         $( document ).bind( "pagechange.mbe", function ( event ) {
209                                 if ( $view.innerWidth() === 0 ) {
210                                         return ;
211                                 }
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();
219                                 }
220                                 self._modifyInputBoxWidth();
221                                 $(inputbox).show();
222                         });
223
224                         $view.bind( "click", function ( event ) {
225                                 if ( self._focusStatus === "focusOut" ) {
226                                         self.focusIn();
227                                 }
228                         });
229                 },
230
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 ) {
236                                 return;
237                         }
238
239                         if ( ! messages ) {
240                                 return ;
241                         }
242
243                         var self = this,
244                                 $view = self.element,
245                                 content = messages,
246                                 index = blockIndex,
247                                 blocks = null,
248                                 dataBlock = null,
249                                 displayText = null,
250                                 textBlock = null;
251
252                         if ( self._viewWidth === 0 ) {
253                                 self._viewWidth = $view.innerWidth();
254                         }
255
256                         // save src data
257                         dataBlock = $( document.createElement( 'input' ) );
258                         dataBlock.attr( "value", content ).addClass( "ui-multibuttonentry-data" ).hide();
259
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 );
265
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();
271                                 }
272
273                                 var lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
274                                 if ( typeof lockBlock != "undefined" ) {
275                                         lockBlock.removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
276                                 }
277                                 $( this ).removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
278                                 self._trigger( "select" );
279                         });
280
281                         blocks = $view.find( "div" );
282                         if ( index !== null && index <= blocks.length ) {
283                                 $( blocks[index] ).before( textBlock );
284                         } else {
285                                 $view.find( ".ui-multibuttonentry-input" ).before( textBlock );
286                         }
287
288                         self._currentWidth += self._calcBlockWidth( textBlock );
289                         self._modifyInputBoxWidth();
290                         self._trigger( "add" );
291                 },
292
293                 _removeTextBlock : function () {
294                         var self = this,
295                                 $view = this.element,
296                                 targetBlock = null,
297                                 lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
298
299                         if ( lockBlock !== null && lockBlock.length > 0 ) {
300                                 self._currentWidth -= self._calcBlockWidth( lockBlock );
301                                 lockBlock.remove();
302                                 self._modifyInputBoxWidth();
303                                 this._trigger( "remove" );
304                         } else {
305                                 $view.find( "div:last" ).removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
306                         }
307                 },
308
309                 _calcBlockWidth : function ( block ) {
310                         var blockWidth = 0;
311                         blockWidth = $( block ).outerWidth( true );
312                         return blockWidth;
313                 },
314
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" );
320                         }
321                 },
322
323                 // call when remove text block by backspace key.
324                 _validateTargetBlock : function () {
325                         var self = this,
326                                 $view = self.element,
327                                 lastBlock = $view.find( "div:last" ),
328                                 tmpBlock = null;
329
330                         if ( lastBlock.hasClass( "ui-multibuttonentry-sblock" ) ) {
331                                 self._removeTextBlock();
332                         } else {
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" );
336                         }
337                 },
338
339                 _ellipsisTextBlock : function ( text ) {
340                         var self = this,
341                                 str = text,
342                                 length = 0,
343                                 maxWidth = self._viewWidth,
344                                 maxCharCnt = parseInt( ( self._viewWidth / self._fontSize ), 10 ) - 5,
345                                 ellipsisStr = null;
346                         if ( str ) {
347                                 length = str.length ;
348                                 if ( length > maxCharCnt ) {
349                                         ellipsisStr = str.substring( 0, maxCharCnt );
350                                         ellipsisStr += "...";
351                                 } else {
352                                         ellipsisStr = str;
353                                 }
354                         }
355                         return ellipsisStr;
356                 },
357
358                 _modifyInputBoxWidth : function () {
359                         var self = this,
360                                 $view = self.element,
361                                 labelWidth = self._labelWidth,
362                                 anchorWidth = self._anchorWidth,
363                                 inputBoxWidth = self._viewWidth - labelWidth - anchorWidth,
364                                 blocks = $view.find( "div" ),
365                                 blockWidth = 0,
366                                 index = 0,
367                                 margin = self._marginWidth,
368                                 inputBox = $view.find( ".ui-multibuttonentry-input" );
369
370                         if ( $view.width() === 0 ) {
371                                 return ;
372                         }
373
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;
380                                         } else {
381                                                 inputBoxWidth = self._viewWidth - blockWidth - anchorWidth;
382                                         }
383                                 }
384                         }
385                         $( inputBox ).width( inputBoxWidth - margin - 1 );
386                 },
387
388                 _stringFormat : function ( expression ) {
389                         var pattern = null,
390                                 message = expression,
391                                 i = 0;
392                         for ( i = 1; i < arguments.length; i += 1 ) {
393                                 pattern = "{" + ( i - 1 ) + "}";
394                                 message = message.replace( pattern, arguments[i] );
395                         }
396                         return message;
397                 },
398
399                 _resizeBlock : function () {
400                         var self = this,
401                                 $view = self.element,
402                                 dataBlocks = $( ".ui-multibuttonentry-data" ),
403                                 blocks = $view.find( "div" ),
404                                 srcTexts = [],
405                                 index = 0;
406
407                         $view.hide();
408                         for ( index = 0 ; index < dataBlocks.length ; index += 1 ) {
409                                 srcTexts[index] = $( dataBlocks[index] ).val();
410                                 self._addTextBlock( srcTexts[index] );
411                         }
412                         blocks.remove();
413                         $view.show();
414                 },
415
416                 //---------------------------------------------------- //
417                 //                                      Public Method   //
418                 //----------------------------------------------------//
419                 //
420                 // Focus In Event
421                 //
422                 focusIn : function () {
423                         if ( this._focusStatus === "focusIn" ) {
424                                 return;
425                         }
426
427                         var $view = this.element;
428
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();
435
436                         // change focus state.
437                         this._modifyInputBoxWidth();
438                         this._focusStatus = "focusIn";
439                         $view.removeClass( "ui-multibuttonentry-focusout" ).addClass( "ui-multibuttonentry-focusin" );
440                 },
441
442                 focusOut : function () {
443                         if ( this._focusStatus === "focusOut" ) {
444                                 return;
445                         }
446
447                         var self = this,
448                                 $view = self.element,
449                                 tempBlock = null,
450                                 statement = "",
451                                 index = 0,
452                                 lastIndex = 10,
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 ),
457                                 blockWidth = 0;
458
459                         $view.find( ".ui-multibuttonentry-input" ).hide();
460                         $view.find( "a" ).hide();
461                         blocks.hide();
462
463                         currentWidth = currentWidth - self._reservedWidth;
464
465                         for ( index = 0; index < blocks.length; index++ ) {
466                                 blockWidth = $( blocks[index] ).outerWidth( true );
467                                 if ( currentWidth - blockWidth <= 0 ) {
468                                         lastIndex = index - 1;
469                                         break;
470                                 }
471
472                                 $( blocks[index] ).show();
473                                 currentWidth -= blockWidth;
474                         }
475
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 );
482                         }
483
484                         // update foucs state
485                         this._focusStatus = "focusOut";
486                         $view.removeClass( "ui-multibuttonentry-focusin" ).addClass( "ui-multibuttonentry-focusout" );
487                 },
488
489                 inputText : function ( message ) {
490                         var $view = this.element;
491
492                         if ( arguments.length === 0 ) {
493                                 return $view.find( ".ui-multibuttonentry-input" ).val();
494                         }
495                         $view.find( ".ui-multibuttonentry-input" ).val( message );
496                         return message;
497                 },
498
499                 select : function ( index ) {
500                         var $view = this.element,
501                                 lockBlock = null,
502                                 blocks = null;
503
504                         if ( this._focusStatus === "focusOut" ) {
505                                 return;
506                         }
507
508                         if ( arguments.length === 0 ) {
509                                 // return a selected block.
510                                 lockBlock = $view.find( "div.ui-multibuttonentry-sblock" ).children( ".ui-multibuttonentry-data" );
511                                 if ( lockBlock) {
512                                         return lockBlock.attr( "value" );
513                                 }
514                                 return null;
515                         }
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" );
523                         }
524                         return null;
525                 },
526
527                 add : function ( message, position ) {
528                         if ( this._focusStatus === "focusOut" ) {
529                                 return;
530                         }
531
532                         this._addTextBlock( message, position );
533                 },
534
535                 remove : function ( position ) {
536                         var self = this,
537                                 $view = this.element,
538                                 blocks = $view.find( "div" ),
539                                 index = 0;
540                         if ( this._focusStatus === "focusOut" ) {
541                                 return;
542                         }
543
544                         if ( arguments.length === 0 ) {
545                                 blocks.remove();
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" );
552                         }
553                         self._modifyInputBoxWidth();
554                 },
555
556                 length : function () {
557                         return this.element.find( "div" ).length;
558                 },
559
560                 refresh : function () {
561                         var self = this;
562                         self.element.hide();
563                         self.element.show();
564                 },
565
566                 destroy : function () {
567                         var $view = this.element;
568
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();
573
574                         this._trigger( "destroy" );
575                 }
576         });
577
578         $( document ).bind( "pagecreate create", function () {
579                 $( ":jqmData(role='multibuttonentry')" ).multibuttonentry();
580         });
581
582         $( window ).bind( "resize", function () {
583                 $( ":jqmData(role='multibuttonentry')" ).multibuttonentry( "refresh" );
584         });
585 } ( jQuery, window, document ) );