sync
[roojs1] / Roo / bootstrap / PhoneInput.js
1 /**
2 *    This script refer to:
3 *    Title: International Telephone Input
4 *    Author: Jack O'Connor
5 *    Code version:  v12.1.12
6 *    Availability: https://github.com/jackocnr/intl-tel-input.git
7 **/
8
9 /**
10  * @class Roo.bootstrap.PhoneInput
11  * @extends Roo.bootstrap.TriggerField
12  * An input with International dial-code selection
13  
14  * @cfg {String} defaultDialCode default '+852'
15  * @cfg {Array} preferedCountries default []
16   
17  * @constructor
18  * Create a new PhoneInput.
19  * @param {Object} config Configuration options
20  */
21
22 Roo.bootstrap.PhoneInput = function(config) {
23     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
24 };
25
26 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
27         
28         listWidth: undefined,
29         
30         selectedClass: 'active',
31         
32         invalidClass : "has-warning",
33         
34         validClass: 'has-success',
35         
36         allowed: '0123456789',
37         
38         /**
39          * @cfg {String} defaultDialCode The default dial code when initializing the input
40          */
41         defaultDialCode: '+852',
42         
43         /**
44          * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
45          */
46         preferedCountries: false,
47         
48         getAutoCreate : function()
49         {
50             var data = Roo.bootstrap.PhoneInputData();
51             var align = this.labelAlign || this.parentLabelAlign();
52             var id = Roo.id();
53             
54             this.allCountries = [];
55             this.dialCodeMapping = [];
56             
57             for (var i = 0; i < data.length; i++) {
58               var c = data[i];
59               this.allCountries[i] = {
60                 name: c[0],
61                 iso2: c[1],
62                 dialCode: c[2],
63                 priority: c[3] || 0,
64                 areaCodes: c[4] || null
65               };
66               this.dialCodeMapping[c[2]] = {
67                   name: c[0],
68                   iso2: c[1],
69                   priority: c[3] || 0,
70                   areaCodes: c[4] || null
71               };
72             }
73             
74             var cfg = {
75                 cls: 'form-group',
76                 cn: []
77             };
78             
79             var input =  {
80                 tag: 'input',
81                 id : id,
82                 cls : 'form-control tel-input',
83                 autocomplete: 'new-password'
84             };
85             
86             var hiddenInput = {
87                 tag: 'input',
88                 type: 'hidden',
89                 cls: 'hidden-tel-input'
90             };
91             
92             if (this.name) {
93                 hiddenInput.name = this.name;
94             }
95             
96             if (this.disabled) {
97                 input.disabled = true;
98             }
99             
100             var flag_container = {
101                 tag: 'div',
102                 cls: 'flag-box',
103                 cn: [
104                     {
105                         tag: 'div',
106                         cls: 'flag'
107                     },
108                     {
109                         tag: 'div',
110                         cls: 'caret'
111                     }
112                 ]
113             };
114             
115             var box = {
116                 tag: 'div',
117                 cls: this.hasFeedback ? 'has-feedback' : '',
118                 cn: [
119                     hiddenInput,
120                     input,
121                     {
122                         tag: 'input',
123                         cls: 'dial-code-holder',
124                         disabled: true
125                     }
126                 ]
127             };
128             
129             var container = {
130                 cls: 'roo-select2-container input-group',
131                 cn: [
132                     flag_container,
133                     box
134                 ]
135             };
136             
137             if (this.fieldLabel.length) {
138                 var indicator = {
139                     tag: 'i',
140                     tooltip: 'This field is required'
141                 };
142                 
143                 var label = {
144                     tag: 'label',
145                     'for':  id,
146                     cls: 'control-label',
147                     cn: []
148                 };
149                 
150                 var label_text = {
151                     tag: 'span',
152                     html: this.fieldLabel
153                 };
154                 
155                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
156                 label.cn = [
157                     indicator,
158                     label_text
159                 ];
160                 
161                 if(this.indicatorpos == 'right') {
162                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
163                     label.cn = [
164                         label_text,
165                         indicator
166                     ];
167                 }
168                 
169                 if(align == 'left') {
170                     container = {
171                         tag: 'div',
172                         cn: [
173                             container
174                         ]
175                     };
176                     
177                     if(this.labelWidth > 12){
178                         label.style = "width: " + this.labelWidth + 'px';
179                     }
180                     if(this.labelWidth < 13 && this.labelmd == 0){
181                         this.labelmd = this.labelWidth;
182                     }
183                     if(this.labellg > 0){
184                         label.cls += ' col-lg-' + this.labellg;
185                         input.cls += ' col-lg-' + (12 - this.labellg);
186                     }
187                     if(this.labelmd > 0){
188                         label.cls += ' col-md-' + this.labelmd;
189                         container.cls += ' col-md-' + (12 - this.labelmd);
190                     }
191                     if(this.labelsm > 0){
192                         label.cls += ' col-sm-' + this.labelsm;
193                         container.cls += ' col-sm-' + (12 - this.labelsm);
194                     }
195                     if(this.labelxs > 0){
196                         label.cls += ' col-xs-' + this.labelxs;
197                         container.cls += ' col-xs-' + (12 - this.labelxs);
198                     }
199                 }
200             }
201             
202             cfg.cn = [
203                 label,
204                 container
205             ];
206             
207             var settings = this;
208             
209             ['xs','sm','md','lg'].map(function(size){
210                 if (settings[size]) {
211                     cfg.cls += ' col-' + size + '-' + settings[size];
212                 }
213             });
214             
215             this.store = new Roo.data.Store({
216                 proxy : new Roo.data.MemoryProxy({}),
217                 reader : new Roo.data.JsonReader({
218                     fields : [
219                         {
220                             'name' : 'name',
221                             'type' : 'string'
222                         },
223                         {
224                             'name' : 'iso2',
225                             'type' : 'string'
226                         },
227                         {
228                             'name' : 'dialCode',
229                             'type' : 'string'
230                         },
231                         {
232                             'name' : 'priority',
233                             'type' : 'string'
234                         },
235                         {
236                             'name' : 'areaCodes',
237                             'type' : 'string'
238                         }
239                     ]
240                 })
241             });
242             
243             if(!this.preferedCountries) {
244                 this.preferedCountries = [
245                     'hk',
246                     'gb',
247                     'us'
248                 ];
249             }
250             
251             var p = this.preferedCountries.reverse();
252             
253             if(p) {
254                 for (var i = 0; i < p.length; i++) {
255                     for (var j = 0; j < this.allCountries.length; j++) {
256                         if(this.allCountries[j].iso2 == p[i]) {
257                             var t = this.allCountries[j];
258                             this.allCountries.splice(j,1);
259                             this.allCountries.unshift(t);
260                         }
261                     } 
262                 }
263             }
264             
265             this.store.proxy.data = {
266                 success: true,
267                 data: this.allCountries
268             };
269             
270             return cfg;
271         },
272         
273         initEvents : function()
274         {
275             this.createList();
276             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
277             
278             this.indicator = this.indicatorEl();
279             this.flag = this.flagEl();
280             this.dialCodeHolder = this.dialCodeHolderEl();
281             
282             this.trigger = this.el.select('div.flag-box',true).first();
283             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
284             
285             var _this = this;
286             
287             (function(){
288                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
289                 _this.list.setWidth(lw);
290             }).defer(100);
291             
292             this.list.on('mouseover', this.onViewOver, this);
293             this.list.on('mousemove', this.onViewMove, this);
294             this.inputEl().on("keyup", this.onKeyUp, this);
295             
296             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
297
298             this.view = new Roo.View(this.list, this.tpl, {
299                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
300             });
301             
302             this.view.on('click', this.onViewClick, this);
303             this.setValue(this.defaultDialCode);
304         },
305         
306         onTriggerClick : function(e)
307         {
308             Roo.log('trigger click');
309             if(this.disabled){
310                 return;
311             }
312             
313             if(this.isExpanded()){
314                 this.collapse();
315                 this.hasFocus = false;
316             }else {
317                 this.store.load({});
318                 this.hasFocus = true;
319                 this.expand();
320             }
321         },
322         
323         isExpanded : function()
324         {
325             return this.list.isVisible();
326         },
327         
328         collapse : function()
329         {
330             if(!this.isExpanded()){
331                 return;
332             }
333             this.list.hide();
334             Roo.get(document).un('mousedown', this.collapseIf, this);
335             Roo.get(document).un('mousewheel', this.collapseIf, this);
336             this.fireEvent('collapse', this);
337             this.validate();
338         },
339         
340         expand : function()
341         {
342             Roo.log('expand');
343
344             if(this.isExpanded() || !this.hasFocus){
345                 return;
346             }
347             
348             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
349             this.list.setWidth(lw);
350             
351             this.list.show();
352             this.restrictHeight();
353             
354             Roo.get(document).on('mousedown', this.collapseIf, this);
355             Roo.get(document).on('mousewheel', this.collapseIf, this);
356             
357             this.fireEvent('expand', this);
358         },
359         
360         restrictHeight : function()
361         {
362             this.list.alignTo(this.inputEl(), this.listAlign);
363             this.list.alignTo(this.inputEl(), this.listAlign);
364         },
365         
366         onViewOver : function(e, t)
367         {
368             if(this.inKeyMode){
369                 return;
370             }
371             var item = this.view.findItemFromChild(t);
372             
373             if(item){
374                 var index = this.view.indexOf(item);
375                 this.select(index, false);
376             }
377         },
378
379         // private
380         onViewClick : function(view, doFocus, el, e)
381         {
382             var index = this.view.getSelectedIndexes()[0];
383             
384             var r = this.store.getAt(index);
385             
386             if(r){
387                 this.onSelect(r, index);
388             }
389             if(doFocus !== false && !this.blockFocus){
390                 this.inputEl().focus();
391             }
392         },
393         
394         onViewMove : function(e, t)
395         {
396             this.inKeyMode = false;
397         },
398         
399         select : function(index, scrollIntoView)
400         {
401             this.selectedIndex = index;
402             this.view.select(index);
403             if(scrollIntoView !== false){
404                 var el = this.view.getNode(index);
405                 if(el){
406                     this.list.scrollChildIntoView(el, false);
407                 }
408             }
409         },
410         
411         createList : function()
412         {
413             this.list = Roo.get(document.body).createChild({
414                 tag: 'ul',
415                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
416                 style: 'display:none'
417             });
418             
419             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
420         },
421         
422         collapseIf : function(e)
423         {
424             var in_combo  = e.within(this.el);
425             var in_list =  e.within(this.list);
426             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
427             
428             if (in_combo || in_list || is_list) {
429                 return;
430             }
431             this.collapse();
432         },
433         
434         onSelect : function(record, index)
435         {
436             if(this.fireEvent('beforeselect', this, record, index) !== false){
437                 
438                 this.setFlagClass(record.data.iso2);
439                 this.setDialCode(record.data.dialCode);
440                 this.hasFocus = false;
441                 this.collapse();
442                 this.fireEvent('select', this, record, index);
443             }
444         },
445         
446         flagEl : function()
447         {
448             var flag = this.el.select('div.flag',true).first();
449             if(!flag){
450                 return false;
451             }
452             return flag;
453         },
454         
455         dialCodeHolderEl : function()
456         {
457             var d = this.el.select('input.dial-code-holder',true).first();
458             if(!d){
459                 return false;
460             }
461             return d;
462         },
463         
464         setDialCode : function(v)
465         {
466             this.dialCodeHolder.dom.value = '+'+v;
467         },
468         
469         setFlagClass : function(n)
470         {
471             this.flag.dom.className = 'flag '+n;
472         },
473         
474         getValue : function()
475         {
476             var v = this.inputEl().getValue();
477             if(this.dialCodeHolder) {
478                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
479             }
480             return v;
481         },
482         
483         setValue : function(v)
484         {
485             var d = this.getDialCode(v);
486             
487             //invalid dial code
488             if(v.length == 0 || !d || d.length == 0) {
489                 if(this.rendered){
490                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
491                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
492                 }
493                 return;
494             }
495             
496             //valid dial code
497             this.setFlagClass(this.dialCodeMapping[d].iso2);
498             this.setDialCode(d);
499             this.inputEl().dom.value = v.replace('+'+d,'');
500             this.hiddenEl().dom.value = this.getValue();
501             
502             this.validate();
503         },
504         
505         getDialCode : function(v)
506         {
507             v = v ||  '';
508             
509             if (v.length == 0) {
510                 return this.dialCodeHolder.dom.value;
511             }
512             
513             var dialCode = "";
514             if (v.charAt(0) != "+") {
515                 return false;
516             }
517             var numericChars = "";
518             for (var i = 1; i < v.length; i++) {
519               var c = v.charAt(i);
520               if (!isNaN(c)) {
521                 numericChars += c;
522                 if (this.dialCodeMapping[numericChars]) {
523                   dialCode = v.substr(1, i);
524                 }
525                 if (numericChars.length == 4) {
526                   break;
527                 }
528               }
529             }
530             return dialCode;
531         },
532         
533         reset : function()
534         {
535             this.setValue(this.defaultDialCode);
536             this.validate();
537         },
538         
539         hiddenEl : function()
540         {
541             return this.el.select('input.hidden-tel-input',true).first();
542         },
543         
544         onKeyUp : function(e){
545             
546             var k = e.getKey();
547             var c = e.getCharCode();
548             
549             if(
550                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
551                     this.allowed.indexOf(String.fromCharCode(c)) === -1
552             ){
553                 e.stopEvent();
554             }
555             
556             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
557             //     return;
558             // }
559             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
560                 e.stopEvent();
561             }
562             
563             this.setValue(this.getValue());
564         }
565         
566 });