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