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