Roo/bootstrap/PhoneInput.js
[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 Roo.bootstrap.PhoneInput = function(config) {
10     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
11 };
12
13 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
14         
15         triggerList : true,
16         
17         listWidth: undefined,
18         
19         selectedClass: 'active',
20         
21         dialCodeMapping: false,
22         
23         defaultDialCode: '+852',
24         
25         preferedCountries: false,
26         
27         allCountries: false,
28         
29         CountryData: Roo.bootstrap.PhoneInput.AllCountries,
30         
31         getAutoCreate : function()
32         {
33             var align = this.labelAlign || this.parentLabelAlign();
34             var id = Roo.id();
35             
36             for (var i = 0; i < this.CountryData.length; i++) {
37               var c = this.CountryData[i];
38               this.allCountries[i] = {
39                 name: c[0],
40                 iso2: c[1],
41                 dialCode: c[2],
42                 priority: c[3] || 0,
43                 areaCodes: c[4] || null
44               };
45               this.dialCodeMapping[c[2]] = {
46                   name: c[0],
47                   iso2: c[1],
48                   priority: c[3] || 0,
49                   areaCodes: c[4] || null
50               };
51             }
52             
53             var cfg = {
54                 cls: 'form-group',
55                 cn: []
56             };
57             
58             var input =  {
59                 tag: 'input',
60                 id : id,
61                 type : 'number',
62                 cls : 'form-control tel-input',
63                 autocomplete: 'new-password'
64             };
65             
66             if (this.name) {
67                 input.name = this.name;
68             }
69             
70             if (this.disabled) {
71                 input.disabled = true;
72             }
73             
74             var flag_container = {
75                 tag: 'div',
76                 cls: 'flag-box',
77                 cn: [
78                     {
79                         tag: 'div',
80                         cls: 'flag'
81                     },
82                     {
83                         tag: 'div',
84                         cls: 'caret'
85                     }
86                 ]
87             };
88             
89             var box = {
90                 tag: 'div',
91                 cls: this.hasFeedback ? 'has-feedback' : '',
92                 cn: [
93                     input,
94                     {
95                         tag: 'input',
96                         cls: 'dial-code-holder',
97                         disabled: true
98                     }
99                 ]
100             };
101             
102             var container = {
103                 cls: 'roo-select2-container input-group',
104                 cn: [
105                     flag_container,
106                     box
107                 ]
108             };
109             
110             if (this.fieldLabel.length) {
111                 var indicator = {
112                     tag: 'i',
113                     tooltip: 'This field is required'
114                 };
115                 
116                 var label = {
117                     tag: 'label',
118                     'for':  id,
119                     cls: 'control-label',
120                     cn: []
121                 };
122                 
123                 var label_text = {
124                     tag: 'span',
125                     html: this.fieldLabel
126                 };
127                 
128                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
129                 label.cn = [
130                     indicator,
131                     label_text
132                 ];
133                 
134                 if(this.indicatorpos == 'right') {
135                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
136                     label.cn = [
137                         label_text,
138                         indicator
139                     ];
140                 }
141                 
142                 if(align == 'left') {
143                     container = {
144                         tag: 'div',
145                         cn: [
146                             container
147                         ]
148                     };
149                     
150                     if(this.labelWidth > 12){
151                         label.style = "width: " + this.labelWidth + 'px';
152                     }
153                     if(this.labelWidth < 13 && this.labelmd == 0){
154                         this.labelmd = this.labelWidth;
155                     }
156                     if(this.labellg > 0){
157                         label.cls += ' col-lg-' + this.labellg;
158                         input.cls += ' col-lg-' + (12 - this.labellg);
159                     }
160                     if(this.labelmd > 0){
161                         label.cls += ' col-md-' + this.labelmd;
162                         container.cls += ' col-md-' + (12 - this.labelmd);
163                     }
164                     if(this.labelsm > 0){
165                         label.cls += ' col-sm-' + this.labelsm;
166                         container.cls += ' col-sm-' + (12 - this.labelsm);
167                     }
168                     if(this.labelxs > 0){
169                         label.cls += ' col-xs-' + this.labelxs;
170                         container.cls += ' col-xs-' + (12 - this.labelxs);
171                     }
172                 }
173             }
174             
175             cfg.cn = [
176                 label,
177                 container
178             ];
179             
180             var settings = this;
181             
182             ['xs','sm','md','lg'].map(function(size){
183                 if (settings[size]) {
184                     cfg.cls += ' col-' + size + '-' + settings[size];
185                 }
186             });
187             
188             this.store = new Roo.data.Store({
189                 proxy : new Roo.data.MemoryProxy({}),
190                 reader : new Roo.data.JsonReader({
191                     fields : [
192                         {
193                             'name' : 'name',
194                             'type' : 'string'
195                         },
196                         {
197                             'name' : 'iso2',
198                             'type' : 'string'
199                         },
200                         {
201                             'name' : 'dialCode',
202                             'type' : 'string'
203                         },
204                         {
205                             'name' : 'priority',
206                             'type' : 'string'
207                         },
208                         {
209                             'name' : 'areaCodes',
210                             'type' : 'string'
211                         }
212                     ]
213                 })
214             });
215             
216             this.store.proxy.data = {
217                 success: true,
218                 data: this.allCountries
219             };
220             
221             return cfg;
222         },
223         
224         initEvents : function()
225         {
226             this.createList();
227             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
228             
229             this.indicator = this.indicatorEl();
230             this.flag = this.flagEl();
231             this.dialCodeHolder = this.dialCodeHolderEl();
232             
233             this.trigger = this.el.select('div.flag-box',true).first();
234             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
235             
236             var _this = this;
237             
238             (function(){
239                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
240                 _this.list.setWidth(lw);
241             }).defer(100);
242             
243             this.list.on('mouseover', this.onViewOver, this);
244             this.list.on('mousemove', this.onViewMove, this);
245             //this.list.on('scroll', this.onViewScroll, this);
246             
247             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
248
249             this.view = new Roo.View(this.list, this.tpl, {
250                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
251             });
252             
253             this.view.on('click', this.onViewClick, this);
254             this.setValue(this.defaultDialCode);
255         },
256         
257         onTriggerClick : function(e)
258         {
259             Roo.log('trigger click');
260             if(this.disabled || !this.triggerList){
261                 return;
262             }
263             
264             if(this.isExpanded()){
265                 this.collapse();
266                 this.hasFocus = false;
267             }else {
268                 this.store.load({});
269                 this.hasFocus = true;
270                 this.expand();
271             }
272         },
273         
274         isExpanded : function()
275         {
276             return this.list.isVisible();
277         },
278         
279         collapse : function()
280         {
281             if(!this.isExpanded()){
282                 return;
283             }
284             this.list.hide();
285             Roo.get(document).un('mousedown', this.collapseIf, this);
286             Roo.get(document).un('mousewheel', this.collapseIf, this);
287             this.fireEvent('collapse', this);
288             this.validate();
289         },
290         
291         expand : function()
292         {
293             Roo.log('expand');
294
295             if(this.isExpanded() || !this.hasFocus){
296                 return;
297             }
298             
299             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
300             this.list.setWidth(lw);
301             
302             this.list.show();
303             this.restrictHeight();
304             
305             Roo.get(document).on('mousedown', this.collapseIf, this);
306             Roo.get(document).on('mousewheel', this.collapseIf, this);
307             
308             this.fireEvent('expand', this);
309         },
310         
311         restrictHeight : function()
312         {
313             this.list.alignTo(this.inputEl(), this.listAlign);
314             this.list.alignTo(this.inputEl(), this.listAlign);
315         },
316         
317         onViewOver : function(e, t)
318         {
319             if(this.inKeyMode){
320                 return;
321             }
322             var item = this.view.findItemFromChild(t);
323             
324             if(item){
325                 var index = this.view.indexOf(item);
326                 this.select(index, false);
327             }
328         },
329
330         // private
331         onViewClick : function(view, doFocus, el, e)
332         {
333             var index = this.view.getSelectedIndexes()[0];
334             
335             var r = this.store.getAt(index);
336             
337             if(r){
338                 this.onSelect(r, index);
339             }
340             if(doFocus !== false && !this.blockFocus){
341                 this.inputEl().focus();
342             }
343         },
344         
345         onViewMove : function(e, t)
346         {
347             this.inKeyMode = false;
348         },
349         
350         select : function(index, scrollIntoView)
351         {
352             this.selectedIndex = index;
353             this.view.select(index);
354             if(scrollIntoView !== false){
355                 var el = this.view.getNode(index);
356                 if(el){
357                     this.list.scrollChildIntoView(el, false);
358                 }
359             }
360         },
361         
362         createList : function()
363         {
364             this.list = Roo.get(document.body).createChild({
365                 tag: 'ul',
366                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
367                 style: 'display:none'
368             });
369             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
370         },
371         
372         collapseIf : function(e)
373         {
374             var in_combo  = e.within(this.el);
375             var in_list =  e.within(this.list);
376             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
377             
378             if (in_combo || in_list || is_list) {
379                 return;
380             }
381             this.collapse();
382         },
383         
384         onSelect : function(record, index)
385         {
386             if(this.fireEvent('beforeselect', this, record, index) !== false){
387                 
388                 this.setFlagClass(record.data.iso2);
389                 this.setDialCode(record.data.dialCode);
390                 this.hasFocus = false;
391                 this.collapse();
392                 this.fireEvent('select', this, record, index);
393             }
394         },
395         
396         flagEl : function()
397         {
398             var flag = this.el.select('div.flag',true).first();
399             if(!flag){
400                 return false;
401             }
402             return flag;
403         },
404         
405         dialCodeHolderEl : function()
406         {
407             var d = this.el.select('input.dial-code-holder',true).first();
408             if(!d){
409                 return false;
410             }
411             return d;
412         },
413         
414         setDialCode : function(v)
415         {
416             this.dialCodeHolder.dom.value = '+'+v;
417         },
418         
419         setFlagClass : function(n)
420         {
421             this.flag.dom.className = 'flag '+n;
422         },
423         
424         getValue : function()
425         {
426             var v = this.inputEl().getValue();
427             if(this.dialCodeHolder) {
428                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
429             }
430             return v;
431         },
432         
433         setValue : function(v)
434         {
435             var d = this.getDialCode(v);
436             this.value = v;
437             
438             if(!d || d.length == 0) {
439                 if(this.rendered){
440                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
441                 }
442                 return;
443             }
444             
445             this.setFlagClass(this.dialCodeMapping[d].iso2);
446             this.setDialCode(d);
447             this.inputEl().dom.value = v.replace('+'+d,'');
448         },
449         
450         getDialCode : function(v = '')
451         {
452             if (v.length == 0) {
453                 return this.dialCodeHolder.dom.value;
454             }
455             
456             var dialCode = "";
457             // only interested in international numbers (starting with a plus)
458             if (v.charAt(0) != "+") {
459                 return false;
460             }
461             var numericChars = "";
462             for (var i = 1; i < v.length; i++) {
463               var c = v.charAt(i);
464               if (!isNaN(c)) {
465                 numericChars += c;
466                 if (this.dialCodeMapping[numericChars]) {
467                   dialCode = v.substr(1, i);
468                 }
469                 if (numericChars.length == 4) {
470                   break;
471                 }
472               }
473             }
474             return dialCode;
475         },
476         
477         validate : function()
478         {
479             //
480             return false;
481         }
482 });