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