2.0_beta sync to rsa
[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-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')
40  *
41  *      APIs:
42  *
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.
46  *              select (  [number]  )
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.
57  *              remove ( [number] )
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)
61  *              length ( void )
62  *                      : Get a number of buttons.
63  *              foucsIn ( void )
64  *                      : This method change a status to 'focusin'.
65  *                      This status is able to manage a widget.
66  *              focusOut ( void )
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.
71  *
72  *
73  *      Events:
74  *
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.
79  *
80  *      Examples:
81  *
82  *              <div data-role="multibuttonentry" data-label="To : " data-listUrl:"#addressbook" data-descMessage="{0} & {1} more...">
83  *              </div>
84  *
85  */
86
87 ( function ( $, window, document, undefined ) {
88         $.widget( "tizen.multibuttonentry", $.mobile.widget, {
89                 _focusStatus : null,
90                 _items : null,
91                 _viewWidth : 0,
92                 _reservedWidth : 0,
93                 _currentWidth : 0,
94                 _fontSize : 0,
95                 _anchorWidth : 0,
96                 _labelWidth : 0,
97                 _marginWidth : 0,
98                 options : {
99                         label : "To : ",
100                         listUrl : "#addressbook",
101                         descMessage : "{0} & {1} more..."
102                 },
103                 _create : function () {
104                         var self = this,
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" ) );
111
112                         $view.hide().empty().addClass( "ui-" + role );
113
114                         // create a label tag.
115                         $( labeltag ).text( this.options.label ).addClass( "ui-multibuttonentry-label" );
116                         $view.append( labeltag );
117
118                         // create a input tag
119                         $( inputbox ).text( option.label ).addClass( "ui-multibuttonentry-input" );
120                         $view.append( inputbox );
121
122                         // create a anchor tag.
123                         $( moreBlock ).text( "+" ).attr( "href", option.listUrl ).addClass( "ui-multibuttonentry-link" );
124
125                         // append default htmlelements to main widget.
126                         $view.append( moreBlock );
127
128                         // bind a event
129                         this._bindEvents();
130                         self._focusStatus = "init";
131                         // display widget
132                         $view.show();
133                         $view.attr( "tabindex", -1 ).focusin( function ( e ) {
134                                 self.focusIn();
135                         });
136
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;
143                 },
144                 // bind events
145                 _bindEvents : function () {
146                         var self = this,
147                                 $view = self.element,
148                                 option = self.options,
149                                 inputbox = $view.find( ".ui-multibuttonentry-input" ),
150                                 moreBlock = $view.find( ".ui-multibuttonentry-link" );
151
152                         inputbox.bind( "keydown", function ( event ) {
153                                 // 8  : backspace
154                                 // 13 : Enter
155                                 var keyValue = event.keyCode,
156                                         valueString = $( inputbox ).val();
157
158                                 if ( keyValue == 8 ) {
159                                         if ( valueString.length === 0 ) {
160                                                 self._validateTargetBlock();
161                                         }
162                                 } else if ( keyValue == 13 ) {
163                                         if ( valueString.length !== 0 ) {
164                                                 self._addTextBlock( valueString );
165                                         }
166                                         inputbox.val( "" );
167                                 } else {
168                                         self._unlockTextBlock();
169                                 }
170                         });
171
172                         moreBlock.click( function () {
173                                 $(inputbox).hide();
174
175                                 $.mobile.changePage( option.listUrl, {
176                                         transition: "slide",
177                                         reverse: false,
178                                         changeHash: false
179                                 } );
180                         } );
181
182                         $( document ).bind( "pagechange.mbe", function ( event ) {
183                                 if ( $view.innerWidth() === 0 ) {
184                                         return ;
185                                 }
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();
193                                 }
194                                 self._modifyInputBoxWidth();
195                                 $(inputbox).show();
196                         });
197                 },
198
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 ) {
204                                 return;
205                         }
206
207                         if ( ! messages ) {
208                                 return ;
209                         }
210
211                         var self = this,
212                                 $view = self.element,
213                                 content = messages,
214                                 index = blcokIndex,
215                                 blocks = null,
216                                 dataBlock = null,
217                                 displayText = null,
218                                 textBlock = null;
219
220                         if ( self._viewWidth === 0 ) {
221                                 self._viewWidth = $view.innerWidth();
222                         }
223                         // save src data
224                         dataBlock = $( document.createElement( 'input' ) );
225                         dataBlock.attr( "value", content ).addClass( "ui-multibuttonentry-data" ).hide();
226
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" ) {
235                                         self.focusInEvent();
236                                         return;
237                                 }
238
239                                 if ( $( this ).hasClass( "ui-multibuttonentry-sblock" ) ) {
240                                         // If block is selected, it will be removed.
241                                         self._removeTextBlock();
242                                 }
243
244                                 var lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
245                                 if ( typeof lockBlock != "undefined" ) {
246                                         lockBlock.removeClass( "ui-multibuttonentry-sblock" ).addClass( "ui-multibuttonentry-block" );
247                                 }
248                                 $( this ).removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
249                                 self._trigger( "select" );
250                         });
251
252                         blocks = $view.find( "div" );
253                         if ( index !== null && index <= blocks.length ) {
254                                 $( blocks[index] ).before( textBlock );
255                         } else {
256                                 $view.find( ".ui-multibuttonentry-input" ).before( textBlock );
257                         }
258
259                         self._currentWidth += self._calcBlockWidth( textBlock );
260                         self._modifyInputBoxWidth();
261                         self._trigger( "add" );
262                 },
263
264                 _removeTextBlock : function () {
265                         var self = this,
266                                 $view = this.element,
267                                 targetBlock = null,
268                                 lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
269
270                         if ( lockBlock !== null && lockBlock.length > 0 ) {
271                                 self._currentWidth -= self._calcBlockWidth( lockBlock );
272                                 lockBlock.remove();
273                                 self._modifyInputBoxWidth();
274                                 this._trigger( "remove" );
275                         } else {
276                                 $view.find( "div:last" ).removeClass( "ui-multibuttonentry-block" ).addClass( "ui-multibuttonentry-sblock" );
277                         }
278                 },
279
280                 _calcBlockWidth : function ( block ) {
281                         var blockWidth = 0;
282                         blockWidth = $( block ).outerWidth( true );
283                         return blockWidth;
284                 },
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" );
290                         }
291                 },
292
293                 // call when remove text block by backspace key.
294                 _validateTargetBlock : function () {
295                         var self = this,
296                                 $view = self.element,
297                                 lastBlock = $view.find( "div:last" ),
298                                 tmpBlock = null;
299
300                         if ( lastBlock.hasClass( "ui-multibuttonentry-sblock" ) ) {
301                                 self._removeTextBlock();
302                         } else {
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" );
306                         }
307                 },
308
309                 _ellipsisTextBlock : function ( text ) {
310                         var self = this,
311                                 str = text,
312                                 length = 0,
313                                 maxWidth = self._viewWidth,
314                                 maxCharCnt = parseInt( ( self._viewWidth / self._fontSize ), 10 ) - 5,
315                                 ellipsisStr = null;
316                         if ( str ) {
317                                 length = str.length ;
318                                 if ( length > maxCharCnt ) {
319                                         ellipsisStr = str.substring( 0, maxCharCnt );
320                                         ellipsisStr += "...";
321                                 } else {
322                                         ellipsisStr = str;
323                                 }
324                         }
325                         return ellipsisStr;
326                 },
327                 _modifyInputBoxWidth : function () {
328                         var self = this,
329                                 $view = self.element,
330                                 labelWidth = self._labelWidth,
331                                 anchorWidth = self._anchorWidth,
332                                 inputBoxWidth = self._viewWidth - labelWidth - anchorWidth,
333                                 blocks = $view.find( "div" ),
334                                 blockWidth = 0,
335                                 index = 0,
336                                 margin = self._marginWidth,
337                                 inputBox = $view.find( ".ui-multibuttonentry-input" );
338
339                         if ( $view.width() === 0 ) {
340                                 return ;
341                         }
342
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;
349                                         } else {
350                                                 inputBoxWidth = self._viewWidth - blockWidth - anchorWidth;
351                                         }
352                                 }
353                         }
354                         $( inputBox ).width( inputBoxWidth - margin - 1 );
355                 },
356                 _stringFormat : function ( expression ) {
357                         var pattern = null,
358                                 message = expression,
359                                 i = 0;
360                         for ( i = 1; i < arguments.length; i += 1 ) {
361                                 pattern = "{" + ( i - 1 ) + "}";
362                                 message = message.replace( pattern, arguments[i] );
363                         }
364                         return message;
365                 },
366                 _resizeBlock : function () {
367                         var self = this,
368                                 $view = self.element,
369                                 dataBlocks = $( ".ui-multibuttonentry-data" ),
370                                 blocks = $view.find( "div" ),
371                                 srcTexts = [],
372                                 index = 0;
373
374                         $view.hide();
375                         for ( index = 0 ; index < dataBlocks.length ; index += 1 ) {
376                                 srcTexts[index] = $( dataBlocks[index] ).val();
377                                 self._addTextBlock( srcTexts[index] );
378                         }
379                         blocks.remove();
380                         $view.show();
381                 },
382
383                 //----------------------------------------------------//
384                 //                                      Public Method                                   //
385                 //----------------------------------------------------//
386                 //
387                 // Focus In Event
388                 //
389                 focusIn : function () {
390                         if ( this._focusStatus === "focusIn" ) {
391                                 return;
392                         }
393
394                         var $view = this.element;
395
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();
402
403                         // change focus state.
404                         this._modifyInputBoxWidth();
405                         this._focusStatus = "focusIn";
406                 },
407                 focusOut : function () {
408                         if ( this._focusStatus === "focusOut" ) {
409                                 return;
410                         }
411
412                         var self = this,
413                                 $view = self.element,
414                                 tempBlock = null,
415                                 statement = "",
416                                 index = 0,
417                                 lastIndex = 10,
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;
423
424                         $view.find( ".ui-multibuttonentry-input" ).hide();
425                         $view.find( "a" ).hide();
426                         blocks.hide();
427
428                         // div button
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 );
436                                         break;
437                                 }
438                                 lastIndex = statement.length;
439                         }
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";
448                 },
449                 inputText : function ( message ) {
450                         var $view = this.element;
451
452                         if ( arguments.length === 0 ) {
453                                 return $view.find( ".ui-multibuttonentry-input" ).val();
454                         }
455                         $view.find( ".ui-multibuttonentry-input" ).val( message );
456                         return message;
457                 },
458                 select : function ( index ) {
459                         var $view = this.element,
460                                 lockBlock = null,
461                                 blocks = null;
462
463                         if ( this._focusStatus === "focusOut" ) {
464                                 return;
465                         }
466
467                         if ( arguments.length === 0 ) {
468                                 // return a selected block.
469                                 lockBlock = $view.find( "div.ui-multibuttonentry-sblock" );
470                                 if ( lockBlock) {
471                                         return lockBlock.text();
472                                 }
473                                 return null;
474                         }
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" );
482                         }
483                         return null;
484                 },
485                 add : function ( message, position ) {
486                         if ( this._focusStatus === "focusOut" ) {
487                                 return;
488                         }
489
490                         this._addTextBlock( message, position );
491                 },
492                 remove : function ( position ) {
493                         var self = this,
494                                 $view = this.element,
495                                 blocks = $view.find( "div" ),
496                                 index = 0;
497                         if ( this._focusStatus === "focusOut" ) {
498                                 return;
499                         }
500
501                         if ( arguments.length === 0 ) {
502                                 blocks.remove();
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" );
509                         }
510                         self._modifyInputBoxWidth();
511                 },
512                 length : function () {
513                         return this.element.find( "div" ).length;
514                 },
515                 refresh : function () {
516                         var self = this;
517                         self.element.hide();
518                         self.element.show();
519                 },
520                 destory : function () {
521                         var $view = this.element;
522
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();
527
528                         this._trigger( "destory" );
529                 }
530         });
531
532         $( document ).bind( "pagecreate create", function () {
533                 $( ":jqmData(role='multibuttonentry')" ).multibuttonentry();
534         });
535
536         $( window ).bind( "resize", function () {
537                 $( ":jqmData(role='multibuttonentry')" ).multibuttonentry( "refresh" );
538         });
539 } ( jQuery, window, document ) );