Roo/bootstrap/ComboBox.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372     }
373 });
374
375  /*
376  * - LGPL
377  *
378  * Body
379  *
380  */
381
382 /**
383  * @class Roo.bootstrap.Body
384  * @extends Roo.bootstrap.Component
385  * Bootstrap Body class
386  *
387  * @constructor
388  * Create a new body
389  * @param {Object} config The config object
390  */
391
392 Roo.bootstrap.Body = function(config){
393
394     config = config || {};
395
396     Roo.bootstrap.Body.superclass.constructor.call(this, config);
397     this.el = Roo.get(config.el ? config.el : document.body );
398     if (this.cls && this.cls.length) {
399         Roo.get(document.body).addClass(this.cls);
400     }
401 };
402
403 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
404
405     is_body : true,// just to make sure it's constructed?
406
407         autoCreate : {
408         cls: 'container'
409     },
410     onRender : function(ct, position)
411     {
412        /* Roo.log("Roo.bootstrap.Body - onRender");
413         if (this.cls && this.cls.length) {
414             Roo.get(document.body).addClass(this.cls);
415         }
416         // style??? xttr???
417         */
418     }
419
420
421
422
423 });
424 /*
425  * - LGPL
426  *
427  * button group
428  * 
429  */
430
431
432 /**
433  * @class Roo.bootstrap.ButtonGroup
434  * @extends Roo.bootstrap.Component
435  * Bootstrap ButtonGroup class
436  * @cfg {String} size lg | sm | xs (default empty normal)
437  * @cfg {String} align vertical | justified  (default none)
438  * @cfg {String} direction up | down (default down)
439  * @cfg {Boolean} toolbar false | true
440  * @cfg {Boolean} btn true | false
441  * 
442  * 
443  * @constructor
444  * Create a new Input
445  * @param {Object} config The config object
446  */
447
448 Roo.bootstrap.ButtonGroup = function(config){
449     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
450 };
451
452 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
453     
454     size: '',
455     align: '',
456     direction: '',
457     toolbar: false,
458     btn: true,
459
460     getAutoCreate : function(){
461         var cfg = {
462             cls: 'btn-group',
463             html : null
464         };
465         
466         cfg.html = this.html || cfg.html;
467         
468         if (this.toolbar) {
469             cfg = {
470                 cls: 'btn-toolbar',
471                 html: null
472             };
473             
474             return cfg;
475         }
476         
477         if (['vertical','justified'].indexOf(this.align)!==-1) {
478             cfg.cls = 'btn-group-' + this.align;
479             
480             if (this.align == 'justified') {
481                 console.log(this.items);
482             }
483         }
484         
485         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
486             cfg.cls += ' btn-group-' + this.size;
487         }
488         
489         if (this.direction == 'up') {
490             cfg.cls += ' dropup' ;
491         }
492         
493         return cfg;
494     }
495    
496 });
497
498  /*
499  * - LGPL
500  *
501  * button
502  * 
503  */
504
505 /**
506  * @class Roo.bootstrap.Button
507  * @extends Roo.bootstrap.Component
508  * Bootstrap Button class
509  * @cfg {String} html The button content
510  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
511  * @cfg {String} size ( lg | sm | xs)
512  * @cfg {String} tag ( a | input | submit)
513  * @cfg {String} href empty or href
514  * @cfg {Boolean} disabled default false;
515  * @cfg {Boolean} isClose default false;
516  * @cfg {String} glyphicon (| adjust | align-center | align-justify | align-left | align-right | arrow-down | arrow-left | arrow-right | arrow-up | asterisk | backward | ban-circle | barcode | bell | bold | book | bookmark | briefcase | bullhorn | calendar | camera | certificate | check | chevron-down | chevron-left | chevron-right | chevron-up | circle-arrow-down | circle-arrow-left | circle-arrow-right | circle-arrow-up | cloud | cloud-download | cloud-upload | cog | collapse-down | collapse-up | comment | compressed | copyright-mark | credit-card | cutlery | dashboard | download | download-alt | earphone | edit | eject | envelope | euro | exclamation-sign | expand | export | eye-close | eye-open | facetime-video | fast-backward | fast-forward | file | film | filter | fire | flag | flash | floppy-disk | floppy-open | floppy-remove | floppy-save | floppy-saved | folder-close | folder-open | font | forward | fullscreen | gbp | gift | glass | globe | hand-down | hand-left | hand-right | hand-up | hd-video | hdd | header | headphones | heart | heart-empty | home | import | inbox | indent-left | indent-right | info-sign | italic | leaf | link | list | list-alt | lock | log-in | log-out | magnet | map-marker | minus | minus-sign | move | music | new-window | off | ok | ok-circle | ok-sign | open | paperclip | pause | pencil | phone | phone-alt | picture | plane | play | play-circle | plus | plus-sign | print | pushpin | qrcode | question-sign | random | record | refresh | registration-mark | remove | remove-circle | remove-sign | repeat | resize-full | resize-horizontal | resize-small | resize-vertical | retweet | road | save | saved | screenshot | sd-video | search | send | share | share-alt | shopping-cart | signal | sort | sort-by-alphabet | sort-by-alphabet-alt | sort-by-attributes | sort-by-attributes-alt | sort-by-order | sort-by-order-alt | sound-5-1 | sound-6-1 | sound-7-1 | sound-dolby | sound-stereo | star | star-empty | stats | step-backward | step-forward | stop | subtitles | tag | tags | tasks | text-height | text-width | th | th-large | th-list | thumbs-down | thumbs-up | time | tint | tower | transfer | trash | tree-conifer | tree-deciduous | unchecked | upload | usd | user | volume-down | volume-off | volume-up | warning-sign | wrench | zoom-in | zoom-out)
517  * @cfg {String} badge text for badge
518  * @cfg {String} theme default 
519  * @cfg {Boolean} inverse 
520  * @cfg {Boolean} toggle 
521  * @cfg {String} ontext text for on toggle state
522  * @cfg {String} offtext text for off toggle state
523  * @cfg {Boolean} defaulton 
524  * @cfg {Boolean} preventDefault  default true
525  * @cfg {Boolean} removeClass remove the standard class..
526  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
527  * 
528  * @constructor
529  * Create a new button
530  * @param {Object} config The config object
531  */
532
533
534 Roo.bootstrap.Button = function(config){
535     Roo.bootstrap.Button.superclass.constructor.call(this, config);
536     this.weightClass = ["btn-default", 
537                        "btn-primary", 
538                        "btn-success", 
539                        "btn-info", 
540                        "btn-warning",
541                        "btn-danger",
542                        "btn-link"
543                       ],  
544     this.addEvents({
545         // raw events
546         /**
547          * @event click
548          * When a butotn is pressed
549          * @param {Roo.bootstrap.Button} this
550          * @param {Roo.EventObject} e
551          */
552         "click" : true,
553          /**
554          * @event toggle
555          * After the button has been toggles
556          * @param {Roo.EventObject} e
557          * @param {boolean} pressed (also available as button.pressed)
558          */
559         "toggle" : true
560     });
561 };
562
563 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
564     html: false,
565     active: false,
566     weight: '',
567     size: '',
568     tag: 'button',
569     href: '',
570     disabled: false,
571     isClose: false,
572     glyphicon: '',
573     badge: '',
574     theme: 'default',
575     inverse: false,
576     
577     toggle: false,
578     ontext: 'ON',
579     offtext: 'OFF',
580     defaulton: true,
581     preventDefault: true,
582     removeClass: false,
583     name: false,
584     target: false,
585     
586     
587     pressed : null,
588      
589     
590     getAutoCreate : function(){
591         
592         var cfg = {
593             tag : 'button',
594             cls : 'roo-button',
595             html: ''
596         };
597         
598         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
599             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
600             this.tag = 'button';
601         } else {
602             cfg.tag = this.tag;
603         }
604         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
605         
606         if (this.toggle == true) {
607             cfg={
608                 tag: 'div',
609                 cls: 'slider-frame roo-button',
610                 cn: [
611                     {
612                         tag: 'span',
613                         'data-on-text':'ON',
614                         'data-off-text':'OFF',
615                         cls: 'slider-button',
616                         html: this.offtext
617                     }
618                 ]
619             };
620             
621             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
622                 cfg.cls += ' '+this.weight;
623             }
624             
625             return cfg;
626         }
627         
628         if (this.isClose) {
629             cfg.cls += ' close';
630             
631             cfg["aria-hidden"] = true;
632             
633             cfg.html = "&times;";
634             
635             return cfg;
636         }
637         
638          
639         if (this.theme==='default') {
640             cfg.cls = 'btn roo-button';
641             
642             //if (this.parentType != 'Navbar') {
643             this.weight = this.weight.length ?  this.weight : 'default';
644             //}
645             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646                 
647                 cfg.cls += ' btn-' + this.weight;
648             }
649         } else if (this.theme==='glow') {
650             
651             cfg.tag = 'a';
652             cfg.cls = 'btn-glow roo-button';
653             
654             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
655                 
656                 cfg.cls += ' ' + this.weight;
657             }
658         }
659    
660         
661         if (this.inverse) {
662             this.cls += ' inverse';
663         }
664         
665         
666         if (this.active) {
667             cfg.cls += ' active';
668         }
669         
670         if (this.disabled) {
671             cfg.disabled = 'disabled';
672         }
673         
674         if (this.items) {
675             Roo.log('changing to ul' );
676             cfg.tag = 'ul';
677             this.glyphicon = 'caret';
678         }
679         
680         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
681          
682         //gsRoo.log(this.parentType);
683         if (this.parentType === 'Navbar' && !this.parent().bar) {
684             Roo.log('changing to li?');
685             
686             cfg.tag = 'li';
687             
688             cfg.cls = '';
689             cfg.cn =  [{
690                 tag : 'a',
691                 cls : 'roo-button',
692                 html : this.html,
693                 href : this.href || '#'
694             }];
695             if (this.menu) {
696                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
697                 cfg.cls += ' dropdown';
698             }   
699             
700             delete cfg.html;
701             
702         }
703         
704        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
705         
706         if (this.glyphicon) {
707             cfg.html = ' ' + cfg.html;
708             
709             cfg.cn = [
710                 {
711                     tag: 'span',
712                     cls: 'glyphicon glyphicon-' + this.glyphicon
713                 }
714             ];
715         }
716         
717         if (this.badge) {
718             cfg.html += ' ';
719             
720             cfg.tag = 'a';
721             
722 //            cfg.cls='btn roo-button';
723             
724             cfg.href=this.href;
725             
726             var value = cfg.html;
727             
728             if(this.glyphicon){
729                 value = {
730                             tag: 'span',
731                             cls: 'glyphicon glyphicon-' + this.glyphicon,
732                             html: this.html
733                         };
734                 
735             }
736             
737             cfg.cn = [
738                 value,
739                 {
740                     tag: 'span',
741                     cls: 'badge',
742                     html: this.badge
743                 }
744             ];
745             
746             cfg.html='';
747         }
748         
749         if (this.menu) {
750             cfg.cls += ' dropdown';
751             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
752         }
753         
754         if (cfg.tag !== 'a' && this.href !== '') {
755             throw "Tag must be a to set href.";
756         } else if (this.href.length > 0) {
757             cfg.href = this.href;
758         }
759         
760         if(this.removeClass){
761             cfg.cls = '';
762         }
763         
764         if(this.target){
765             cfg.target = this.target;
766         }
767         
768         return cfg;
769     },
770     initEvents: function() {
771        // Roo.log('init events?');
772 //        Roo.log(this.el.dom);
773         // add the menu...
774         
775         if (typeof (this.menu) != 'undefined') {
776             this.menu.parentType = this.xtype;
777             this.menu.triggerEl = this.el;
778             this.addxtype(Roo.apply({}, this.menu));
779         }
780
781
782        if (this.el.hasClass('roo-button')) {
783             this.el.on('click', this.onClick, this);
784        } else {
785             this.el.select('.roo-button').on('click', this.onClick, this);
786        }
787        
788        if(this.removeClass){
789            this.el.on('click', this.onClick, this);
790        }
791        
792        this.el.enableDisplayMode();
793         
794     },
795     onClick : function(e)
796     {
797         if (this.disabled) {
798             return;
799         }
800         
801         
802         Roo.log('button on click ');
803         if(this.preventDefault){
804             e.preventDefault();
805         }
806         if (this.pressed === true || this.pressed === false) {
807             this.pressed = !this.pressed;
808             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
809             this.fireEvent('toggle', this, e, this.pressed);
810         }
811         
812         
813         this.fireEvent('click', this, e);
814     },
815     
816     /**
817      * Enables this button
818      */
819     enable : function()
820     {
821         this.disabled = false;
822         this.el.removeClass('disabled');
823     },
824     
825     /**
826      * Disable this button
827      */
828     disable : function()
829     {
830         this.disabled = true;
831         this.el.addClass('disabled');
832     },
833      /**
834      * sets the active state on/off, 
835      * @param {Boolean} state (optional) Force a particular state
836      */
837     setActive : function(v) {
838         
839         this.el[v ? 'addClass' : 'removeClass']('active');
840     },
841      /**
842      * toggles the current active state 
843      */
844     toggleActive : function()
845     {
846        var active = this.el.hasClass('active');
847        this.setActive(!active);
848        
849         
850     },
851     setText : function(str)
852     {
853         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
854     },
855     getText : function()
856     {
857         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
858     },
859     hide: function() {
860        
861      
862         this.el.hide();   
863     },
864     show: function() {
865        
866         this.el.show();   
867     },
868     setWeight : function(str)
869     {
870           this.el.removeClass(this.weightClass);
871         this.el.addClass('btn-' + str);        
872     }
873     
874     
875 });
876
877  /*
878  * - LGPL
879  *
880  * column
881  * 
882  */
883
884 /**
885  * @class Roo.bootstrap.Column
886  * @extends Roo.bootstrap.Component
887  * Bootstrap Column class
888  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
889  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
890  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
891  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
892  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
893  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
894  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
895  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
896  *
897  * 
898  * @cfg {Boolean} hidden (true|false) hide the element
899  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
900  * @cfg {String} fa (ban|check|...) font awesome icon
901  * @cfg {Number} fasize (1|2|....) font awsome size
902
903  * @cfg {String} icon (info-sign|check|...) glyphicon name
904
905  * @cfg {String} html content of column.
906  * 
907  * @constructor
908  * Create a new Column
909  * @param {Object} config The config object
910  */
911
912 Roo.bootstrap.Column = function(config){
913     Roo.bootstrap.Column.superclass.constructor.call(this, config);
914 };
915
916 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
917     
918     xs: false,
919     sm: false,
920     md: false,
921     lg: false,
922     xsoff: false,
923     smoff: false,
924     mdoff: false,
925     lgoff: false,
926     html: '',
927     offset: 0,
928     alert: false,
929     fa: false,
930     icon : false,
931     hidden : false,
932     fasize : 1,
933     
934     getAutoCreate : function(){
935         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
936         
937         cfg = {
938             tag: 'div',
939             cls: 'column'
940         };
941         
942         var settings=this;
943         ['xs','sm','md','lg'].map(function(size){
944             //Roo.log( size + ':' + settings[size]);
945             
946             if (settings[size+'off'] !== false) {
947                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
948             }
949             
950             if (settings[size] === false) {
951                 return;
952             }
953             
954             if (!settings[size]) { // 0 = hidden
955                 cfg.cls += ' hidden-' + size;
956                 return;
957             }
958             cfg.cls += ' col-' + size + '-' + settings[size];
959             
960         });
961         
962         if (this.hidden) {
963             cfg.cls += ' hidden';
964         }
965         
966         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
967             cfg.cls +=' alert alert-' + this.alert;
968         }
969         
970         
971         if (this.html.length) {
972             cfg.html = this.html;
973         }
974         if (this.fa) {
975             var fasize = '';
976             if (this.fasize > 1) {
977                 fasize = ' fa-' + this.fasize + 'x';
978             }
979             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
980             
981             
982         }
983         if (this.icon) {
984             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
985         }
986         
987         return cfg;
988     }
989    
990 });
991
992  
993
994  /*
995  * - LGPL
996  *
997  * page container.
998  * 
999  */
1000
1001
1002 /**
1003  * @class Roo.bootstrap.Container
1004  * @extends Roo.bootstrap.Component
1005  * Bootstrap Container class
1006  * @cfg {Boolean} jumbotron is it a jumbotron element
1007  * @cfg {String} html content of element
1008  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1009  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
1010  * @cfg {String} header content of header (for panel)
1011  * @cfg {String} footer content of footer (for panel)
1012  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1013  * @cfg {String} tag (header|aside|section) type of HTML tag.
1014  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1015  * @cfg {String} fa font awesome icon
1016  * @cfg {String} icon (info-sign|check|...) glyphicon name
1017  * @cfg {Boolean} hidden (true|false) hide the element
1018  * @cfg {Boolean} expandable (true|false) default false
1019  * @cfg {Boolean} expanded (true|false) default true
1020  * @cfg {String} rheader contet on the right of header
1021  * @cfg {Boolean} clickable (true|false) default false
1022
1023  *     
1024  * @constructor
1025  * Create a new Container
1026  * @param {Object} config The config object
1027  */
1028
1029 Roo.bootstrap.Container = function(config){
1030     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1031     
1032     this.addEvents({
1033         // raw events
1034          /**
1035          * @event expand
1036          * After the panel has been expand
1037          * 
1038          * @param {Roo.bootstrap.Container} this
1039          */
1040         "expand" : true,
1041         /**
1042          * @event collapse
1043          * After the panel has been collapsed
1044          * 
1045          * @param {Roo.bootstrap.Container} this
1046          */
1047         "collapse" : true,
1048         /**
1049          * @event click
1050          * When a element is chick
1051          * @param {Roo.bootstrap.Container} this
1052          * @param {Roo.EventObject} e
1053          */
1054         "click" : true
1055     });
1056 };
1057
1058 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1059     
1060     jumbotron : false,
1061     well: '',
1062     panel : '',
1063     header: '',
1064     footer : '',
1065     sticky: '',
1066     tag : false,
1067     alert : false,
1068     fa: false,
1069     icon : false,
1070     expandable : false,
1071     rheader : '',
1072     expanded : true,
1073     clickable: false,
1074   
1075      
1076     getChildContainer : function() {
1077         
1078         if(!this.el){
1079             return false;
1080         }
1081         
1082         if (this.panel.length) {
1083             return this.el.select('.panel-body',true).first();
1084         }
1085         
1086         return this.el;
1087     },
1088     
1089     
1090     getAutoCreate : function(){
1091         
1092         var cfg = {
1093             tag : this.tag || 'div',
1094             html : '',
1095             cls : ''
1096         };
1097         if (this.jumbotron) {
1098             cfg.cls = 'jumbotron';
1099         }
1100         
1101         
1102         
1103         // - this is applied by the parent..
1104         //if (this.cls) {
1105         //    cfg.cls = this.cls + '';
1106         //}
1107         
1108         if (this.sticky.length) {
1109             
1110             var bd = Roo.get(document.body);
1111             if (!bd.hasClass('bootstrap-sticky')) {
1112                 bd.addClass('bootstrap-sticky');
1113                 Roo.select('html',true).setStyle('height', '100%');
1114             }
1115              
1116             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1117         }
1118         
1119         
1120         if (this.well.length) {
1121             switch (this.well) {
1122                 case 'lg':
1123                 case 'sm':
1124                     cfg.cls +=' well well-' +this.well;
1125                     break;
1126                 default:
1127                     cfg.cls +=' well';
1128                     break;
1129             }
1130         }
1131         
1132         if (this.hidden) {
1133             cfg.cls += ' hidden';
1134         }
1135         
1136         
1137         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1138             cfg.cls +=' alert alert-' + this.alert;
1139         }
1140         
1141         var body = cfg;
1142         
1143         if (this.panel.length) {
1144             cfg.cls += ' panel panel-' + this.panel;
1145             cfg.cn = [];
1146             if (this.header.length) {
1147                 
1148                 var h = [];
1149                 
1150                 if(this.expandable){
1151                     
1152                     cfg.cls = cfg.cls + ' expandable';
1153                     
1154                     h.push({
1155                         tag: 'i',
1156                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1157                     });
1158                     
1159                 }
1160                 
1161                 h.push(
1162                     {
1163                         tag: 'span',
1164                         cls : 'panel-title',
1165                         html : (this.expandable ? '&nbsp;' : '') + this.header
1166                     },
1167                     {
1168                         tag: 'span',
1169                         cls: 'panel-header-right',
1170                         html: this.rheader
1171                     }
1172                 );
1173                 
1174                 cfg.cn.push({
1175                     cls : 'panel-heading',
1176                     style : this.expandable ? 'cursor: pointer' : '',
1177                     cn : h
1178                 });
1179                 
1180             }
1181             
1182             body = false;
1183             cfg.cn.push({
1184                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1185                 html : this.html
1186             });
1187             
1188             
1189             if (this.footer.length) {
1190                 cfg.cn.push({
1191                     cls : 'panel-footer',
1192                     html : this.footer
1193                     
1194                 });
1195             }
1196             
1197         }
1198         
1199         if (body) {
1200             body.html = this.html || cfg.html;
1201             // prefix with the icons..
1202             if (this.fa) {
1203                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1204             }
1205             if (this.icon) {
1206                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1207             }
1208             
1209             
1210         }
1211         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1212             cfg.cls =  'container';
1213         }
1214         
1215         return cfg;
1216     },
1217     
1218     initEvents: function() 
1219     {
1220         if(this.expandable){
1221             var headerEl = this.headerEl();
1222         
1223             if(headerEl){
1224                 headerEl.on('click', this.onToggleClick, this);
1225             }
1226         }
1227         
1228         if(this.clickable){
1229             this.el.on('click', this.onClick, this);
1230         }
1231         
1232     },
1233     
1234     onToggleClick : function()
1235     {
1236         var headerEl = this.headerEl();
1237         
1238         if(!headerEl){
1239             return;
1240         }
1241         
1242         if(this.expanded){
1243             this.collapse();
1244             return;
1245         }
1246         
1247         this.expand();
1248     },
1249     
1250     expand : function()
1251     {
1252         if(this.fireEvent('expand', this)) {
1253             
1254             this.expanded = true;
1255             
1256             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1257             
1258             this.el.select('.panel-body',true).first().removeClass('hide');
1259             
1260             var toggleEl = this.toggleEl();
1261
1262             if(!toggleEl){
1263                 return;
1264             }
1265
1266             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1267         }
1268         
1269     },
1270     
1271     collapse : function()
1272     {
1273         if(this.fireEvent('collapse', this)) {
1274             
1275             this.expanded = false;
1276             
1277             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1278             this.el.select('.panel-body',true).first().addClass('hide');
1279         
1280             var toggleEl = this.toggleEl();
1281
1282             if(!toggleEl){
1283                 return;
1284             }
1285
1286             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1287         }
1288     },
1289     
1290     toggleEl : function()
1291     {
1292         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1293             return;
1294         }
1295         
1296         return this.el.select('.panel-heading .fa',true).first();
1297     },
1298     
1299     headerEl : function()
1300     {
1301         if(!this.el || !this.panel.length || !this.header.length){
1302             return;
1303         }
1304         
1305         return this.el.select('.panel-heading',true).first()
1306     },
1307     
1308     bodyEl : function()
1309     {
1310         if(!this.el || !this.panel.length){
1311             return;
1312         }
1313         
1314         return this.el.select('.panel-body',true).first()
1315     },
1316     
1317     titleEl : function()
1318     {
1319         if(!this.el || !this.panel.length || !this.header.length){
1320             return;
1321         }
1322         
1323         return this.el.select('.panel-title',true).first();
1324     },
1325     
1326     setTitle : function(v)
1327     {
1328         var titleEl = this.titleEl();
1329         
1330         if(!titleEl){
1331             return;
1332         }
1333         
1334         titleEl.dom.innerHTML = v;
1335     },
1336     
1337     getTitle : function()
1338     {
1339         
1340         var titleEl = this.titleEl();
1341         
1342         if(!titleEl){
1343             return '';
1344         }
1345         
1346         return titleEl.dom.innerHTML;
1347     },
1348     
1349     setRightTitle : function(v)
1350     {
1351         var t = this.el.select('.panel-header-right',true).first();
1352         
1353         if(!t){
1354             return;
1355         }
1356         
1357         t.dom.innerHTML = v;
1358     },
1359     
1360     onClick : function(e)
1361     {
1362         e.preventDefault();
1363         
1364         this.fireEvent('click', this, e);
1365     }
1366    
1367 });
1368
1369  /*
1370  * - LGPL
1371  *
1372  * image
1373  * 
1374  */
1375
1376
1377 /**
1378  * @class Roo.bootstrap.Img
1379  * @extends Roo.bootstrap.Component
1380  * Bootstrap Img class
1381  * @cfg {Boolean} imgResponsive false | true
1382  * @cfg {String} border rounded | circle | thumbnail
1383  * @cfg {String} src image source
1384  * @cfg {String} alt image alternative text
1385  * @cfg {String} href a tag href
1386  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1387  * @cfg {String} xsUrl xs image source
1388  * @cfg {String} smUrl sm image source
1389  * @cfg {String} mdUrl md image source
1390  * @cfg {String} lgUrl lg image source
1391  * 
1392  * @constructor
1393  * Create a new Input
1394  * @param {Object} config The config object
1395  */
1396
1397 Roo.bootstrap.Img = function(config){
1398     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1399     
1400     this.addEvents({
1401         // img events
1402         /**
1403          * @event click
1404          * The img click event for the img.
1405          * @param {Roo.EventObject} e
1406          */
1407         "click" : true
1408     });
1409 };
1410
1411 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1412     
1413     imgResponsive: true,
1414     border: '',
1415     src: 'about:blank',
1416     href: false,
1417     target: false,
1418     xsUrl: '',
1419     smUrl: '',
1420     mdUrl: '',
1421     lgUrl: '',
1422
1423     getAutoCreate : function()
1424     {   
1425         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1426             return this.createSingleImg();
1427         }
1428         
1429         var cfg = {
1430             tag: 'div',
1431             cls: 'roo-image-responsive-group',
1432             cn: []
1433         };
1434         var _this = this;
1435         
1436         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1437             
1438             if(!_this[size + 'Url']){
1439                 return;
1440             }
1441             
1442             var img = {
1443                 tag: 'img',
1444                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1445                 html: _this.html || cfg.html,
1446                 src: _this[size + 'Url']
1447             };
1448             
1449             img.cls += ' roo-image-responsive-' + size;
1450             
1451             var s = ['xs', 'sm', 'md', 'lg'];
1452             
1453             s.splice(s.indexOf(size), 1);
1454             
1455             Roo.each(s, function(ss){
1456                 img.cls += ' hidden-' + ss;
1457             });
1458             
1459             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1460                 cfg.cls += ' img-' + _this.border;
1461             }
1462             
1463             if(_this.alt){
1464                 cfg.alt = _this.alt;
1465             }
1466             
1467             if(_this.href){
1468                 var a = {
1469                     tag: 'a',
1470                     href: _this.href,
1471                     cn: [
1472                         img
1473                     ]
1474                 };
1475
1476                 if(this.target){
1477                     a.target = _this.target;
1478                 }
1479             }
1480             
1481             cfg.cn.push((_this.href) ? a : img);
1482             
1483         });
1484         
1485         return cfg;
1486     },
1487     
1488     createSingleImg : function()
1489     {
1490         var cfg = {
1491             tag: 'img',
1492             cls: (this.imgResponsive) ? 'img-responsive' : '',
1493             html : null,
1494             src : 'about:blank'  // just incase src get's set to undefined?!?
1495         };
1496         
1497         cfg.html = this.html || cfg.html;
1498         
1499         cfg.src = this.src || cfg.src;
1500         
1501         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1502             cfg.cls += ' img-' + this.border;
1503         }
1504         
1505         if(this.alt){
1506             cfg.alt = this.alt;
1507         }
1508         
1509         if(this.href){
1510             var a = {
1511                 tag: 'a',
1512                 href: this.href,
1513                 cn: [
1514                     cfg
1515                 ]
1516             };
1517             
1518             if(this.target){
1519                 a.target = this.target;
1520             }
1521             
1522         }
1523         
1524         return (this.href) ? a : cfg;
1525     },
1526     
1527     initEvents: function() 
1528     {
1529         if(!this.href){
1530             this.el.on('click', this.onClick, this);
1531         }
1532         
1533     },
1534     
1535     onClick : function(e)
1536     {
1537         Roo.log('img onclick');
1538         this.fireEvent('click', this, e);
1539     },
1540     /**
1541      * Sets the url of the image - used to update it
1542      * @param {String} url the url of the image
1543      */
1544     
1545     setSrc : function(url)
1546     {
1547         this.src =  url;
1548         
1549         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1550             this.el.dom.src =  url;
1551             return;
1552         }
1553         
1554         this.el.select('img', true).first().dom.src =  url;
1555     }
1556     
1557     
1558    
1559 });
1560
1561  /*
1562  * - LGPL
1563  *
1564  * image
1565  * 
1566  */
1567
1568
1569 /**
1570  * @class Roo.bootstrap.Link
1571  * @extends Roo.bootstrap.Component
1572  * Bootstrap Link Class
1573  * @cfg {String} alt image alternative text
1574  * @cfg {String} href a tag href
1575  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1576  * @cfg {String} html the content of the link.
1577  * @cfg {String} anchor name for the anchor link
1578  * @cfg {String} fa - favicon
1579
1580  * @cfg {Boolean} preventDefault (true | false) default false
1581
1582  * 
1583  * @constructor
1584  * Create a new Input
1585  * @param {Object} config The config object
1586  */
1587
1588 Roo.bootstrap.Link = function(config){
1589     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1590     
1591     this.addEvents({
1592         // img events
1593         /**
1594          * @event click
1595          * The img click event for the img.
1596          * @param {Roo.EventObject} e
1597          */
1598         "click" : true
1599     });
1600 };
1601
1602 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1603     
1604     href: false,
1605     target: false,
1606     preventDefault: false,
1607     anchor : false,
1608     alt : false,
1609     fa: false,
1610
1611
1612     getAutoCreate : function()
1613     {
1614         var html = this.html || '';
1615         
1616         if (this.fa !== false) {
1617             html = '<i class="fa fa-' + this.fa + '"></i>';
1618         }
1619         var cfg = {
1620             tag: 'a'
1621         };
1622         // anchor's do not require html/href...
1623         if (this.anchor === false) {
1624             cfg.html = html;
1625             cfg.href = this.href || '#';
1626         } else {
1627             cfg.name = this.anchor;
1628             if (this.html !== false || this.fa !== false) {
1629                 cfg.html = html;
1630             }
1631             if (this.href !== false) {
1632                 cfg.href = this.href;
1633             }
1634         }
1635         
1636         if(this.alt !== false){
1637             cfg.alt = this.alt;
1638         }
1639         
1640         
1641         if(this.target !== false) {
1642             cfg.target = this.target;
1643         }
1644         
1645         return cfg;
1646     },
1647     
1648     initEvents: function() {
1649         
1650         if(!this.href || this.preventDefault){
1651             this.el.on('click', this.onClick, this);
1652         }
1653     },
1654     
1655     onClick : function(e)
1656     {
1657         if(this.preventDefault){
1658             e.preventDefault();
1659         }
1660         //Roo.log('img onclick');
1661         this.fireEvent('click', this, e);
1662     }
1663    
1664 });
1665
1666  /*
1667  * - LGPL
1668  *
1669  * header
1670  * 
1671  */
1672
1673 /**
1674  * @class Roo.bootstrap.Header
1675  * @extends Roo.bootstrap.Component
1676  * Bootstrap Header class
1677  * @cfg {String} html content of header
1678  * @cfg {Number} level (1|2|3|4|5|6) default 1
1679  * 
1680  * @constructor
1681  * Create a new Header
1682  * @param {Object} config The config object
1683  */
1684
1685
1686 Roo.bootstrap.Header  = function(config){
1687     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1688 };
1689
1690 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1691     
1692     //href : false,
1693     html : false,
1694     level : 1,
1695     
1696     
1697     
1698     getAutoCreate : function(){
1699         
1700         
1701         
1702         var cfg = {
1703             tag: 'h' + (1 *this.level),
1704             html: this.html || ''
1705         } ;
1706         
1707         return cfg;
1708     }
1709    
1710 });
1711
1712  
1713
1714  /*
1715  * Based on:
1716  * Ext JS Library 1.1.1
1717  * Copyright(c) 2006-2007, Ext JS, LLC.
1718  *
1719  * Originally Released Under LGPL - original licence link has changed is not relivant.
1720  *
1721  * Fork - LGPL
1722  * <script type="text/javascript">
1723  */
1724  
1725 /**
1726  * @class Roo.bootstrap.MenuMgr
1727  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1728  * @singleton
1729  */
1730 Roo.bootstrap.MenuMgr = function(){
1731    var menus, active, groups = {}, attached = false, lastShow = new Date();
1732
1733    // private - called when first menu is created
1734    function init(){
1735        menus = {};
1736        active = new Roo.util.MixedCollection();
1737        Roo.get(document).addKeyListener(27, function(){
1738            if(active.length > 0){
1739                hideAll();
1740            }
1741        });
1742    }
1743
1744    // private
1745    function hideAll(){
1746        if(active && active.length > 0){
1747            var c = active.clone();
1748            c.each(function(m){
1749                m.hide();
1750            });
1751        }
1752    }
1753
1754    // private
1755    function onHide(m){
1756        active.remove(m);
1757        if(active.length < 1){
1758            Roo.get(document).un("mouseup", onMouseDown);
1759             
1760            attached = false;
1761        }
1762    }
1763
1764    // private
1765    function onShow(m){
1766        var last = active.last();
1767        lastShow = new Date();
1768        active.add(m);
1769        if(!attached){
1770           Roo.get(document).on("mouseup", onMouseDown);
1771            
1772            attached = true;
1773        }
1774        if(m.parentMenu){
1775           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1776           m.parentMenu.activeChild = m;
1777        }else if(last && last.isVisible()){
1778           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1779        }
1780    }
1781
1782    // private
1783    function onBeforeHide(m){
1784        if(m.activeChild){
1785            m.activeChild.hide();
1786        }
1787        if(m.autoHideTimer){
1788            clearTimeout(m.autoHideTimer);
1789            delete m.autoHideTimer;
1790        }
1791    }
1792
1793    // private
1794    function onBeforeShow(m){
1795        var pm = m.parentMenu;
1796        if(!pm && !m.allowOtherMenus){
1797            hideAll();
1798        }else if(pm && pm.activeChild && active != m){
1799            pm.activeChild.hide();
1800        }
1801    }
1802
1803    // private this should really trigger on mouseup..
1804    function onMouseDown(e){
1805         Roo.log("on Mouse Up");
1806         
1807         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1808             Roo.log("MenuManager hideAll");
1809             hideAll();
1810             e.stopEvent();
1811         }
1812         
1813         
1814    }
1815
1816    // private
1817    function onBeforeCheck(mi, state){
1818        if(state){
1819            var g = groups[mi.group];
1820            for(var i = 0, l = g.length; i < l; i++){
1821                if(g[i] != mi){
1822                    g[i].setChecked(false);
1823                }
1824            }
1825        }
1826    }
1827
1828    return {
1829
1830        /**
1831         * Hides all menus that are currently visible
1832         */
1833        hideAll : function(){
1834             hideAll();  
1835        },
1836
1837        // private
1838        register : function(menu){
1839            if(!menus){
1840                init();
1841            }
1842            menus[menu.id] = menu;
1843            menu.on("beforehide", onBeforeHide);
1844            menu.on("hide", onHide);
1845            menu.on("beforeshow", onBeforeShow);
1846            menu.on("show", onShow);
1847            var g = menu.group;
1848            if(g && menu.events["checkchange"]){
1849                if(!groups[g]){
1850                    groups[g] = [];
1851                }
1852                groups[g].push(menu);
1853                menu.on("checkchange", onCheck);
1854            }
1855        },
1856
1857         /**
1858          * Returns a {@link Roo.menu.Menu} object
1859          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1860          * be used to generate and return a new Menu instance.
1861          */
1862        get : function(menu){
1863            if(typeof menu == "string"){ // menu id
1864                return menus[menu];
1865            }else if(menu.events){  // menu instance
1866                return menu;
1867            }
1868            /*else if(typeof menu.length == 'number'){ // array of menu items?
1869                return new Roo.bootstrap.Menu({items:menu});
1870            }else{ // otherwise, must be a config
1871                return new Roo.bootstrap.Menu(menu);
1872            }
1873            */
1874            return false;
1875        },
1876
1877        // private
1878        unregister : function(menu){
1879            delete menus[menu.id];
1880            menu.un("beforehide", onBeforeHide);
1881            menu.un("hide", onHide);
1882            menu.un("beforeshow", onBeforeShow);
1883            menu.un("show", onShow);
1884            var g = menu.group;
1885            if(g && menu.events["checkchange"]){
1886                groups[g].remove(menu);
1887                menu.un("checkchange", onCheck);
1888            }
1889        },
1890
1891        // private
1892        registerCheckable : function(menuItem){
1893            var g = menuItem.group;
1894            if(g){
1895                if(!groups[g]){
1896                    groups[g] = [];
1897                }
1898                groups[g].push(menuItem);
1899                menuItem.on("beforecheckchange", onBeforeCheck);
1900            }
1901        },
1902
1903        // private
1904        unregisterCheckable : function(menuItem){
1905            var g = menuItem.group;
1906            if(g){
1907                groups[g].remove(menuItem);
1908                menuItem.un("beforecheckchange", onBeforeCheck);
1909            }
1910        }
1911    };
1912 }();/*
1913  * - LGPL
1914  *
1915  * menu
1916  * 
1917  */
1918
1919 /**
1920  * @class Roo.bootstrap.Menu
1921  * @extends Roo.bootstrap.Component
1922  * Bootstrap Menu class - container for MenuItems
1923  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1924  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1925  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1926  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1927  * 
1928  * @constructor
1929  * Create a new Menu
1930  * @param {Object} config The config object
1931  */
1932
1933
1934 Roo.bootstrap.Menu = function(config){
1935     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1936     if (this.registerMenu && this.type != 'treeview')  {
1937         Roo.bootstrap.MenuMgr.register(this);
1938     }
1939     this.addEvents({
1940         /**
1941          * @event beforeshow
1942          * Fires before this menu is displayed
1943          * @param {Roo.menu.Menu} this
1944          */
1945         beforeshow : true,
1946         /**
1947          * @event beforehide
1948          * Fires before this menu is hidden
1949          * @param {Roo.menu.Menu} this
1950          */
1951         beforehide : true,
1952         /**
1953          * @event show
1954          * Fires after this menu is displayed
1955          * @param {Roo.menu.Menu} this
1956          */
1957         show : true,
1958         /**
1959          * @event hide
1960          * Fires after this menu is hidden
1961          * @param {Roo.menu.Menu} this
1962          */
1963         hide : true,
1964         /**
1965          * @event click
1966          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1967          * @param {Roo.menu.Menu} this
1968          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1969          * @param {Roo.EventObject} e
1970          */
1971         click : true,
1972         /**
1973          * @event mouseover
1974          * Fires when the mouse is hovering over this menu
1975          * @param {Roo.menu.Menu} this
1976          * @param {Roo.EventObject} e
1977          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1978          */
1979         mouseover : true,
1980         /**
1981          * @event mouseout
1982          * Fires when the mouse exits this menu
1983          * @param {Roo.menu.Menu} this
1984          * @param {Roo.EventObject} e
1985          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1986          */
1987         mouseout : true,
1988         /**
1989          * @event itemclick
1990          * Fires when a menu item contained in this menu is clicked
1991          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1992          * @param {Roo.EventObject} e
1993          */
1994         itemclick: true
1995     });
1996     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1997 };
1998
1999 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2000     
2001    /// html : false,
2002     //align : '',
2003     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2004     type: false,
2005     /**
2006      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2007      */
2008     registerMenu : true,
2009     
2010     menuItems :false, // stores the menu items..
2011     
2012     hidden:true,
2013         
2014     parentMenu : false,
2015     
2016     stopEvent : true,
2017     
2018     isLink : false,
2019     
2020     getChildContainer : function() {
2021         return this.el;  
2022     },
2023     
2024     getAutoCreate : function(){
2025          
2026         //if (['right'].indexOf(this.align)!==-1) {
2027         //    cfg.cn[1].cls += ' pull-right'
2028         //}
2029         
2030         
2031         var cfg = {
2032             tag : 'ul',
2033             cls : 'dropdown-menu' ,
2034             style : 'z-index:1000'
2035             
2036         };
2037         
2038         if (this.type === 'submenu') {
2039             cfg.cls = 'submenu active';
2040         }
2041         if (this.type === 'treeview') {
2042             cfg.cls = 'treeview-menu';
2043         }
2044         
2045         return cfg;
2046     },
2047     initEvents : function() {
2048         
2049        // Roo.log("ADD event");
2050        // Roo.log(this.triggerEl.dom);
2051         
2052         this.triggerEl.on('click', this.onTriggerClick, this);
2053         
2054         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2055         
2056         this.triggerEl.addClass('dropdown-toggle');
2057         
2058         if (Roo.isTouch) {
2059             this.el.on('touchstart'  , this.onTouch, this);
2060         }
2061         this.el.on('click' , this.onClick, this);
2062
2063         this.el.on("mouseover", this.onMouseOver, this);
2064         this.el.on("mouseout", this.onMouseOut, this);
2065         
2066     },
2067     
2068     findTargetItem : function(e)
2069     {
2070         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2071         if(!t){
2072             return false;
2073         }
2074         //Roo.log(t);         Roo.log(t.id);
2075         if(t && t.id){
2076             //Roo.log(this.menuitems);
2077             return this.menuitems.get(t.id);
2078             
2079             //return this.items.get(t.menuItemId);
2080         }
2081         
2082         return false;
2083     },
2084     
2085     onTouch : function(e) 
2086     {
2087         Roo.log("menu.onTouch");
2088         //e.stopEvent(); this make the user popdown broken
2089         this.onClick(e);
2090     },
2091     
2092     onClick : function(e)
2093     {
2094         Roo.log("menu.onClick");
2095         
2096         var t = this.findTargetItem(e);
2097         if(!t || t.isContainer){
2098             return;
2099         }
2100         Roo.log(e);
2101         /*
2102         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2103             if(t == this.activeItem && t.shouldDeactivate(e)){
2104                 this.activeItem.deactivate();
2105                 delete this.activeItem;
2106                 return;
2107             }
2108             if(t.canActivate){
2109                 this.setActiveItem(t, true);
2110             }
2111             return;
2112             
2113             
2114         }
2115         */
2116        
2117         Roo.log('pass click event');
2118         
2119         t.onClick(e);
2120         
2121         this.fireEvent("click", this, t, e);
2122         
2123         var _this = this;
2124         
2125         if(!t.href.length || t.href == '#'){
2126             (function() { _this.hide(); }).defer(100);
2127         }
2128         
2129     },
2130     
2131     onMouseOver : function(e){
2132         var t  = this.findTargetItem(e);
2133         //Roo.log(t);
2134         //if(t){
2135         //    if(t.canActivate && !t.disabled){
2136         //        this.setActiveItem(t, true);
2137         //    }
2138         //}
2139         
2140         this.fireEvent("mouseover", this, e, t);
2141     },
2142     isVisible : function(){
2143         return !this.hidden;
2144     },
2145      onMouseOut : function(e){
2146         var t  = this.findTargetItem(e);
2147         
2148         //if(t ){
2149         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2150         //        this.activeItem.deactivate();
2151         //        delete this.activeItem;
2152         //    }
2153         //}
2154         this.fireEvent("mouseout", this, e, t);
2155     },
2156     
2157     
2158     /**
2159      * Displays this menu relative to another element
2160      * @param {String/HTMLElement/Roo.Element} element The element to align to
2161      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2162      * the element (defaults to this.defaultAlign)
2163      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2164      */
2165     show : function(el, pos, parentMenu){
2166         this.parentMenu = parentMenu;
2167         if(!this.el){
2168             this.render();
2169         }
2170         this.fireEvent("beforeshow", this);
2171         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2172     },
2173      /**
2174      * Displays this menu at a specific xy position
2175      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2176      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2177      */
2178     showAt : function(xy, parentMenu, /* private: */_e){
2179         this.parentMenu = parentMenu;
2180         if(!this.el){
2181             this.render();
2182         }
2183         if(_e !== false){
2184             this.fireEvent("beforeshow", this);
2185             //xy = this.el.adjustForConstraints(xy);
2186         }
2187         
2188         //this.el.show();
2189         this.hideMenuItems();
2190         this.hidden = false;
2191         this.triggerEl.addClass('open');
2192         
2193         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2194             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2195         }
2196         
2197         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2198             this.el.setXY(xy);
2199         }
2200         
2201         this.focus();
2202         this.fireEvent("show", this);
2203     },
2204     
2205     focus : function(){
2206         return;
2207         if(!this.hidden){
2208             this.doFocus.defer(50, this);
2209         }
2210     },
2211
2212     doFocus : function(){
2213         if(!this.hidden){
2214             this.focusEl.focus();
2215         }
2216     },
2217
2218     /**
2219      * Hides this menu and optionally all parent menus
2220      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2221      */
2222     hide : function(deep)
2223     {
2224         
2225         this.hideMenuItems();
2226         if(this.el && this.isVisible()){
2227             this.fireEvent("beforehide", this);
2228             if(this.activeItem){
2229                 this.activeItem.deactivate();
2230                 this.activeItem = null;
2231             }
2232             this.triggerEl.removeClass('open');;
2233             this.hidden = true;
2234             this.fireEvent("hide", this);
2235         }
2236         if(deep === true && this.parentMenu){
2237             this.parentMenu.hide(true);
2238         }
2239     },
2240     
2241     onTriggerClick : function(e)
2242     {
2243         Roo.log('trigger click');
2244         
2245         var target = e.getTarget();
2246         
2247         Roo.log(target.nodeName.toLowerCase());
2248         
2249         if(target.nodeName.toLowerCase() === 'i'){
2250             e.preventDefault();
2251         }
2252         
2253     },
2254     
2255     onTriggerPress  : function(e)
2256     {
2257         Roo.log('trigger press');
2258         //Roo.log(e.getTarget());
2259        // Roo.log(this.triggerEl.dom);
2260        
2261         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2262         var pel = Roo.get(e.getTarget());
2263         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2264             Roo.log('is treeview or dropdown?');
2265             return;
2266         }
2267         
2268         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2269             return;
2270         }
2271         
2272         if (this.isVisible()) {
2273             Roo.log('hide');
2274             this.hide();
2275         } else {
2276             Roo.log('show');
2277             this.show(this.triggerEl, false, false);
2278         }
2279         
2280         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2281             e.stopEvent();
2282         }
2283         
2284     },
2285        
2286     
2287     hideMenuItems : function()
2288     {
2289         Roo.log("hide Menu Items");
2290         if (!this.el) { 
2291             return;
2292         }
2293         //$(backdrop).remove()
2294         this.el.select('.open',true).each(function(aa) {
2295             
2296             aa.removeClass('open');
2297           //var parent = getParent($(this))
2298           //var relatedTarget = { relatedTarget: this }
2299           
2300            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2301           //if (e.isDefaultPrevented()) return
2302            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2303         });
2304     },
2305     addxtypeChild : function (tree, cntr) {
2306         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2307           
2308         this.menuitems.add(comp);
2309         return comp;
2310
2311     },
2312     getEl : function()
2313     {
2314         Roo.log(this.el);
2315         return this.el;
2316     }
2317 });
2318
2319  
2320  /*
2321  * - LGPL
2322  *
2323  * menu item
2324  * 
2325  */
2326
2327
2328 /**
2329  * @class Roo.bootstrap.MenuItem
2330  * @extends Roo.bootstrap.Component
2331  * Bootstrap MenuItem class
2332  * @cfg {String} html the menu label
2333  * @cfg {String} href the link
2334  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2335  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2336  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2337  * @cfg {String} fa favicon to show on left of menu item.
2338  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2339  * 
2340  * 
2341  * @constructor
2342  * Create a new MenuItem
2343  * @param {Object} config The config object
2344  */
2345
2346
2347 Roo.bootstrap.MenuItem = function(config){
2348     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2349     this.addEvents({
2350         // raw events
2351         /**
2352          * @event click
2353          * The raw click event for the entire grid.
2354          * @param {Roo.bootstrap.MenuItem} this
2355          * @param {Roo.EventObject} e
2356          */
2357         "click" : true
2358     });
2359 };
2360
2361 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2362     
2363     href : false,
2364     html : false,
2365     preventDefault: false,
2366     isContainer : false,
2367     active : false,
2368     fa: false,
2369     
2370     getAutoCreate : function(){
2371         
2372         if(this.isContainer){
2373             return {
2374                 tag: 'li',
2375                 cls: 'dropdown-menu-item'
2376             };
2377         }
2378         var ctag = {
2379             tag: 'span',
2380             html: 'Link'
2381         };
2382         
2383         var anc = {
2384             tag : 'a',
2385             href : '#',
2386             cn : [  ]
2387         };
2388         
2389         if (this.fa !== false) {
2390             anc.cn.push({
2391                 tag : 'i',
2392                 cls : 'fa fa-' + this.fa
2393             });
2394         }
2395         
2396         anc.cn.push(ctag);
2397         
2398         
2399         var cfg= {
2400             tag: 'li',
2401             cls: 'dropdown-menu-item',
2402             cn: [ anc ]
2403         };
2404         if (this.parent().type == 'treeview') {
2405             cfg.cls = 'treeview-menu';
2406         }
2407         if (this.active) {
2408             cfg.cls += ' active';
2409         }
2410         
2411         
2412         
2413         anc.href = this.href || cfg.cn[0].href ;
2414         ctag.html = this.html || cfg.cn[0].html ;
2415         return cfg;
2416     },
2417     
2418     initEvents: function()
2419     {
2420         if (this.parent().type == 'treeview') {
2421             this.el.select('a').on('click', this.onClick, this);
2422         }
2423         
2424         if (this.menu) {
2425             this.menu.parentType = this.xtype;
2426             this.menu.triggerEl = this.el;
2427             this.menu = this.addxtype(Roo.apply({}, this.menu));
2428         }
2429         
2430     },
2431     onClick : function(e)
2432     {
2433         Roo.log('item on click ');
2434         
2435         if(this.preventDefault){
2436             e.preventDefault();
2437         }
2438         //this.parent().hideMenuItems();
2439         
2440         this.fireEvent('click', this, e);
2441     },
2442     getEl : function()
2443     {
2444         return this.el;
2445     } 
2446 });
2447
2448  
2449
2450  /*
2451  * - LGPL
2452  *
2453  * menu separator
2454  * 
2455  */
2456
2457
2458 /**
2459  * @class Roo.bootstrap.MenuSeparator
2460  * @extends Roo.bootstrap.Component
2461  * Bootstrap MenuSeparator class
2462  * 
2463  * @constructor
2464  * Create a new MenuItem
2465  * @param {Object} config The config object
2466  */
2467
2468
2469 Roo.bootstrap.MenuSeparator = function(config){
2470     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2471 };
2472
2473 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2474     
2475     getAutoCreate : function(){
2476         var cfg = {
2477             cls: 'divider',
2478             tag : 'li'
2479         };
2480         
2481         return cfg;
2482     }
2483    
2484 });
2485
2486  
2487
2488  
2489 /*
2490 * Licence: LGPL
2491 */
2492
2493 /**
2494  * @class Roo.bootstrap.Modal
2495  * @extends Roo.bootstrap.Component
2496  * Bootstrap Modal class
2497  * @cfg {String} title Title of dialog
2498  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2499  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2500  * @cfg {Boolean} specificTitle default false
2501  * @cfg {Array} buttons Array of buttons or standard button set..
2502  * @cfg {String} buttonPosition (left|right|center) default right
2503  * @cfg {Boolean} animate default true
2504  * @cfg {Boolean} allow_close default true
2505  * @cfg {Boolean} fitwindow default false
2506  * @cfg {String} size (sm|lg) default empty
2507  *
2508  *
2509  * @constructor
2510  * Create a new Modal Dialog
2511  * @param {Object} config The config object
2512  */
2513
2514 Roo.bootstrap.Modal = function(config){
2515     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2516     this.addEvents({
2517         // raw events
2518         /**
2519          * @event btnclick
2520          * The raw btnclick event for the button
2521          * @param {Roo.EventObject} e
2522          */
2523         "btnclick" : true,
2524         /**
2525          * @event resize
2526          * Fire when dialog resize
2527          * @param {Roo.bootstrap.Modal} this
2528          * @param {Roo.EventObject} e
2529          */
2530         "resize" : true
2531     });
2532     this.buttons = this.buttons || [];
2533
2534     if (this.tmpl) {
2535         this.tmpl = Roo.factory(this.tmpl);
2536     }
2537
2538 };
2539
2540 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2541
2542     title : 'test dialog',
2543
2544     buttons : false,
2545
2546     // set on load...
2547
2548     html: false,
2549
2550     tmp: false,
2551
2552     specificTitle: false,
2553
2554     buttonPosition: 'right',
2555
2556     allow_close : true,
2557
2558     animate : true,
2559
2560     fitwindow: false,
2561
2562
2563      // private
2564     dialogEl: false,
2565     bodyEl:  false,
2566     footerEl:  false,
2567     titleEl:  false,
2568     closeEl:  false,
2569
2570     size: '',
2571
2572
2573     onRender : function(ct, position)
2574     {
2575         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2576
2577         if(!this.el){
2578             var cfg = Roo.apply({},  this.getAutoCreate());
2579             cfg.id = Roo.id();
2580             //if(!cfg.name){
2581             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2582             //}
2583             //if (!cfg.name.length) {
2584             //    delete cfg.name;
2585            // }
2586             if (this.cls) {
2587                 cfg.cls += ' ' + this.cls;
2588             }
2589             if (this.style) {
2590                 cfg.style = this.style;
2591             }
2592             this.el = Roo.get(document.body).createChild(cfg, position);
2593         }
2594         //var type = this.el.dom.type;
2595
2596
2597         if(this.tabIndex !== undefined){
2598             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2599         }
2600
2601         this.dialogEl = this.el.select('.modal-dialog',true).first();
2602         this.bodyEl = this.el.select('.modal-body',true).first();
2603         this.closeEl = this.el.select('.modal-header .close', true).first();
2604         this.headerEl = this.el.select('.modal-header',true).first();
2605         this.titleEl = this.el.select('.modal-title',true).first();
2606         this.footerEl = this.el.select('.modal-footer',true).first();
2607
2608         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2609         this.maskEl.enableDisplayMode("block");
2610         this.maskEl.hide();
2611         //this.el.addClass("x-dlg-modal");
2612
2613         if (this.buttons.length) {
2614             Roo.each(this.buttons, function(bb) {
2615                 var b = Roo.apply({}, bb);
2616                 b.xns = b.xns || Roo.bootstrap;
2617                 b.xtype = b.xtype || 'Button';
2618                 if (typeof(b.listeners) == 'undefined') {
2619                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2620                 }
2621
2622                 var btn = Roo.factory(b);
2623
2624                 btn.render(this.el.select('.modal-footer div').first());
2625
2626             },this);
2627         }
2628         // render the children.
2629         var nitems = [];
2630
2631         if(typeof(this.items) != 'undefined'){
2632             var items = this.items;
2633             delete this.items;
2634
2635             for(var i =0;i < items.length;i++) {
2636                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2637             }
2638         }
2639
2640         this.items = nitems;
2641
2642         // where are these used - they used to be body/close/footer
2643
2644
2645         this.initEvents();
2646         //this.el.addClass([this.fieldClass, this.cls]);
2647
2648     },
2649
2650     getAutoCreate : function(){
2651
2652
2653         var bdy = {
2654                 cls : 'modal-body',
2655                 html : this.html || ''
2656         };
2657
2658         var title = {
2659             tag: 'h4',
2660             cls : 'modal-title',
2661             html : this.title
2662         };
2663
2664         if(this.specificTitle){
2665             title = this.title;
2666
2667         };
2668
2669         var header = [];
2670         if (this.allow_close) {
2671             header.push({
2672                 tag: 'button',
2673                 cls : 'close',
2674                 html : '&times'
2675             });
2676         }
2677
2678         header.push(title);
2679
2680         var size = '';
2681
2682         if(this.size.length){
2683             size = 'modal-' + this.size;
2684         }
2685
2686         var modal = {
2687             cls: "modal",
2688             style : 'display: none',
2689             cn : [
2690                 {
2691                     cls: "modal-dialog " + size,
2692                     cn : [
2693                         {
2694                             cls : "modal-content",
2695                             cn : [
2696                                 {
2697                                     cls : 'modal-header',
2698                                     cn : header
2699                                 },
2700                                 bdy,
2701                                 {
2702                                     cls : 'modal-footer',
2703                                     cn : [
2704                                         {
2705                                             tag: 'div',
2706                                             cls: 'btn-' + this.buttonPosition
2707                                         }
2708                                     ]
2709
2710                                 }
2711
2712
2713                             ]
2714
2715                         }
2716                     ]
2717
2718                 }
2719             ]
2720         };
2721
2722         if(this.animate){
2723             modal.cls += ' fade';
2724         }
2725
2726         return modal;
2727
2728     },
2729     getChildContainer : function() {
2730
2731          return this.bodyEl;
2732
2733     },
2734     getButtonContainer : function() {
2735          return this.el.select('.modal-footer div',true).first();
2736
2737     },
2738     initEvents : function()
2739     {
2740         if (this.allow_close) {
2741             this.closeEl.on('click', this.hide, this);
2742         }
2743         Roo.EventManager.onWindowResize(this.resize, this, true);
2744
2745
2746     },
2747
2748     resize : function()
2749     {
2750         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2751         if (this.fitwindow) {
2752             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2753             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2754             this.setSize(w,h);
2755         }
2756     },
2757
2758     setSize : function(w,h)
2759     {
2760         if (!w && !h) {
2761             return;
2762         }
2763         this.resizeTo(w,h);
2764     },
2765
2766     show : function() {
2767
2768         if (!this.rendered) {
2769             this.render();
2770         }
2771
2772         this.el.setStyle('display', 'block');
2773
2774         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2775             var _this = this;
2776             (function(){
2777                 this.el.addClass('in');
2778             }).defer(50, this);
2779         }else{
2780             this.el.addClass('in');
2781
2782         }
2783
2784         // not sure how we can show data in here..
2785         //if (this.tmpl) {
2786         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2787         //}
2788
2789         Roo.get(document.body).addClass("x-body-masked");
2790         
2791         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2792         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2793         this.maskEl.show();
2794         
2795         this.resize();
2796         
2797         this.fireEvent('show', this);
2798
2799         // set zindex here - otherwise it appears to be ignored...
2800         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2801
2802         (function () {
2803             this.items.forEach( function(e) {
2804                 e.layout ? e.layout() : false;
2805
2806             });
2807         }).defer(100,this);
2808
2809     },
2810     hide : function()
2811     {
2812         if(this.fireEvent("beforehide", this) !== false){
2813             this.maskEl.hide();
2814             Roo.get(document.body).removeClass("x-body-masked");
2815             this.el.removeClass('in');
2816             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2817
2818             if(this.animate){ // why
2819                 var _this = this;
2820                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2821             }else{
2822                 this.el.setStyle('display', 'none');
2823             }
2824             this.fireEvent('hide', this);
2825         }
2826     },
2827
2828     addButton : function(str, cb)
2829     {
2830
2831
2832         var b = Roo.apply({}, { html : str } );
2833         b.xns = b.xns || Roo.bootstrap;
2834         b.xtype = b.xtype || 'Button';
2835         if (typeof(b.listeners) == 'undefined') {
2836             b.listeners = { click : cb.createDelegate(this)  };
2837         }
2838
2839         var btn = Roo.factory(b);
2840
2841         btn.render(this.el.select('.modal-footer div').first());
2842
2843         return btn;
2844
2845     },
2846
2847     setDefaultButton : function(btn)
2848     {
2849         //this.el.select('.modal-footer').()
2850     },
2851     diff : false,
2852
2853     resizeTo: function(w,h)
2854     {
2855         // skip.. ?? why??
2856
2857         this.dialogEl.setWidth(w);
2858         if (this.diff === false) {
2859             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2860         }
2861
2862         this.bodyEl.setHeight(h-this.diff);
2863
2864         this.fireEvent('resize', this);
2865
2866     },
2867     setContentSize  : function(w, h)
2868     {
2869
2870     },
2871     onButtonClick: function(btn,e)
2872     {
2873         //Roo.log([a,b,c]);
2874         this.fireEvent('btnclick', btn.name, e);
2875     },
2876      /**
2877      * Set the title of the Dialog
2878      * @param {String} str new Title
2879      */
2880     setTitle: function(str) {
2881         this.titleEl.dom.innerHTML = str;
2882     },
2883     /**
2884      * Set the body of the Dialog
2885      * @param {String} str new Title
2886      */
2887     setBody: function(str) {
2888         this.bodyEl.dom.innerHTML = str;
2889     },
2890     /**
2891      * Set the body of the Dialog using the template
2892      * @param {Obj} data - apply this data to the template and replace the body contents.
2893      */
2894     applyBody: function(obj)
2895     {
2896         if (!this.tmpl) {
2897             Roo.log("Error - using apply Body without a template");
2898             //code
2899         }
2900         this.tmpl.overwrite(this.bodyEl, obj);
2901     }
2902
2903 });
2904
2905
2906 Roo.apply(Roo.bootstrap.Modal,  {
2907     /**
2908          * Button config that displays a single OK button
2909          * @type Object
2910          */
2911         OK :  [{
2912             name : 'ok',
2913             weight : 'primary',
2914             html : 'OK'
2915         }],
2916         /**
2917          * Button config that displays Yes and No buttons
2918          * @type Object
2919          */
2920         YESNO : [
2921             {
2922                 name  : 'no',
2923                 html : 'No'
2924             },
2925             {
2926                 name  :'yes',
2927                 weight : 'primary',
2928                 html : 'Yes'
2929             }
2930         ],
2931
2932         /**
2933          * Button config that displays OK and Cancel buttons
2934          * @type Object
2935          */
2936         OKCANCEL : [
2937             {
2938                name : 'cancel',
2939                 html : 'Cancel'
2940             },
2941             {
2942                 name : 'ok',
2943                 weight : 'primary',
2944                 html : 'OK'
2945             }
2946         ],
2947         /**
2948          * Button config that displays Yes, No and Cancel buttons
2949          * @type Object
2950          */
2951         YESNOCANCEL : [
2952             {
2953                 name : 'yes',
2954                 weight : 'primary',
2955                 html : 'Yes'
2956             },
2957             {
2958                 name : 'no',
2959                 html : 'No'
2960             },
2961             {
2962                 name : 'cancel',
2963                 html : 'Cancel'
2964             }
2965         ],
2966         
2967         zIndex : 10001
2968 });
2969 /*
2970  * - LGPL
2971  *
2972  * messagebox - can be used as a replace
2973  * 
2974  */
2975 /**
2976  * @class Roo.MessageBox
2977  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2978  * Example usage:
2979  *<pre><code>
2980 // Basic alert:
2981 Roo.Msg.alert('Status', 'Changes saved successfully.');
2982
2983 // Prompt for user data:
2984 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2985     if (btn == 'ok'){
2986         // process text value...
2987     }
2988 });
2989
2990 // Show a dialog using config options:
2991 Roo.Msg.show({
2992    title:'Save Changes?',
2993    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2994    buttons: Roo.Msg.YESNOCANCEL,
2995    fn: processResult,
2996    animEl: 'elId'
2997 });
2998 </code></pre>
2999  * @singleton
3000  */
3001 Roo.bootstrap.MessageBox = function(){
3002     var dlg, opt, mask, waitTimer;
3003     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3004     var buttons, activeTextEl, bwidth;
3005
3006     
3007     // private
3008     var handleButton = function(button){
3009         dlg.hide();
3010         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3011     };
3012
3013     // private
3014     var handleHide = function(){
3015         if(opt && opt.cls){
3016             dlg.el.removeClass(opt.cls);
3017         }
3018         //if(waitTimer){
3019         //    Roo.TaskMgr.stop(waitTimer);
3020         //    waitTimer = null;
3021         //}
3022     };
3023
3024     // private
3025     var updateButtons = function(b){
3026         var width = 0;
3027         if(!b){
3028             buttons["ok"].hide();
3029             buttons["cancel"].hide();
3030             buttons["yes"].hide();
3031             buttons["no"].hide();
3032             //dlg.footer.dom.style.display = 'none';
3033             return width;
3034         }
3035         dlg.footerEl.dom.style.display = '';
3036         for(var k in buttons){
3037             if(typeof buttons[k] != "function"){
3038                 if(b[k]){
3039                     buttons[k].show();
3040                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3041                     width += buttons[k].el.getWidth()+15;
3042                 }else{
3043                     buttons[k].hide();
3044                 }
3045             }
3046         }
3047         return width;
3048     };
3049
3050     // private
3051     var handleEsc = function(d, k, e){
3052         if(opt && opt.closable !== false){
3053             dlg.hide();
3054         }
3055         if(e){
3056             e.stopEvent();
3057         }
3058     };
3059
3060     return {
3061         /**
3062          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3063          * @return {Roo.BasicDialog} The BasicDialog element
3064          */
3065         getDialog : function(){
3066            if(!dlg){
3067                 dlg = new Roo.bootstrap.Modal( {
3068                     //draggable: true,
3069                     //resizable:false,
3070                     //constraintoviewport:false,
3071                     //fixedcenter:true,
3072                     //collapsible : false,
3073                     //shim:true,
3074                     //modal: true,
3075                 //    width: 'auto',
3076                   //  height:100,
3077                     //buttonAlign:"center",
3078                     closeClick : function(){
3079                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3080                             handleButton("no");
3081                         }else{
3082                             handleButton("cancel");
3083                         }
3084                     }
3085                 });
3086                 dlg.render();
3087                 dlg.on("hide", handleHide);
3088                 mask = dlg.mask;
3089                 //dlg.addKeyListener(27, handleEsc);
3090                 buttons = {};
3091                 this.buttons = buttons;
3092                 var bt = this.buttonText;
3093                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3094                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3095                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3096                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3097                 //Roo.log(buttons);
3098                 bodyEl = dlg.bodyEl.createChild({
3099
3100                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3101                         '<textarea class="roo-mb-textarea"></textarea>' +
3102                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3103                 });
3104                 msgEl = bodyEl.dom.firstChild;
3105                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3106                 textboxEl.enableDisplayMode();
3107                 textboxEl.addKeyListener([10,13], function(){
3108                     if(dlg.isVisible() && opt && opt.buttons){
3109                         if(opt.buttons.ok){
3110                             handleButton("ok");
3111                         }else if(opt.buttons.yes){
3112                             handleButton("yes");
3113                         }
3114                     }
3115                 });
3116                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3117                 textareaEl.enableDisplayMode();
3118                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3119                 progressEl.enableDisplayMode();
3120                 
3121                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3122                 //var pf = progressEl.dom.firstChild;
3123                 //if (pf) {
3124                     //pp = Roo.get(pf.firstChild);
3125                     //pp.setHeight(pf.offsetHeight);
3126                 //}
3127                 
3128             }
3129             return dlg;
3130         },
3131
3132         /**
3133          * Updates the message box body text
3134          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3135          * the XHTML-compliant non-breaking space character '&amp;#160;')
3136          * @return {Roo.MessageBox} This message box
3137          */
3138         updateText : function(text)
3139         {
3140             if(!dlg.isVisible() && !opt.width){
3141                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3142                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3143             }
3144             msgEl.innerHTML = text || '&#160;';
3145       
3146             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3147             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3148             var w = Math.max(
3149                     Math.min(opt.width || cw , this.maxWidth), 
3150                     Math.max(opt.minWidth || this.minWidth, bwidth)
3151             );
3152             if(opt.prompt){
3153                 activeTextEl.setWidth(w);
3154             }
3155             if(dlg.isVisible()){
3156                 dlg.fixedcenter = false;
3157             }
3158             // to big, make it scroll. = But as usual stupid IE does not support
3159             // !important..
3160             
3161             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3162                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3163                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3164             } else {
3165                 bodyEl.dom.style.height = '';
3166                 bodyEl.dom.style.overflowY = '';
3167             }
3168             if (cw > w) {
3169                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3170             } else {
3171                 bodyEl.dom.style.overflowX = '';
3172             }
3173             
3174             dlg.setContentSize(w, bodyEl.getHeight());
3175             if(dlg.isVisible()){
3176                 dlg.fixedcenter = true;
3177             }
3178             return this;
3179         },
3180
3181         /**
3182          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3183          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3184          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3185          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3186          * @return {Roo.MessageBox} This message box
3187          */
3188         updateProgress : function(value, text){
3189             if(text){
3190                 this.updateText(text);
3191             }
3192             if (pp) { // weird bug on my firefox - for some reason this is not defined
3193                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3194             }
3195             return this;
3196         },        
3197
3198         /**
3199          * Returns true if the message box is currently displayed
3200          * @return {Boolean} True if the message box is visible, else false
3201          */
3202         isVisible : function(){
3203             return dlg && dlg.isVisible();  
3204         },
3205
3206         /**
3207          * Hides the message box if it is displayed
3208          */
3209         hide : function(){
3210             if(this.isVisible()){
3211                 dlg.hide();
3212             }  
3213         },
3214
3215         /**
3216          * Displays a new message box, or reinitializes an existing message box, based on the config options
3217          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3218          * The following config object properties are supported:
3219          * <pre>
3220 Property    Type             Description
3221 ----------  ---------------  ------------------------------------------------------------------------------------
3222 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3223                                    closes (defaults to undefined)
3224 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3225                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3226 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3227                                    progress and wait dialogs will ignore this property and always hide the
3228                                    close button as they can only be closed programmatically.
3229 cls               String           A custom CSS class to apply to the message box element
3230 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3231                                    displayed (defaults to 75)
3232 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3233                                    function will be btn (the name of the button that was clicked, if applicable,
3234                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3235                                    Progress and wait dialogs will ignore this option since they do not respond to
3236                                    user actions and can only be closed programmatically, so any required function
3237                                    should be called by the same code after it closes the dialog.
3238 icon              String           A CSS class that provides a background image to be used as an icon for
3239                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3240 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3241 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3242 modal             Boolean          False to allow user interaction with the page while the message box is
3243                                    displayed (defaults to true)
3244 msg               String           A string that will replace the existing message box body text (defaults
3245                                    to the XHTML-compliant non-breaking space character '&#160;')
3246 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3247 progress          Boolean          True to display a progress bar (defaults to false)
3248 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3249 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3250 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3251 title             String           The title text
3252 value             String           The string value to set into the active textbox element if displayed
3253 wait              Boolean          True to display a progress bar (defaults to false)
3254 width             Number           The width of the dialog in pixels
3255 </pre>
3256          *
3257          * Example usage:
3258          * <pre><code>
3259 Roo.Msg.show({
3260    title: 'Address',
3261    msg: 'Please enter your address:',
3262    width: 300,
3263    buttons: Roo.MessageBox.OKCANCEL,
3264    multiline: true,
3265    fn: saveAddress,
3266    animEl: 'addAddressBtn'
3267 });
3268 </code></pre>
3269          * @param {Object} config Configuration options
3270          * @return {Roo.MessageBox} This message box
3271          */
3272         show : function(options)
3273         {
3274             
3275             // this causes nightmares if you show one dialog after another
3276             // especially on callbacks..
3277              
3278             if(this.isVisible()){
3279                 
3280                 this.hide();
3281                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3282                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3283                 Roo.log("New Dialog Message:" +  options.msg )
3284                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3285                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3286                 
3287             }
3288             var d = this.getDialog();
3289             opt = options;
3290             d.setTitle(opt.title || "&#160;");
3291             d.closeEl.setDisplayed(opt.closable !== false);
3292             activeTextEl = textboxEl;
3293             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3294             if(opt.prompt){
3295                 if(opt.multiline){
3296                     textboxEl.hide();
3297                     textareaEl.show();
3298                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3299                         opt.multiline : this.defaultTextHeight);
3300                     activeTextEl = textareaEl;
3301                 }else{
3302                     textboxEl.show();
3303                     textareaEl.hide();
3304                 }
3305             }else{
3306                 textboxEl.hide();
3307                 textareaEl.hide();
3308             }
3309             progressEl.setDisplayed(opt.progress === true);
3310             this.updateProgress(0);
3311             activeTextEl.dom.value = opt.value || "";
3312             if(opt.prompt){
3313                 dlg.setDefaultButton(activeTextEl);
3314             }else{
3315                 var bs = opt.buttons;
3316                 var db = null;
3317                 if(bs && bs.ok){
3318                     db = buttons["ok"];
3319                 }else if(bs && bs.yes){
3320                     db = buttons["yes"];
3321                 }
3322                 dlg.setDefaultButton(db);
3323             }
3324             bwidth = updateButtons(opt.buttons);
3325             this.updateText(opt.msg);
3326             if(opt.cls){
3327                 d.el.addClass(opt.cls);
3328             }
3329             d.proxyDrag = opt.proxyDrag === true;
3330             d.modal = opt.modal !== false;
3331             d.mask = opt.modal !== false ? mask : false;
3332             if(!d.isVisible()){
3333                 // force it to the end of the z-index stack so it gets a cursor in FF
3334                 document.body.appendChild(dlg.el.dom);
3335                 d.animateTarget = null;
3336                 d.show(options.animEl);
3337             }
3338             return this;
3339         },
3340
3341         /**
3342          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3343          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3344          * and closing the message box when the process is complete.
3345          * @param {String} title The title bar text
3346          * @param {String} msg The message box body text
3347          * @return {Roo.MessageBox} This message box
3348          */
3349         progress : function(title, msg){
3350             this.show({
3351                 title : title,
3352                 msg : msg,
3353                 buttons: false,
3354                 progress:true,
3355                 closable:false,
3356                 minWidth: this.minProgressWidth,
3357                 modal : true
3358             });
3359             return this;
3360         },
3361
3362         /**
3363          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3364          * If a callback function is passed it will be called after the user clicks the button, and the
3365          * id of the button that was clicked will be passed as the only parameter to the callback
3366          * (could also be the top-right close button).
3367          * @param {String} title The title bar text
3368          * @param {String} msg The message box body text
3369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3370          * @param {Object} scope (optional) The scope of the callback function
3371          * @return {Roo.MessageBox} This message box
3372          */
3373         alert : function(title, msg, fn, scope)
3374         {
3375             this.show({
3376                 title : title,
3377                 msg : msg,
3378                 buttons: this.OK,
3379                 fn: fn,
3380                 closable : false,
3381                 scope : scope,
3382                 modal : true
3383             });
3384             return this;
3385         },
3386
3387         /**
3388          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3389          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3390          * You are responsible for closing the message box when the process is complete.
3391          * @param {String} msg The message box body text
3392          * @param {String} title (optional) The title bar text
3393          * @return {Roo.MessageBox} This message box
3394          */
3395         wait : function(msg, title){
3396             this.show({
3397                 title : title,
3398                 msg : msg,
3399                 buttons: false,
3400                 closable:false,
3401                 progress:true,
3402                 modal:true,
3403                 width:300,
3404                 wait:true
3405             });
3406             waitTimer = Roo.TaskMgr.start({
3407                 run: function(i){
3408                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3409                 },
3410                 interval: 1000
3411             });
3412             return this;
3413         },
3414
3415         /**
3416          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3417          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3418          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3419          * @param {String} title The title bar text
3420          * @param {String} msg The message box body text
3421          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3422          * @param {Object} scope (optional) The scope of the callback function
3423          * @return {Roo.MessageBox} This message box
3424          */
3425         confirm : function(title, msg, fn, scope){
3426             this.show({
3427                 title : title,
3428                 msg : msg,
3429                 buttons: this.YESNO,
3430                 fn: fn,
3431                 scope : scope,
3432                 modal : true
3433             });
3434             return this;
3435         },
3436
3437         /**
3438          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3439          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3440          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3441          * (could also be the top-right close button) and the text that was entered will be passed as the two
3442          * parameters to the callback.
3443          * @param {String} title The title bar text
3444          * @param {String} msg The message box body text
3445          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3446          * @param {Object} scope (optional) The scope of the callback function
3447          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3448          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3449          * @return {Roo.MessageBox} This message box
3450          */
3451         prompt : function(title, msg, fn, scope, multiline){
3452             this.show({
3453                 title : title,
3454                 msg : msg,
3455                 buttons: this.OKCANCEL,
3456                 fn: fn,
3457                 minWidth:250,
3458                 scope : scope,
3459                 prompt:true,
3460                 multiline: multiline,
3461                 modal : true
3462             });
3463             return this;
3464         },
3465
3466         /**
3467          * Button config that displays a single OK button
3468          * @type Object
3469          */
3470         OK : {ok:true},
3471         /**
3472          * Button config that displays Yes and No buttons
3473          * @type Object
3474          */
3475         YESNO : {yes:true, no:true},
3476         /**
3477          * Button config that displays OK and Cancel buttons
3478          * @type Object
3479          */
3480         OKCANCEL : {ok:true, cancel:true},
3481         /**
3482          * Button config that displays Yes, No and Cancel buttons
3483          * @type Object
3484          */
3485         YESNOCANCEL : {yes:true, no:true, cancel:true},
3486
3487         /**
3488          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3489          * @type Number
3490          */
3491         defaultTextHeight : 75,
3492         /**
3493          * The maximum width in pixels of the message box (defaults to 600)
3494          * @type Number
3495          */
3496         maxWidth : 600,
3497         /**
3498          * The minimum width in pixels of the message box (defaults to 100)
3499          * @type Number
3500          */
3501         minWidth : 100,
3502         /**
3503          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3504          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3505          * @type Number
3506          */
3507         minProgressWidth : 250,
3508         /**
3509          * An object containing the default button text strings that can be overriden for localized language support.
3510          * Supported properties are: ok, cancel, yes and no.
3511          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3512          * @type Object
3513          */
3514         buttonText : {
3515             ok : "OK",
3516             cancel : "Cancel",
3517             yes : "Yes",
3518             no : "No"
3519         }
3520     };
3521 }();
3522
3523 /**
3524  * Shorthand for {@link Roo.MessageBox}
3525  */
3526 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3527 Roo.Msg = Roo.Msg || Roo.MessageBox;
3528 /*
3529  * - LGPL
3530  *
3531  * navbar
3532  * 
3533  */
3534
3535 /**
3536  * @class Roo.bootstrap.Navbar
3537  * @extends Roo.bootstrap.Component
3538  * Bootstrap Navbar class
3539
3540  * @constructor
3541  * Create a new Navbar
3542  * @param {Object} config The config object
3543  */
3544
3545
3546 Roo.bootstrap.Navbar = function(config){
3547     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3548     this.addEvents({
3549         // raw events
3550         /**
3551          * @event beforetoggle
3552          * Fire before toggle the menu
3553          * @param {Roo.EventObject} e
3554          */
3555         "beforetoggle" : true
3556     });
3557 };
3558
3559 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3560     
3561     
3562    
3563     // private
3564     navItems : false,
3565     loadMask : false,
3566     
3567     
3568     getAutoCreate : function(){
3569         
3570         
3571         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3572         
3573     },
3574     
3575     initEvents :function ()
3576     {
3577         //Roo.log(this.el.select('.navbar-toggle',true));
3578         this.el.select('.navbar-toggle',true).on('click', function() {
3579             if(this.fireEvent('beforetoggle', this) !== false){
3580                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3581             }
3582             
3583         }, this);
3584         
3585         var mark = {
3586             tag: "div",
3587             cls:"x-dlg-mask"
3588         };
3589         
3590         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3591         
3592         var size = this.el.getSize();
3593         this.maskEl.setSize(size.width, size.height);
3594         this.maskEl.enableDisplayMode("block");
3595         this.maskEl.hide();
3596         
3597         if(this.loadMask){
3598             this.maskEl.show();
3599         }
3600     },
3601     
3602     
3603     getChildContainer : function()
3604     {
3605         if (this.el.select('.collapse').getCount()) {
3606             return this.el.select('.collapse',true).first();
3607         }
3608         
3609         return this.el;
3610     },
3611     
3612     mask : function()
3613     {
3614         this.maskEl.show();
3615     },
3616     
3617     unmask : function()
3618     {
3619         this.maskEl.hide();
3620     } 
3621     
3622     
3623     
3624     
3625 });
3626
3627
3628
3629  
3630
3631  /*
3632  * - LGPL
3633  *
3634  * navbar
3635  * 
3636  */
3637
3638 /**
3639  * @class Roo.bootstrap.NavSimplebar
3640  * @extends Roo.bootstrap.Navbar
3641  * Bootstrap Sidebar class
3642  *
3643  * @cfg {Boolean} inverse is inverted color
3644  * 
3645  * @cfg {String} type (nav | pills | tabs)
3646  * @cfg {Boolean} arrangement stacked | justified
3647  * @cfg {String} align (left | right) alignment
3648  * 
3649  * @cfg {Boolean} main (true|false) main nav bar? default false
3650  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3651  * 
3652  * @cfg {String} tag (header|footer|nav|div) default is nav 
3653
3654  * 
3655  * 
3656  * 
3657  * @constructor
3658  * Create a new Sidebar
3659  * @param {Object} config The config object
3660  */
3661
3662
3663 Roo.bootstrap.NavSimplebar = function(config){
3664     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3665 };
3666
3667 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3668     
3669     inverse: false,
3670     
3671     type: false,
3672     arrangement: '',
3673     align : false,
3674     
3675     
3676     
3677     main : false,
3678     
3679     
3680     tag : false,
3681     
3682     
3683     getAutoCreate : function(){
3684         
3685         
3686         var cfg = {
3687             tag : this.tag || 'div',
3688             cls : 'navbar'
3689         };
3690           
3691         
3692         cfg.cn = [
3693             {
3694                 cls: 'nav',
3695                 tag : 'ul'
3696             }
3697         ];
3698         
3699          
3700         this.type = this.type || 'nav';
3701         if (['tabs','pills'].indexOf(this.type)!==-1) {
3702             cfg.cn[0].cls += ' nav-' + this.type
3703         
3704         
3705         } else {
3706             if (this.type!=='nav') {
3707                 Roo.log('nav type must be nav/tabs/pills')
3708             }
3709             cfg.cn[0].cls += ' navbar-nav'
3710         }
3711         
3712         
3713         
3714         
3715         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3716             cfg.cn[0].cls += ' nav-' + this.arrangement;
3717         }
3718         
3719         
3720         if (this.align === 'right') {
3721             cfg.cn[0].cls += ' navbar-right';
3722         }
3723         
3724         if (this.inverse) {
3725             cfg.cls += ' navbar-inverse';
3726             
3727         }
3728         
3729         
3730         return cfg;
3731     
3732         
3733     }
3734     
3735     
3736     
3737 });
3738
3739
3740
3741  
3742
3743  
3744        /*
3745  * - LGPL
3746  *
3747  * navbar
3748  * 
3749  */
3750
3751 /**
3752  * @class Roo.bootstrap.NavHeaderbar
3753  * @extends Roo.bootstrap.NavSimplebar
3754  * Bootstrap Sidebar class
3755  *
3756  * @cfg {String} brand what is brand
3757  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3758  * @cfg {String} brand_href href of the brand
3759  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3760  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3761  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3762  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3763  * 
3764  * @constructor
3765  * Create a new Sidebar
3766  * @param {Object} config The config object
3767  */
3768
3769
3770 Roo.bootstrap.NavHeaderbar = function(config){
3771     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3772       
3773 };
3774
3775 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3776     
3777     position: '',
3778     brand: '',
3779     brand_href: false,
3780     srButton : true,
3781     autohide : false,
3782     desktopCenter : false,
3783    
3784     
3785     getAutoCreate : function(){
3786         
3787         var   cfg = {
3788             tag: this.nav || 'nav',
3789             cls: 'navbar',
3790             role: 'navigation',
3791             cn: []
3792         };
3793         
3794         var cn = cfg.cn;
3795         if (this.desktopCenter) {
3796             cn.push({cls : 'container', cn : []});
3797             cn = cn[0].cn;
3798         }
3799         
3800         if(this.srButton){
3801             cn.push({
3802                 tag: 'div',
3803                 cls: 'navbar-header',
3804                 cn: [
3805                     {
3806                         tag: 'button',
3807                         type: 'button',
3808                         cls: 'navbar-toggle',
3809                         'data-toggle': 'collapse',
3810                         cn: [
3811                             {
3812                                 tag: 'span',
3813                                 cls: 'sr-only',
3814                                 html: 'Toggle navigation'
3815                             },
3816                             {
3817                                 tag: 'span',
3818                                 cls: 'icon-bar'
3819                             },
3820                             {
3821                                 tag: 'span',
3822                                 cls: 'icon-bar'
3823                             },
3824                             {
3825                                 tag: 'span',
3826                                 cls: 'icon-bar'
3827                             }
3828                         ]
3829                     }
3830                 ]
3831             });
3832         }
3833         
3834         cn.push({
3835             tag: 'div',
3836             cls: 'collapse navbar-collapse',
3837             cn : []
3838         });
3839         
3840         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3841         
3842         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3843             cfg.cls += ' navbar-' + this.position;
3844             
3845             // tag can override this..
3846             
3847             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3848         }
3849         
3850         if (this.brand !== '') {
3851             cn[0].cn.push({
3852                 tag: 'a',
3853                 href: this.brand_href ? this.brand_href : '#',
3854                 cls: 'navbar-brand',
3855                 cn: [
3856                 this.brand
3857                 ]
3858             });
3859         }
3860         
3861         if(this.main){
3862             cfg.cls += ' main-nav';
3863         }
3864         
3865         
3866         return cfg;
3867
3868         
3869     },
3870     getHeaderChildContainer : function()
3871     {
3872         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3873             return this.el.select('.navbar-header',true).first();
3874         }
3875         
3876         return this.getChildContainer();
3877     },
3878     
3879     
3880     initEvents : function()
3881     {
3882         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3883         
3884         if (this.autohide) {
3885             
3886             var prevScroll = 0;
3887             var ft = this.el;
3888             
3889             Roo.get(document).on('scroll',function(e) {
3890                 var ns = Roo.get(document).getScroll().top;
3891                 var os = prevScroll;
3892                 prevScroll = ns;
3893                 
3894                 if(ns > os){
3895                     ft.removeClass('slideDown');
3896                     ft.addClass('slideUp');
3897                     return;
3898                 }
3899                 ft.removeClass('slideUp');
3900                 ft.addClass('slideDown');
3901                  
3902               
3903           },this);
3904         }
3905     }    
3906     
3907 });
3908
3909
3910
3911  
3912
3913  /*
3914  * - LGPL
3915  *
3916  * navbar
3917  * 
3918  */
3919
3920 /**
3921  * @class Roo.bootstrap.NavSidebar
3922  * @extends Roo.bootstrap.Navbar
3923  * Bootstrap Sidebar class
3924  * 
3925  * @constructor
3926  * Create a new Sidebar
3927  * @param {Object} config The config object
3928  */
3929
3930
3931 Roo.bootstrap.NavSidebar = function(config){
3932     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3933 };
3934
3935 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3936     
3937     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3938     
3939     getAutoCreate : function(){
3940         
3941         
3942         return  {
3943             tag: 'div',
3944             cls: 'sidebar sidebar-nav'
3945         };
3946     
3947         
3948     }
3949     
3950     
3951     
3952 });
3953
3954
3955
3956  
3957
3958  /*
3959  * - LGPL
3960  *
3961  * nav group
3962  * 
3963  */
3964
3965 /**
3966  * @class Roo.bootstrap.NavGroup
3967  * @extends Roo.bootstrap.Component
3968  * Bootstrap NavGroup class
3969  * @cfg {String} align (left|right)
3970  * @cfg {Boolean} inverse
3971  * @cfg {String} type (nav|pills|tab) default nav
3972  * @cfg {String} navId - reference Id for navbar.
3973
3974  * 
3975  * @constructor
3976  * Create a new nav group
3977  * @param {Object} config The config object
3978  */
3979
3980 Roo.bootstrap.NavGroup = function(config){
3981     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3982     this.navItems = [];
3983    
3984     Roo.bootstrap.NavGroup.register(this);
3985      this.addEvents({
3986         /**
3987              * @event changed
3988              * Fires when the active item changes
3989              * @param {Roo.bootstrap.NavGroup} this
3990              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3991              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3992          */
3993         'changed': true
3994      });
3995     
3996 };
3997
3998 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3999     
4000     align: '',
4001     inverse: false,
4002     form: false,
4003     type: 'nav',
4004     navId : '',
4005     // private
4006     
4007     navItems : false, 
4008     
4009     getAutoCreate : function()
4010     {
4011         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4012         
4013         cfg = {
4014             tag : 'ul',
4015             cls: 'nav' 
4016         };
4017         
4018         if (['tabs','pills'].indexOf(this.type)!==-1) {
4019             cfg.cls += ' nav-' + this.type
4020         } else {
4021             if (this.type!=='nav') {
4022                 Roo.log('nav type must be nav/tabs/pills')
4023             }
4024             cfg.cls += ' navbar-nav'
4025         }
4026         
4027         if (this.parent() && this.parent().sidebar) {
4028             cfg = {
4029                 tag: 'ul',
4030                 cls: 'dashboard-menu sidebar-menu'
4031             };
4032             
4033             return cfg;
4034         }
4035         
4036         if (this.form === true) {
4037             cfg = {
4038                 tag: 'form',
4039                 cls: 'navbar-form'
4040             };
4041             
4042             if (this.align === 'right') {
4043                 cfg.cls += ' navbar-right';
4044             } else {
4045                 cfg.cls += ' navbar-left';
4046             }
4047         }
4048         
4049         if (this.align === 'right') {
4050             cfg.cls += ' navbar-right';
4051         }
4052         
4053         if (this.inverse) {
4054             cfg.cls += ' navbar-inverse';
4055             
4056         }
4057         
4058         
4059         return cfg;
4060     },
4061     /**
4062     * sets the active Navigation item
4063     * @param {Roo.bootstrap.NavItem} the new current navitem
4064     */
4065     setActiveItem : function(item)
4066     {
4067         var prev = false;
4068         Roo.each(this.navItems, function(v){
4069             if (v == item) {
4070                 return ;
4071             }
4072             if (v.isActive()) {
4073                 v.setActive(false, true);
4074                 prev = v;
4075                 
4076             }
4077             
4078         });
4079
4080         item.setActive(true, true);
4081         this.fireEvent('changed', this, item, prev);
4082         
4083         
4084     },
4085     /**
4086     * gets the active Navigation item
4087     * @return {Roo.bootstrap.NavItem} the current navitem
4088     */
4089     getActive : function()
4090     {
4091         
4092         var prev = false;
4093         Roo.each(this.navItems, function(v){
4094             
4095             if (v.isActive()) {
4096                 prev = v;
4097                 
4098             }
4099             
4100         });
4101         return prev;
4102     },
4103     
4104     indexOfNav : function()
4105     {
4106         
4107         var prev = false;
4108         Roo.each(this.navItems, function(v,i){
4109             
4110             if (v.isActive()) {
4111                 prev = i;
4112                 
4113             }
4114             
4115         });
4116         return prev;
4117     },
4118     /**
4119     * adds a Navigation item
4120     * @param {Roo.bootstrap.NavItem} the navitem to add
4121     */
4122     addItem : function(cfg)
4123     {
4124         var cn = new Roo.bootstrap.NavItem(cfg);
4125         this.register(cn);
4126         cn.parentId = this.id;
4127         cn.onRender(this.el, null);
4128         return cn;
4129     },
4130     /**
4131     * register a Navigation item
4132     * @param {Roo.bootstrap.NavItem} the navitem to add
4133     */
4134     register : function(item)
4135     {
4136         this.navItems.push( item);
4137         item.navId = this.navId;
4138     
4139     },
4140     
4141     /**
4142     * clear all the Navigation item
4143     */
4144    
4145     clearAll : function()
4146     {
4147         this.navItems = [];
4148         this.el.dom.innerHTML = '';
4149     },
4150     
4151     getNavItem: function(tabId)
4152     {
4153         var ret = false;
4154         Roo.each(this.navItems, function(e) {
4155             if (e.tabId == tabId) {
4156                ret =  e;
4157                return false;
4158             }
4159             return true;
4160             
4161         });
4162         return ret;
4163     },
4164     
4165     setActiveNext : function()
4166     {
4167         var i = this.indexOfNav(this.getActive());
4168         if (i > this.navItems.length) {
4169             return;
4170         }
4171         this.setActiveItem(this.navItems[i+1]);
4172     },
4173     setActivePrev : function()
4174     {
4175         var i = this.indexOfNav(this.getActive());
4176         if (i  < 1) {
4177             return;
4178         }
4179         this.setActiveItem(this.navItems[i-1]);
4180     },
4181     clearWasActive : function(except) {
4182         Roo.each(this.navItems, function(e) {
4183             if (e.tabId != except.tabId && e.was_active) {
4184                e.was_active = false;
4185                return false;
4186             }
4187             return true;
4188             
4189         });
4190     },
4191     getWasActive : function ()
4192     {
4193         var r = false;
4194         Roo.each(this.navItems, function(e) {
4195             if (e.was_active) {
4196                r = e;
4197                return false;
4198             }
4199             return true;
4200             
4201         });
4202         return r;
4203     }
4204     
4205     
4206 });
4207
4208  
4209 Roo.apply(Roo.bootstrap.NavGroup, {
4210     
4211     groups: {},
4212      /**
4213     * register a Navigation Group
4214     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4215     */
4216     register : function(navgrp)
4217     {
4218         this.groups[navgrp.navId] = navgrp;
4219         
4220     },
4221     /**
4222     * fetch a Navigation Group based on the navigation ID
4223     * @param {string} the navgroup to add
4224     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4225     */
4226     get: function(navId) {
4227         if (typeof(this.groups[navId]) == 'undefined') {
4228             return false;
4229             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4230         }
4231         return this.groups[navId] ;
4232     }
4233     
4234     
4235     
4236 });
4237
4238  /*
4239  * - LGPL
4240  *
4241  * row
4242  * 
4243  */
4244
4245 /**
4246  * @class Roo.bootstrap.NavItem
4247  * @extends Roo.bootstrap.Component
4248  * Bootstrap Navbar.NavItem class
4249  * @cfg {String} href  link to
4250  * @cfg {String} html content of button
4251  * @cfg {String} badge text inside badge
4252  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4253  * @cfg {String} glyphicon name of glyphicon
4254  * @cfg {String} icon name of font awesome icon
4255  * @cfg {Boolean} active Is item active
4256  * @cfg {Boolean} disabled Is item disabled
4257  
4258  * @cfg {Boolean} preventDefault (true | false) default false
4259  * @cfg {String} tabId the tab that this item activates.
4260  * @cfg {String} tagtype (a|span) render as a href or span?
4261  * @cfg {Boolean} animateRef (true|false) link to element default false  
4262   
4263  * @constructor
4264  * Create a new Navbar Item
4265  * @param {Object} config The config object
4266  */
4267 Roo.bootstrap.NavItem = function(config){
4268     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4269     this.addEvents({
4270         // raw events
4271         /**
4272          * @event click
4273          * The raw click event for the entire grid.
4274          * @param {Roo.EventObject} e
4275          */
4276         "click" : true,
4277          /**
4278             * @event changed
4279             * Fires when the active item active state changes
4280             * @param {Roo.bootstrap.NavItem} this
4281             * @param {boolean} state the new state
4282              
4283          */
4284         'changed': true,
4285         /**
4286             * @event scrollto
4287             * Fires when scroll to element
4288             * @param {Roo.bootstrap.NavItem} this
4289             * @param {Object} options
4290             * @param {Roo.EventObject} e
4291              
4292          */
4293         'scrollto': true
4294     });
4295    
4296 };
4297
4298 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4299     
4300     href: false,
4301     html: '',
4302     badge: '',
4303     icon: false,
4304     glyphicon: false,
4305     active: false,
4306     preventDefault : false,
4307     tabId : false,
4308     tagtype : 'a',
4309     disabled : false,
4310     animateRef : false,
4311     was_active : false,
4312     
4313     getAutoCreate : function(){
4314          
4315         var cfg = {
4316             tag: 'li',
4317             cls: 'nav-item'
4318             
4319         };
4320         
4321         if (this.active) {
4322             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4323         }
4324         if (this.disabled) {
4325             cfg.cls += ' disabled';
4326         }
4327         
4328         if (this.href || this.html || this.glyphicon || this.icon) {
4329             cfg.cn = [
4330                 {
4331                     tag: this.tagtype,
4332                     href : this.href || "#",
4333                     html: this.html || ''
4334                 }
4335             ];
4336             
4337             if (this.icon) {
4338                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4339             }
4340
4341             if(this.glyphicon) {
4342                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4343             }
4344             
4345             if (this.menu) {
4346                 
4347                 cfg.cn[0].html += " <span class='caret'></span>";
4348              
4349             }
4350             
4351             if (this.badge !== '') {
4352                  
4353                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4354             }
4355         }
4356         
4357         
4358         
4359         return cfg;
4360     },
4361     initEvents: function() 
4362     {
4363         if (typeof (this.menu) != 'undefined') {
4364             this.menu.parentType = this.xtype;
4365             this.menu.triggerEl = this.el;
4366             this.menu = this.addxtype(Roo.apply({}, this.menu));
4367         }
4368         
4369         this.el.select('a',true).on('click', this.onClick, this);
4370         
4371         if(this.tagtype == 'span'){
4372             this.el.select('span',true).on('click', this.onClick, this);
4373         }
4374        
4375         // at this point parent should be available..
4376         this.parent().register(this);
4377     },
4378     
4379     onClick : function(e)
4380     {
4381         if (e.getTarget('.dropdown-menu-item')) {
4382             // did you click on a menu itemm.... - then don't trigger onclick..
4383             return;
4384         }
4385         
4386         if(
4387                 this.preventDefault || 
4388                 this.href == '#' 
4389         ){
4390             Roo.log("NavItem - prevent Default?");
4391             e.preventDefault();
4392         }
4393         
4394         if (this.disabled) {
4395             return;
4396         }
4397         
4398         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4399         if (tg && tg.transition) {
4400             Roo.log("waiting for the transitionend");
4401             return;
4402         }
4403         
4404         
4405         
4406         //Roo.log("fire event clicked");
4407         if(this.fireEvent('click', this, e) === false){
4408             return;
4409         };
4410         
4411         if(this.tagtype == 'span'){
4412             return;
4413         }
4414         
4415         //Roo.log(this.href);
4416         var ael = this.el.select('a',true).first();
4417         //Roo.log(ael);
4418         
4419         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4420             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4421             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4422                 return; // ignore... - it's a 'hash' to another page.
4423             }
4424             Roo.log("NavItem - prevent Default?");
4425             e.preventDefault();
4426             this.scrollToElement(e);
4427         }
4428         
4429         
4430         var p =  this.parent();
4431    
4432         if (['tabs','pills'].indexOf(p.type)!==-1) {
4433             if (typeof(p.setActiveItem) !== 'undefined') {
4434                 p.setActiveItem(this);
4435             }
4436         }
4437         
4438         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4439         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4440             // remove the collapsed menu expand...
4441             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4442         }
4443     },
4444     
4445     isActive: function () {
4446         return this.active
4447     },
4448     setActive : function(state, fire, is_was_active)
4449     {
4450         if (this.active && !state && this.navId) {
4451             this.was_active = true;
4452             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4453             if (nv) {
4454                 nv.clearWasActive(this);
4455             }
4456             
4457         }
4458         this.active = state;
4459         
4460         if (!state ) {
4461             this.el.removeClass('active');
4462         } else if (!this.el.hasClass('active')) {
4463             this.el.addClass('active');
4464         }
4465         if (fire) {
4466             this.fireEvent('changed', this, state);
4467         }
4468         
4469         // show a panel if it's registered and related..
4470         
4471         if (!this.navId || !this.tabId || !state || is_was_active) {
4472             return;
4473         }
4474         
4475         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4476         if (!tg) {
4477             return;
4478         }
4479         var pan = tg.getPanelByName(this.tabId);
4480         if (!pan) {
4481             return;
4482         }
4483         // if we can not flip to new panel - go back to old nav highlight..
4484         if (false == tg.showPanel(pan)) {
4485             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4486             if (nv) {
4487                 var onav = nv.getWasActive();
4488                 if (onav) {
4489                     onav.setActive(true, false, true);
4490                 }
4491             }
4492             
4493         }
4494         
4495         
4496         
4497     },
4498      // this should not be here...
4499     setDisabled : function(state)
4500     {
4501         this.disabled = state;
4502         if (!state ) {
4503             this.el.removeClass('disabled');
4504         } else if (!this.el.hasClass('disabled')) {
4505             this.el.addClass('disabled');
4506         }
4507         
4508     },
4509     
4510     /**
4511      * Fetch the element to display the tooltip on.
4512      * @return {Roo.Element} defaults to this.el
4513      */
4514     tooltipEl : function()
4515     {
4516         return this.el.select('' + this.tagtype + '', true).first();
4517     },
4518     
4519     scrollToElement : function(e)
4520     {
4521         var c = document.body;
4522         
4523         /*
4524          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4525          */
4526         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4527             c = document.documentElement;
4528         }
4529         
4530         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4531         
4532         if(!target){
4533             return;
4534         }
4535
4536         var o = target.calcOffsetsTo(c);
4537         
4538         var options = {
4539             target : target,
4540             value : o[1]
4541         };
4542         
4543         this.fireEvent('scrollto', this, options, e);
4544         
4545         Roo.get(c).scrollTo('top', options.value, true);
4546         
4547         return;
4548     }
4549 });
4550  
4551
4552  /*
4553  * - LGPL
4554  *
4555  * sidebar item
4556  *
4557  *  li
4558  *    <span> icon </span>
4559  *    <span> text </span>
4560  *    <span>badge </span>
4561  */
4562
4563 /**
4564  * @class Roo.bootstrap.NavSidebarItem
4565  * @extends Roo.bootstrap.NavItem
4566  * Bootstrap Navbar.NavSidebarItem class
4567  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4568  * {Boolean} open is the menu open
4569  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4570  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4571  * {String} buttonSize (sm|md|lg)the extra classes for the button
4572  * {Boolean} showArrow show arrow next to the text (default true)
4573  * @constructor
4574  * Create a new Navbar Button
4575  * @param {Object} config The config object
4576  */
4577 Roo.bootstrap.NavSidebarItem = function(config){
4578     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4579     this.addEvents({
4580         // raw events
4581         /**
4582          * @event click
4583          * The raw click event for the entire grid.
4584          * @param {Roo.EventObject} e
4585          */
4586         "click" : true,
4587          /**
4588             * @event changed
4589             * Fires when the active item active state changes
4590             * @param {Roo.bootstrap.NavSidebarItem} this
4591             * @param {boolean} state the new state
4592              
4593          */
4594         'changed': true
4595     });
4596    
4597 };
4598
4599 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4600     
4601     badgeWeight : 'default',
4602     
4603     open: false,
4604     
4605     buttonView : false,
4606     
4607     buttonWeight : 'default',
4608     
4609     buttonSize : 'md',
4610     
4611     showArrow : true,
4612     
4613     getAutoCreate : function(){
4614         
4615         
4616         var a = {
4617                 tag: 'a',
4618                 href : this.href || '#',
4619                 cls: '',
4620                 html : '',
4621                 cn : []
4622         };
4623         
4624         if(this.buttonView){
4625             a = {
4626                 tag: 'button',
4627                 href : this.href || '#',
4628                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4629                 html : this.html,
4630                 cn : []
4631             };
4632         }
4633         
4634         var cfg = {
4635             tag: 'li',
4636             cls: '',
4637             cn: [ a ]
4638         };
4639         
4640         if (this.active) {
4641             cfg.cls += ' active';
4642         }
4643         
4644         if (this.disabled) {
4645             cfg.cls += ' disabled';
4646         }
4647         if (this.open) {
4648             cfg.cls += ' open x-open';
4649         }
4650         // left icon..
4651         if (this.glyphicon || this.icon) {
4652             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4653             a.cn.push({ tag : 'i', cls : c }) ;
4654         }
4655         
4656         if(!this.buttonView){
4657             var span = {
4658                 tag: 'span',
4659                 html : this.html || ''
4660             };
4661
4662             a.cn.push(span);
4663             
4664         }
4665         
4666         if (this.badge !== '') {
4667             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4668         }
4669         
4670         if (this.menu) {
4671             
4672             if(this.showArrow){
4673                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4674             }
4675             
4676             a.cls += ' dropdown-toggle treeview' ;
4677         }
4678         
4679         return cfg;
4680     },
4681     
4682     initEvents : function()
4683     { 
4684         if (typeof (this.menu) != 'undefined') {
4685             this.menu.parentType = this.xtype;
4686             this.menu.triggerEl = this.el;
4687             this.menu = this.addxtype(Roo.apply({}, this.menu));
4688         }
4689         
4690         this.el.on('click', this.onClick, this);
4691         
4692         if(this.badge !== ''){
4693             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4694         }
4695         
4696     },
4697     
4698     onClick : function(e)
4699     {
4700         if(this.disabled){
4701             e.preventDefault();
4702             return;
4703         }
4704         
4705         if(this.preventDefault){
4706             e.preventDefault();
4707         }
4708         
4709         this.fireEvent('click', this);
4710     },
4711     
4712     disable : function()
4713     {
4714         this.setDisabled(true);
4715     },
4716     
4717     enable : function()
4718     {
4719         this.setDisabled(false);
4720     },
4721     
4722     setDisabled : function(state)
4723     {
4724         if(this.disabled == state){
4725             return;
4726         }
4727         
4728         this.disabled = state;
4729         
4730         if (state) {
4731             this.el.addClass('disabled');
4732             return;
4733         }
4734         
4735         this.el.removeClass('disabled');
4736         
4737         return;
4738     },
4739     
4740     setActive : function(state)
4741     {
4742         if(this.active == state){
4743             return;
4744         }
4745         
4746         this.active = state;
4747         
4748         if (state) {
4749             this.el.addClass('active');
4750             return;
4751         }
4752         
4753         this.el.removeClass('active');
4754         
4755         return;
4756     },
4757     
4758     isActive: function () 
4759     {
4760         return this.active;
4761     },
4762     
4763     setBadge : function(str)
4764     {
4765         if(!this.badgeEl){
4766             return;
4767         }
4768         
4769         this.badgeEl.dom.innerHTML = str;
4770     }
4771     
4772    
4773      
4774  
4775 });
4776  
4777
4778  /*
4779  * - LGPL
4780  *
4781  * row
4782  * 
4783  */
4784
4785 /**
4786  * @class Roo.bootstrap.Row
4787  * @extends Roo.bootstrap.Component
4788  * Bootstrap Row class (contains columns...)
4789  * 
4790  * @constructor
4791  * Create a new Row
4792  * @param {Object} config The config object
4793  */
4794
4795 Roo.bootstrap.Row = function(config){
4796     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4797 };
4798
4799 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4800     
4801     getAutoCreate : function(){
4802        return {
4803             cls: 'row clearfix'
4804        };
4805     }
4806     
4807     
4808 });
4809
4810  
4811
4812  /*
4813  * - LGPL
4814  *
4815  * element
4816  * 
4817  */
4818
4819 /**
4820  * @class Roo.bootstrap.Element
4821  * @extends Roo.bootstrap.Component
4822  * Bootstrap Element class
4823  * @cfg {String} html contents of the element
4824  * @cfg {String} tag tag of the element
4825  * @cfg {String} cls class of the element
4826  * @cfg {Boolean} preventDefault (true|false) default false
4827  * @cfg {Boolean} clickable (true|false) default false
4828  * 
4829  * @constructor
4830  * Create a new Element
4831  * @param {Object} config The config object
4832  */
4833
4834 Roo.bootstrap.Element = function(config){
4835     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4836     
4837     this.addEvents({
4838         // raw events
4839         /**
4840          * @event click
4841          * When a element is chick
4842          * @param {Roo.bootstrap.Element} this
4843          * @param {Roo.EventObject} e
4844          */
4845         "click" : true
4846     });
4847 };
4848
4849 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4850     
4851     tag: 'div',
4852     cls: '',
4853     html: '',
4854     preventDefault: false, 
4855     clickable: false,
4856     
4857     getAutoCreate : function(){
4858         
4859         var cfg = {
4860             tag: this.tag,
4861             cls: this.cls,
4862             html: this.html
4863         };
4864         
4865         return cfg;
4866     },
4867     
4868     initEvents: function() 
4869     {
4870         Roo.bootstrap.Element.superclass.initEvents.call(this);
4871         
4872         if(this.clickable){
4873             this.el.on('click', this.onClick, this);
4874         }
4875         
4876     },
4877     
4878     onClick : function(e)
4879     {
4880         if(this.preventDefault){
4881             e.preventDefault();
4882         }
4883         
4884         this.fireEvent('click', this, e);
4885     },
4886     
4887     getValue : function()
4888     {
4889         return this.el.dom.innerHTML;
4890     },
4891     
4892     setValue : function(value)
4893     {
4894         this.el.dom.innerHTML = value;
4895     }
4896    
4897 });
4898
4899  
4900
4901  /*
4902  * - LGPL
4903  *
4904  * pagination
4905  * 
4906  */
4907
4908 /**
4909  * @class Roo.bootstrap.Pagination
4910  * @extends Roo.bootstrap.Component
4911  * Bootstrap Pagination class
4912  * @cfg {String} size xs | sm | md | lg
4913  * @cfg {Boolean} inverse false | true
4914  * 
4915  * @constructor
4916  * Create a new Pagination
4917  * @param {Object} config The config object
4918  */
4919
4920 Roo.bootstrap.Pagination = function(config){
4921     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4922 };
4923
4924 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4925     
4926     cls: false,
4927     size: false,
4928     inverse: false,
4929     
4930     getAutoCreate : function(){
4931         var cfg = {
4932             tag: 'ul',
4933                 cls: 'pagination'
4934         };
4935         if (this.inverse) {
4936             cfg.cls += ' inverse';
4937         }
4938         if (this.html) {
4939             cfg.html=this.html;
4940         }
4941         if (this.cls) {
4942             cfg.cls += " " + this.cls;
4943         }
4944         return cfg;
4945     }
4946    
4947 });
4948
4949  
4950
4951  /*
4952  * - LGPL
4953  *
4954  * Pagination item
4955  * 
4956  */
4957
4958
4959 /**
4960  * @class Roo.bootstrap.PaginationItem
4961  * @extends Roo.bootstrap.Component
4962  * Bootstrap PaginationItem class
4963  * @cfg {String} html text
4964  * @cfg {String} href the link
4965  * @cfg {Boolean} preventDefault (true | false) default true
4966  * @cfg {Boolean} active (true | false) default false
4967  * @cfg {Boolean} disabled default false
4968  * 
4969  * 
4970  * @constructor
4971  * Create a new PaginationItem
4972  * @param {Object} config The config object
4973  */
4974
4975
4976 Roo.bootstrap.PaginationItem = function(config){
4977     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4978     this.addEvents({
4979         // raw events
4980         /**
4981          * @event click
4982          * The raw click event for the entire grid.
4983          * @param {Roo.EventObject} e
4984          */
4985         "click" : true
4986     });
4987 };
4988
4989 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4990     
4991     href : false,
4992     html : false,
4993     preventDefault: true,
4994     active : false,
4995     cls : false,
4996     disabled: false,
4997     
4998     getAutoCreate : function(){
4999         var cfg= {
5000             tag: 'li',
5001             cn: [
5002                 {
5003                     tag : 'a',
5004                     href : this.href ? this.href : '#',
5005                     html : this.html ? this.html : ''
5006                 }
5007             ]
5008         };
5009         
5010         if(this.cls){
5011             cfg.cls = this.cls;
5012         }
5013         
5014         if(this.disabled){
5015             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5016         }
5017         
5018         if(this.active){
5019             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5020         }
5021         
5022         return cfg;
5023     },
5024     
5025     initEvents: function() {
5026         
5027         this.el.on('click', this.onClick, this);
5028         
5029     },
5030     onClick : function(e)
5031     {
5032         Roo.log('PaginationItem on click ');
5033         if(this.preventDefault){
5034             e.preventDefault();
5035         }
5036         
5037         if(this.disabled){
5038             return;
5039         }
5040         
5041         this.fireEvent('click', this, e);
5042     }
5043    
5044 });
5045
5046  
5047
5048  /*
5049  * - LGPL
5050  *
5051  * slider
5052  * 
5053  */
5054
5055
5056 /**
5057  * @class Roo.bootstrap.Slider
5058  * @extends Roo.bootstrap.Component
5059  * Bootstrap Slider class
5060  *    
5061  * @constructor
5062  * Create a new Slider
5063  * @param {Object} config The config object
5064  */
5065
5066 Roo.bootstrap.Slider = function(config){
5067     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5068 };
5069
5070 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5071     
5072     getAutoCreate : function(){
5073         
5074         var cfg = {
5075             tag: 'div',
5076             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5077             cn: [
5078                 {
5079                     tag: 'a',
5080                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5081                 }
5082             ]
5083         };
5084         
5085         return cfg;
5086     }
5087    
5088 });
5089
5090  /*
5091  * Based on:
5092  * Ext JS Library 1.1.1
5093  * Copyright(c) 2006-2007, Ext JS, LLC.
5094  *
5095  * Originally Released Under LGPL - original licence link has changed is not relivant.
5096  *
5097  * Fork - LGPL
5098  * <script type="text/javascript">
5099  */
5100  
5101
5102 /**
5103  * @class Roo.grid.ColumnModel
5104  * @extends Roo.util.Observable
5105  * This is the default implementation of a ColumnModel used by the Grid. It defines
5106  * the columns in the grid.
5107  * <br>Usage:<br>
5108  <pre><code>
5109  var colModel = new Roo.grid.ColumnModel([
5110         {header: "Ticker", width: 60, sortable: true, locked: true},
5111         {header: "Company Name", width: 150, sortable: true},
5112         {header: "Market Cap.", width: 100, sortable: true},
5113         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5114         {header: "Employees", width: 100, sortable: true, resizable: false}
5115  ]);
5116  </code></pre>
5117  * <p>
5118  
5119  * The config options listed for this class are options which may appear in each
5120  * individual column definition.
5121  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5122  * @constructor
5123  * @param {Object} config An Array of column config objects. See this class's
5124  * config objects for details.
5125 */
5126 Roo.grid.ColumnModel = function(config){
5127         /**
5128      * The config passed into the constructor
5129      */
5130     this.config = config;
5131     this.lookup = {};
5132
5133     // if no id, create one
5134     // if the column does not have a dataIndex mapping,
5135     // map it to the order it is in the config
5136     for(var i = 0, len = config.length; i < len; i++){
5137         var c = config[i];
5138         if(typeof c.dataIndex == "undefined"){
5139             c.dataIndex = i;
5140         }
5141         if(typeof c.renderer == "string"){
5142             c.renderer = Roo.util.Format[c.renderer];
5143         }
5144         if(typeof c.id == "undefined"){
5145             c.id = Roo.id();
5146         }
5147         if(c.editor && c.editor.xtype){
5148             c.editor  = Roo.factory(c.editor, Roo.grid);
5149         }
5150         if(c.editor && c.editor.isFormField){
5151             c.editor = new Roo.grid.GridEditor(c.editor);
5152         }
5153         this.lookup[c.id] = c;
5154     }
5155
5156     /**
5157      * The width of columns which have no width specified (defaults to 100)
5158      * @type Number
5159      */
5160     this.defaultWidth = 100;
5161
5162     /**
5163      * Default sortable of columns which have no sortable specified (defaults to false)
5164      * @type Boolean
5165      */
5166     this.defaultSortable = false;
5167
5168     this.addEvents({
5169         /**
5170              * @event widthchange
5171              * Fires when the width of a column changes.
5172              * @param {ColumnModel} this
5173              * @param {Number} columnIndex The column index
5174              * @param {Number} newWidth The new width
5175              */
5176             "widthchange": true,
5177         /**
5178              * @event headerchange
5179              * Fires when the text of a header changes.
5180              * @param {ColumnModel} this
5181              * @param {Number} columnIndex The column index
5182              * @param {Number} newText The new header text
5183              */
5184             "headerchange": true,
5185         /**
5186              * @event hiddenchange
5187              * Fires when a column is hidden or "unhidden".
5188              * @param {ColumnModel} this
5189              * @param {Number} columnIndex The column index
5190              * @param {Boolean} hidden true if hidden, false otherwise
5191              */
5192             "hiddenchange": true,
5193             /**
5194          * @event columnmoved
5195          * Fires when a column is moved.
5196          * @param {ColumnModel} this
5197          * @param {Number} oldIndex
5198          * @param {Number} newIndex
5199          */
5200         "columnmoved" : true,
5201         /**
5202          * @event columlockchange
5203          * Fires when a column's locked state is changed
5204          * @param {ColumnModel} this
5205          * @param {Number} colIndex
5206          * @param {Boolean} locked true if locked
5207          */
5208         "columnlockchange" : true
5209     });
5210     Roo.grid.ColumnModel.superclass.constructor.call(this);
5211 };
5212 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5213     /**
5214      * @cfg {String} header The header text to display in the Grid view.
5215      */
5216     /**
5217      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5218      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5219      * specified, the column's index is used as an index into the Record's data Array.
5220      */
5221     /**
5222      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5223      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5224      */
5225     /**
5226      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5227      * Defaults to the value of the {@link #defaultSortable} property.
5228      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5229      */
5230     /**
5231      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5232      */
5233     /**
5234      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5235      */
5236     /**
5237      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5238      */
5239     /**
5240      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5241      */
5242     /**
5243      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5244      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5245      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5246      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5247      */
5248        /**
5249      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5250      */
5251     /**
5252      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5253      */
5254     /**
5255      * @cfg {String} cursor (Optional)
5256      */
5257     /**
5258      * @cfg {String} tooltip (Optional)
5259      */
5260     /**
5261      * @cfg {Number} xs (Optional)
5262      */
5263     /**
5264      * @cfg {Number} sm (Optional)
5265      */
5266     /**
5267      * @cfg {Number} md (Optional)
5268      */
5269     /**
5270      * @cfg {Number} lg (Optional)
5271      */
5272     /**
5273      * Returns the id of the column at the specified index.
5274      * @param {Number} index The column index
5275      * @return {String} the id
5276      */
5277     getColumnId : function(index){
5278         return this.config[index].id;
5279     },
5280
5281     /**
5282      * Returns the column for a specified id.
5283      * @param {String} id The column id
5284      * @return {Object} the column
5285      */
5286     getColumnById : function(id){
5287         return this.lookup[id];
5288     },
5289
5290     
5291     /**
5292      * Returns the column for a specified dataIndex.
5293      * @param {String} dataIndex The column dataIndex
5294      * @return {Object|Boolean} the column or false if not found
5295      */
5296     getColumnByDataIndex: function(dataIndex){
5297         var index = this.findColumnIndex(dataIndex);
5298         return index > -1 ? this.config[index] : false;
5299     },
5300     
5301     /**
5302      * Returns the index for a specified column id.
5303      * @param {String} id The column id
5304      * @return {Number} the index, or -1 if not found
5305      */
5306     getIndexById : function(id){
5307         for(var i = 0, len = this.config.length; i < len; i++){
5308             if(this.config[i].id == id){
5309                 return i;
5310             }
5311         }
5312         return -1;
5313     },
5314     
5315     /**
5316      * Returns the index for a specified column dataIndex.
5317      * @param {String} dataIndex The column dataIndex
5318      * @return {Number} the index, or -1 if not found
5319      */
5320     
5321     findColumnIndex : function(dataIndex){
5322         for(var i = 0, len = this.config.length; i < len; i++){
5323             if(this.config[i].dataIndex == dataIndex){
5324                 return i;
5325             }
5326         }
5327         return -1;
5328     },
5329     
5330     
5331     moveColumn : function(oldIndex, newIndex){
5332         var c = this.config[oldIndex];
5333         this.config.splice(oldIndex, 1);
5334         this.config.splice(newIndex, 0, c);
5335         this.dataMap = null;
5336         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5337     },
5338
5339     isLocked : function(colIndex){
5340         return this.config[colIndex].locked === true;
5341     },
5342
5343     setLocked : function(colIndex, value, suppressEvent){
5344         if(this.isLocked(colIndex) == value){
5345             return;
5346         }
5347         this.config[colIndex].locked = value;
5348         if(!suppressEvent){
5349             this.fireEvent("columnlockchange", this, colIndex, value);
5350         }
5351     },
5352
5353     getTotalLockedWidth : function(){
5354         var totalWidth = 0;
5355         for(var i = 0; i < this.config.length; i++){
5356             if(this.isLocked(i) && !this.isHidden(i)){
5357                 this.totalWidth += this.getColumnWidth(i);
5358             }
5359         }
5360         return totalWidth;
5361     },
5362
5363     getLockedCount : function(){
5364         for(var i = 0, len = this.config.length; i < len; i++){
5365             if(!this.isLocked(i)){
5366                 return i;
5367             }
5368         }
5369         
5370         return this.config.length;
5371     },
5372
5373     /**
5374      * Returns the number of columns.
5375      * @return {Number}
5376      */
5377     getColumnCount : function(visibleOnly){
5378         if(visibleOnly === true){
5379             var c = 0;
5380             for(var i = 0, len = this.config.length; i < len; i++){
5381                 if(!this.isHidden(i)){
5382                     c++;
5383                 }
5384             }
5385             return c;
5386         }
5387         return this.config.length;
5388     },
5389
5390     /**
5391      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5392      * @param {Function} fn
5393      * @param {Object} scope (optional)
5394      * @return {Array} result
5395      */
5396     getColumnsBy : function(fn, scope){
5397         var r = [];
5398         for(var i = 0, len = this.config.length; i < len; i++){
5399             var c = this.config[i];
5400             if(fn.call(scope||this, c, i) === true){
5401                 r[r.length] = c;
5402             }
5403         }
5404         return r;
5405     },
5406
5407     /**
5408      * Returns true if the specified column is sortable.
5409      * @param {Number} col The column index
5410      * @return {Boolean}
5411      */
5412     isSortable : function(col){
5413         if(typeof this.config[col].sortable == "undefined"){
5414             return this.defaultSortable;
5415         }
5416         return this.config[col].sortable;
5417     },
5418
5419     /**
5420      * Returns the rendering (formatting) function defined for the column.
5421      * @param {Number} col The column index.
5422      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5423      */
5424     getRenderer : function(col){
5425         if(!this.config[col].renderer){
5426             return Roo.grid.ColumnModel.defaultRenderer;
5427         }
5428         return this.config[col].renderer;
5429     },
5430
5431     /**
5432      * Sets the rendering (formatting) function for a column.
5433      * @param {Number} col The column index
5434      * @param {Function} fn The function to use to process the cell's raw data
5435      * to return HTML markup for the grid view. The render function is called with
5436      * the following parameters:<ul>
5437      * <li>Data value.</li>
5438      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5439      * <li>css A CSS style string to apply to the table cell.</li>
5440      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5441      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5442      * <li>Row index</li>
5443      * <li>Column index</li>
5444      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5445      */
5446     setRenderer : function(col, fn){
5447         this.config[col].renderer = fn;
5448     },
5449
5450     /**
5451      * Returns the width for the specified column.
5452      * @param {Number} col The column index
5453      * @return {Number}
5454      */
5455     getColumnWidth : function(col){
5456         return this.config[col].width * 1 || this.defaultWidth;
5457     },
5458
5459     /**
5460      * Sets the width for a column.
5461      * @param {Number} col The column index
5462      * @param {Number} width The new width
5463      */
5464     setColumnWidth : function(col, width, suppressEvent){
5465         this.config[col].width = width;
5466         this.totalWidth = null;
5467         if(!suppressEvent){
5468              this.fireEvent("widthchange", this, col, width);
5469         }
5470     },
5471
5472     /**
5473      * Returns the total width of all columns.
5474      * @param {Boolean} includeHidden True to include hidden column widths
5475      * @return {Number}
5476      */
5477     getTotalWidth : function(includeHidden){
5478         if(!this.totalWidth){
5479             this.totalWidth = 0;
5480             for(var i = 0, len = this.config.length; i < len; i++){
5481                 if(includeHidden || !this.isHidden(i)){
5482                     this.totalWidth += this.getColumnWidth(i);
5483                 }
5484             }
5485         }
5486         return this.totalWidth;
5487     },
5488
5489     /**
5490      * Returns the header for the specified column.
5491      * @param {Number} col The column index
5492      * @return {String}
5493      */
5494     getColumnHeader : function(col){
5495         return this.config[col].header;
5496     },
5497
5498     /**
5499      * Sets the header for a column.
5500      * @param {Number} col The column index
5501      * @param {String} header The new header
5502      */
5503     setColumnHeader : function(col, header){
5504         this.config[col].header = header;
5505         this.fireEvent("headerchange", this, col, header);
5506     },
5507
5508     /**
5509      * Returns the tooltip for the specified column.
5510      * @param {Number} col The column index
5511      * @return {String}
5512      */
5513     getColumnTooltip : function(col){
5514             return this.config[col].tooltip;
5515     },
5516     /**
5517      * Sets the tooltip for a column.
5518      * @param {Number} col The column index
5519      * @param {String} tooltip The new tooltip
5520      */
5521     setColumnTooltip : function(col, tooltip){
5522             this.config[col].tooltip = tooltip;
5523     },
5524
5525     /**
5526      * Returns the dataIndex for the specified column.
5527      * @param {Number} col The column index
5528      * @return {Number}
5529      */
5530     getDataIndex : function(col){
5531         return this.config[col].dataIndex;
5532     },
5533
5534     /**
5535      * Sets the dataIndex for a column.
5536      * @param {Number} col The column index
5537      * @param {Number} dataIndex The new dataIndex
5538      */
5539     setDataIndex : function(col, dataIndex){
5540         this.config[col].dataIndex = dataIndex;
5541     },
5542
5543     
5544     
5545     /**
5546      * Returns true if the cell is editable.
5547      * @param {Number} colIndex The column index
5548      * @param {Number} rowIndex The row index - this is nto actually used..?
5549      * @return {Boolean}
5550      */
5551     isCellEditable : function(colIndex, rowIndex){
5552         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5553     },
5554
5555     /**
5556      * Returns the editor defined for the cell/column.
5557      * return false or null to disable editing.
5558      * @param {Number} colIndex The column index
5559      * @param {Number} rowIndex The row index
5560      * @return {Object}
5561      */
5562     getCellEditor : function(colIndex, rowIndex){
5563         return this.config[colIndex].editor;
5564     },
5565
5566     /**
5567      * Sets if a column is editable.
5568      * @param {Number} col The column index
5569      * @param {Boolean} editable True if the column is editable
5570      */
5571     setEditable : function(col, editable){
5572         this.config[col].editable = editable;
5573     },
5574
5575
5576     /**
5577      * Returns true if the column is hidden.
5578      * @param {Number} colIndex The column index
5579      * @return {Boolean}
5580      */
5581     isHidden : function(colIndex){
5582         return this.config[colIndex].hidden;
5583     },
5584
5585
5586     /**
5587      * Returns true if the column width cannot be changed
5588      */
5589     isFixed : function(colIndex){
5590         return this.config[colIndex].fixed;
5591     },
5592
5593     /**
5594      * Returns true if the column can be resized
5595      * @return {Boolean}
5596      */
5597     isResizable : function(colIndex){
5598         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5599     },
5600     /**
5601      * Sets if a column is hidden.
5602      * @param {Number} colIndex The column index
5603      * @param {Boolean} hidden True if the column is hidden
5604      */
5605     setHidden : function(colIndex, hidden){
5606         this.config[colIndex].hidden = hidden;
5607         this.totalWidth = null;
5608         this.fireEvent("hiddenchange", this, colIndex, hidden);
5609     },
5610
5611     /**
5612      * Sets the editor for a column.
5613      * @param {Number} col The column index
5614      * @param {Object} editor The editor object
5615      */
5616     setEditor : function(col, editor){
5617         this.config[col].editor = editor;
5618     }
5619 });
5620
5621 Roo.grid.ColumnModel.defaultRenderer = function(value)
5622 {
5623     if(typeof value == "object") {
5624         return value;
5625     }
5626         if(typeof value == "string" && value.length < 1){
5627             return "&#160;";
5628         }
5629     
5630         return String.format("{0}", value);
5631 };
5632
5633 // Alias for backwards compatibility
5634 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5635 /*
5636  * Based on:
5637  * Ext JS Library 1.1.1
5638  * Copyright(c) 2006-2007, Ext JS, LLC.
5639  *
5640  * Originally Released Under LGPL - original licence link has changed is not relivant.
5641  *
5642  * Fork - LGPL
5643  * <script type="text/javascript">
5644  */
5645  
5646 /**
5647  * @class Roo.LoadMask
5648  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5649  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5650  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5651  * element's UpdateManager load indicator and will be destroyed after the initial load.
5652  * @constructor
5653  * Create a new LoadMask
5654  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5655  * @param {Object} config The config object
5656  */
5657 Roo.LoadMask = function(el, config){
5658     this.el = Roo.get(el);
5659     Roo.apply(this, config);
5660     if(this.store){
5661         this.store.on('beforeload', this.onBeforeLoad, this);
5662         this.store.on('load', this.onLoad, this);
5663         this.store.on('loadexception', this.onLoadException, this);
5664         this.removeMask = false;
5665     }else{
5666         var um = this.el.getUpdateManager();
5667         um.showLoadIndicator = false; // disable the default indicator
5668         um.on('beforeupdate', this.onBeforeLoad, this);
5669         um.on('update', this.onLoad, this);
5670         um.on('failure', this.onLoad, this);
5671         this.removeMask = true;
5672     }
5673 };
5674
5675 Roo.LoadMask.prototype = {
5676     /**
5677      * @cfg {Boolean} removeMask
5678      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5679      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5680      */
5681     /**
5682      * @cfg {String} msg
5683      * The text to display in a centered loading message box (defaults to 'Loading...')
5684      */
5685     msg : 'Loading...',
5686     /**
5687      * @cfg {String} msgCls
5688      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5689      */
5690     msgCls : 'x-mask-loading',
5691
5692     /**
5693      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5694      * @type Boolean
5695      */
5696     disabled: false,
5697
5698     /**
5699      * Disables the mask to prevent it from being displayed
5700      */
5701     disable : function(){
5702        this.disabled = true;
5703     },
5704
5705     /**
5706      * Enables the mask so that it can be displayed
5707      */
5708     enable : function(){
5709         this.disabled = false;
5710     },
5711     
5712     onLoadException : function()
5713     {
5714         Roo.log(arguments);
5715         
5716         if (typeof(arguments[3]) != 'undefined') {
5717             Roo.MessageBox.alert("Error loading",arguments[3]);
5718         } 
5719         /*
5720         try {
5721             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5722                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5723             }   
5724         } catch(e) {
5725             
5726         }
5727         */
5728     
5729         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5730     },
5731     // private
5732     onLoad : function()
5733     {
5734         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5735     },
5736
5737     // private
5738     onBeforeLoad : function(){
5739         if(!this.disabled){
5740             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5741         }
5742     },
5743
5744     // private
5745     destroy : function(){
5746         if(this.store){
5747             this.store.un('beforeload', this.onBeforeLoad, this);
5748             this.store.un('load', this.onLoad, this);
5749             this.store.un('loadexception', this.onLoadException, this);
5750         }else{
5751             var um = this.el.getUpdateManager();
5752             um.un('beforeupdate', this.onBeforeLoad, this);
5753             um.un('update', this.onLoad, this);
5754             um.un('failure', this.onLoad, this);
5755         }
5756     }
5757 };/*
5758  * - LGPL
5759  *
5760  * table
5761  * 
5762  */
5763
5764 /**
5765  * @class Roo.bootstrap.Table
5766  * @extends Roo.bootstrap.Component
5767  * Bootstrap Table class
5768  * @cfg {String} cls table class
5769  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5770  * @cfg {String} bgcolor Specifies the background color for a table
5771  * @cfg {Number} border Specifies whether the table cells should have borders or not
5772  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5773  * @cfg {Number} cellspacing Specifies the space between cells
5774  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5775  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5776  * @cfg {String} sortable Specifies that the table should be sortable
5777  * @cfg {String} summary Specifies a summary of the content of a table
5778  * @cfg {Number} width Specifies the width of a table
5779  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5780  * 
5781  * @cfg {boolean} striped Should the rows be alternative striped
5782  * @cfg {boolean} bordered Add borders to the table
5783  * @cfg {boolean} hover Add hover highlighting
5784  * @cfg {boolean} condensed Format condensed
5785  * @cfg {boolean} responsive Format condensed
5786  * @cfg {Boolean} loadMask (true|false) default false
5787  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5788  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5789  * @cfg {Boolean} rowSelection (true|false) default false
5790  * @cfg {Boolean} cellSelection (true|false) default false
5791  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5792  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5793  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5794  
5795  * 
5796  * @constructor
5797  * Create a new Table
5798  * @param {Object} config The config object
5799  */
5800
5801 Roo.bootstrap.Table = function(config){
5802     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5803     
5804   
5805     
5806     // BC...
5807     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5808     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5809     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5810     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5811     
5812     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5813     if (this.sm) {
5814         this.sm.grid = this;
5815         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5816         this.sm = this.selModel;
5817         this.sm.xmodule = this.xmodule || false;
5818     }
5819     
5820     if (this.cm && typeof(this.cm.config) == 'undefined') {
5821         this.colModel = new Roo.grid.ColumnModel(this.cm);
5822         this.cm = this.colModel;
5823         this.cm.xmodule = this.xmodule || false;
5824     }
5825     if (this.store) {
5826         this.store= Roo.factory(this.store, Roo.data);
5827         this.ds = this.store;
5828         this.ds.xmodule = this.xmodule || false;
5829          
5830     }
5831     if (this.footer && this.store) {
5832         this.footer.dataSource = this.ds;
5833         this.footer = Roo.factory(this.footer);
5834     }
5835     
5836     /** @private */
5837     this.addEvents({
5838         /**
5839          * @event cellclick
5840          * Fires when a cell is clicked
5841          * @param {Roo.bootstrap.Table} this
5842          * @param {Roo.Element} el
5843          * @param {Number} rowIndex
5844          * @param {Number} columnIndex
5845          * @param {Roo.EventObject} e
5846          */
5847         "cellclick" : true,
5848         /**
5849          * @event celldblclick
5850          * Fires when a cell is double clicked
5851          * @param {Roo.bootstrap.Table} this
5852          * @param {Roo.Element} el
5853          * @param {Number} rowIndex
5854          * @param {Number} columnIndex
5855          * @param {Roo.EventObject} e
5856          */
5857         "celldblclick" : true,
5858         /**
5859          * @event rowclick
5860          * Fires when a row is clicked
5861          * @param {Roo.bootstrap.Table} this
5862          * @param {Roo.Element} el
5863          * @param {Number} rowIndex
5864          * @param {Roo.EventObject} e
5865          */
5866         "rowclick" : true,
5867         /**
5868          * @event rowdblclick
5869          * Fires when a row is double clicked
5870          * @param {Roo.bootstrap.Table} this
5871          * @param {Roo.Element} el
5872          * @param {Number} rowIndex
5873          * @param {Roo.EventObject} e
5874          */
5875         "rowdblclick" : true,
5876         /**
5877          * @event mouseover
5878          * Fires when a mouseover occur
5879          * @param {Roo.bootstrap.Table} this
5880          * @param {Roo.Element} el
5881          * @param {Number} rowIndex
5882          * @param {Number} columnIndex
5883          * @param {Roo.EventObject} e
5884          */
5885         "mouseover" : true,
5886         /**
5887          * @event mouseout
5888          * Fires when a mouseout occur
5889          * @param {Roo.bootstrap.Table} this
5890          * @param {Roo.Element} el
5891          * @param {Number} rowIndex
5892          * @param {Number} columnIndex
5893          * @param {Roo.EventObject} e
5894          */
5895         "mouseout" : true,
5896         /**
5897          * @event rowclass
5898          * Fires when a row is rendered, so you can change add a style to it.
5899          * @param {Roo.bootstrap.Table} this
5900          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5901          */
5902         'rowclass' : true,
5903           /**
5904          * @event rowsrendered
5905          * Fires when all the  rows have been rendered
5906          * @param {Roo.bootstrap.Table} this
5907          */
5908         'rowsrendered' : true,
5909         /**
5910          * @event contextmenu
5911          * The raw contextmenu event for the entire grid.
5912          * @param {Roo.EventObject} e
5913          */
5914         "contextmenu" : true,
5915         /**
5916          * @event rowcontextmenu
5917          * Fires when a row is right clicked
5918          * @param {Roo.bootstrap.Table} this
5919          * @param {Number} rowIndex
5920          * @param {Roo.EventObject} e
5921          */
5922         "rowcontextmenu" : true,
5923         /**
5924          * @event cellcontextmenu
5925          * Fires when a cell is right clicked
5926          * @param {Roo.bootstrap.Table} this
5927          * @param {Number} rowIndex
5928          * @param {Number} cellIndex
5929          * @param {Roo.EventObject} e
5930          */
5931          "cellcontextmenu" : true,
5932          /**
5933          * @event headercontextmenu
5934          * Fires when a header is right clicked
5935          * @param {Roo.bootstrap.Table} this
5936          * @param {Number} columnIndex
5937          * @param {Roo.EventObject} e
5938          */
5939         "headercontextmenu" : true
5940     });
5941 };
5942
5943 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5944     
5945     cls: false,
5946     align: false,
5947     bgcolor: false,
5948     border: false,
5949     cellpadding: false,
5950     cellspacing: false,
5951     frame: false,
5952     rules: false,
5953     sortable: false,
5954     summary: false,
5955     width: false,
5956     striped : false,
5957     scrollBody : false,
5958     bordered: false,
5959     hover:  false,
5960     condensed : false,
5961     responsive : false,
5962     sm : false,
5963     cm : false,
5964     store : false,
5965     loadMask : false,
5966     footerShow : true,
5967     headerShow : true,
5968   
5969     rowSelection : false,
5970     cellSelection : false,
5971     layout : false,
5972     
5973     // Roo.Element - the tbody
5974     mainBody: false,
5975     // Roo.Element - thead element
5976     mainHead: false,
5977     
5978     container: false, // used by gridpanel...
5979     
5980     lazyLoad : false,
5981     
5982     getAutoCreate : function()
5983     {
5984         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5985         
5986         cfg = {
5987             tag: 'table',
5988             cls : 'table',
5989             cn : []
5990         };
5991         if (this.scrollBody) {
5992             cfg.cls += ' table-body-fixed';
5993         }    
5994         if (this.striped) {
5995             cfg.cls += ' table-striped';
5996         }
5997         
5998         if (this.hover) {
5999             cfg.cls += ' table-hover';
6000         }
6001         if (this.bordered) {
6002             cfg.cls += ' table-bordered';
6003         }
6004         if (this.condensed) {
6005             cfg.cls += ' table-condensed';
6006         }
6007         if (this.responsive) {
6008             cfg.cls += ' table-responsive';
6009         }
6010         
6011         if (this.cls) {
6012             cfg.cls+=  ' ' +this.cls;
6013         }
6014         
6015         // this lot should be simplifed...
6016         
6017         if (this.align) {
6018             cfg.align=this.align;
6019         }
6020         if (this.bgcolor) {
6021             cfg.bgcolor=this.bgcolor;
6022         }
6023         if (this.border) {
6024             cfg.border=this.border;
6025         }
6026         if (this.cellpadding) {
6027             cfg.cellpadding=this.cellpadding;
6028         }
6029         if (this.cellspacing) {
6030             cfg.cellspacing=this.cellspacing;
6031         }
6032         if (this.frame) {
6033             cfg.frame=this.frame;
6034         }
6035         if (this.rules) {
6036             cfg.rules=this.rules;
6037         }
6038         if (this.sortable) {
6039             cfg.sortable=this.sortable;
6040         }
6041         if (this.summary) {
6042             cfg.summary=this.summary;
6043         }
6044         if (this.width) {
6045             cfg.width=this.width;
6046         }
6047         if (this.layout) {
6048             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6049         }
6050         
6051         if(this.store || this.cm){
6052             if(this.headerShow){
6053                 cfg.cn.push(this.renderHeader());
6054             }
6055             
6056             cfg.cn.push(this.renderBody());
6057             
6058             if(this.footerShow){
6059                 cfg.cn.push(this.renderFooter());
6060             }
6061             // where does this come from?
6062             //cfg.cls+=  ' TableGrid';
6063         }
6064         
6065         return { cn : [ cfg ] };
6066     },
6067     
6068     initEvents : function()
6069     {   
6070         if(!this.store || !this.cm){
6071             return;
6072         }
6073         if (this.selModel) {
6074             this.selModel.initEvents();
6075         }
6076         
6077         
6078         //Roo.log('initEvents with ds!!!!');
6079         
6080         this.mainBody = this.el.select('tbody', true).first();
6081         this.mainHead = this.el.select('thead', true).first();
6082         
6083         
6084         
6085         
6086         var _this = this;
6087         
6088         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6089             e.on('click', _this.sort, _this);
6090         });
6091         
6092         this.mainBody.on("click", this.onClick, this);
6093         this.mainBody.on("dblclick", this.onDblClick, this);
6094         
6095         // why is this done????? = it breaks dialogs??
6096         //this.parent().el.setStyle('position', 'relative');
6097         
6098         
6099         if (this.footer) {
6100             this.footer.parentId = this.id;
6101             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6102             
6103             if(this.lazyLoad){
6104                 this.el.select('tfoot tr td').first().addClass('hide');
6105             }
6106         } 
6107         
6108         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6109         
6110         this.store.on('load', this.onLoad, this);
6111         this.store.on('beforeload', this.onBeforeLoad, this);
6112         this.store.on('update', this.onUpdate, this);
6113         this.store.on('add', this.onAdd, this);
6114         this.store.on("clear", this.clear, this);
6115         
6116         this.el.on("contextmenu", this.onContextMenu, this);
6117         
6118         this.mainBody.on('scroll', this.onBodyScroll, this);
6119         
6120         
6121     },
6122     
6123     onContextMenu : function(e, t)
6124     {
6125         this.processEvent("contextmenu", e);
6126     },
6127     
6128     processEvent : function(name, e)
6129     {
6130         if (name != 'touchstart' ) {
6131             this.fireEvent(name, e);    
6132         }
6133         
6134         var t = e.getTarget();
6135         
6136         var cell = Roo.get(t);
6137         
6138         if(!cell){
6139             return;
6140         }
6141         
6142         if(cell.findParent('tfoot', false, true)){
6143             return;
6144         }
6145         
6146         if(cell.findParent('thead', false, true)){
6147             
6148             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6149                 cell = Roo.get(t).findParent('th', false, true);
6150                 if (!cell) {
6151                     Roo.log("failed to find th in thead?");
6152                     Roo.log(e.getTarget());
6153                     return;
6154                 }
6155             }
6156             
6157             var cellIndex = cell.dom.cellIndex;
6158             
6159             var ename = name == 'touchstart' ? 'click' : name;
6160             this.fireEvent("header" + ename, this, cellIndex, e);
6161             
6162             return;
6163         }
6164         
6165         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6166             cell = Roo.get(t).findParent('td', false, true);
6167             if (!cell) {
6168                 Roo.log("failed to find th in tbody?");
6169                 Roo.log(e.getTarget());
6170                 return;
6171             }
6172         }
6173         
6174         var row = cell.findParent('tr', false, true);
6175         var cellIndex = cell.dom.cellIndex;
6176         var rowIndex = row.dom.rowIndex - 1;
6177         
6178         if(row !== false){
6179             
6180             this.fireEvent("row" + name, this, rowIndex, e);
6181             
6182             if(cell !== false){
6183             
6184                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6185             }
6186         }
6187         
6188     },
6189     
6190     onMouseover : function(e, el)
6191     {
6192         var cell = Roo.get(el);
6193         
6194         if(!cell){
6195             return;
6196         }
6197         
6198         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6199             cell = cell.findParent('td', false, true);
6200         }
6201         
6202         var row = cell.findParent('tr', false, true);
6203         var cellIndex = cell.dom.cellIndex;
6204         var rowIndex = row.dom.rowIndex - 1; // start from 0
6205         
6206         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6207         
6208     },
6209     
6210     onMouseout : function(e, el)
6211     {
6212         var cell = Roo.get(el);
6213         
6214         if(!cell){
6215             return;
6216         }
6217         
6218         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6219             cell = cell.findParent('td', false, true);
6220         }
6221         
6222         var row = cell.findParent('tr', false, true);
6223         var cellIndex = cell.dom.cellIndex;
6224         var rowIndex = row.dom.rowIndex - 1; // start from 0
6225         
6226         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6227         
6228     },
6229     
6230     onClick : function(e, el)
6231     {
6232         var cell = Roo.get(el);
6233         
6234         if(!cell || (!this.cellSelection && !this.rowSelection)){
6235             return;
6236         }
6237         
6238         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6239             cell = cell.findParent('td', false, true);
6240         }
6241         
6242         if(!cell || typeof(cell) == 'undefined'){
6243             return;
6244         }
6245         
6246         var row = cell.findParent('tr', false, true);
6247         
6248         if(!row || typeof(row) == 'undefined'){
6249             return;
6250         }
6251         
6252         var cellIndex = cell.dom.cellIndex;
6253         var rowIndex = this.getRowIndex(row);
6254         
6255         // why??? - should these not be based on SelectionModel?
6256         if(this.cellSelection){
6257             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6258         }
6259         
6260         if(this.rowSelection){
6261             this.fireEvent('rowclick', this, row, rowIndex, e);
6262         }
6263         
6264         
6265     },
6266         
6267     onDblClick : function(e,el)
6268     {
6269         var cell = Roo.get(el);
6270         
6271         if(!cell || (!this.cellSelection && !this.rowSelection)){
6272             return;
6273         }
6274         
6275         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6276             cell = cell.findParent('td', false, true);
6277         }
6278         
6279         if(!cell || typeof(cell) == 'undefined'){
6280             return;
6281         }
6282         
6283         var row = cell.findParent('tr', false, true);
6284         
6285         if(!row || typeof(row) == 'undefined'){
6286             return;
6287         }
6288         
6289         var cellIndex = cell.dom.cellIndex;
6290         var rowIndex = this.getRowIndex(row);
6291         
6292         if(this.cellSelection){
6293             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6294         }
6295         
6296         if(this.rowSelection){
6297             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6298         }
6299     },
6300     
6301     sort : function(e,el)
6302     {
6303         var col = Roo.get(el);
6304         
6305         if(!col.hasClass('sortable')){
6306             return;
6307         }
6308         
6309         var sort = col.attr('sort');
6310         var dir = 'ASC';
6311         
6312         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6313             dir = 'DESC';
6314         }
6315         
6316         this.store.sortInfo = {field : sort, direction : dir};
6317         
6318         if (this.footer) {
6319             Roo.log("calling footer first");
6320             this.footer.onClick('first');
6321         } else {
6322         
6323             this.store.load({ params : { start : 0 } });
6324         }
6325     },
6326     
6327     renderHeader : function()
6328     {
6329         var header = {
6330             tag: 'thead',
6331             cn : []
6332         };
6333         
6334         var cm = this.cm;
6335         this.totalWidth = 0;
6336         
6337         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6338             
6339             var config = cm.config[i];
6340             
6341             var c = {
6342                 tag: 'th',
6343                 style : '',
6344                 html: cm.getColumnHeader(i)
6345             };
6346             
6347             var hh = '';
6348             
6349             if(typeof(config.sortable) != 'undefined' && config.sortable){
6350                 c.cls = 'sortable';
6351                 c.html = '<i class="glyphicon"></i>' + c.html;
6352             }
6353             
6354             if(typeof(config.lgHeader) != 'undefined'){
6355                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6356             }
6357             
6358             if(typeof(config.mdHeader) != 'undefined'){
6359                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6360             }
6361             
6362             if(typeof(config.smHeader) != 'undefined'){
6363                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6364             }
6365             
6366             if(typeof(config.xsHeader) != 'undefined'){
6367                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6368             }
6369             
6370             if(hh.length){
6371                 c.html = hh;
6372             }
6373             
6374             if(typeof(config.tooltip) != 'undefined'){
6375                 c.tooltip = config.tooltip;
6376             }
6377             
6378             if(typeof(config.colspan) != 'undefined'){
6379                 c.colspan = config.colspan;
6380             }
6381             
6382             if(typeof(config.hidden) != 'undefined' && config.hidden){
6383                 c.style += ' display:none;';
6384             }
6385             
6386             if(typeof(config.dataIndex) != 'undefined'){
6387                 c.sort = config.dataIndex;
6388             }
6389             
6390            
6391             
6392             if(typeof(config.align) != 'undefined' && config.align.length){
6393                 c.style += ' text-align:' + config.align + ';';
6394             }
6395             
6396             if(typeof(config.width) != 'undefined'){
6397                 c.style += ' width:' + config.width + 'px;';
6398                 this.totalWidth += config.width;
6399             } else {
6400                 this.totalWidth += 100; // assume minimum of 100 per column?
6401             }
6402             
6403             if(typeof(config.cls) != 'undefined'){
6404                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6405             }
6406             
6407             ['xs','sm','md','lg'].map(function(size){
6408                 
6409                 if(typeof(config[size]) == 'undefined'){
6410                     return;
6411                 }
6412                 
6413                 if (!config[size]) { // 0 = hidden
6414                     c.cls += ' hidden-' + size;
6415                     return;
6416                 }
6417                 
6418                 c.cls += ' col-' + size + '-' + config[size];
6419
6420             });
6421             
6422             header.cn.push(c)
6423         }
6424         
6425         return header;
6426     },
6427     
6428     renderBody : function()
6429     {
6430         var body = {
6431             tag: 'tbody',
6432             cn : [
6433                 {
6434                     tag: 'tr',
6435                     cn : [
6436                         {
6437                             tag : 'td',
6438                             colspan :  this.cm.getColumnCount()
6439                         }
6440                     ]
6441                 }
6442             ]
6443         };
6444         
6445         return body;
6446     },
6447     
6448     renderFooter : function()
6449     {
6450         var footer = {
6451             tag: 'tfoot',
6452             cn : [
6453                 {
6454                     tag: 'tr',
6455                     cn : [
6456                         {
6457                             tag : 'td',
6458                             colspan :  this.cm.getColumnCount()
6459                         }
6460                     ]
6461                 }
6462             ]
6463         };
6464         
6465         return footer;
6466     },
6467     
6468     
6469     
6470     onLoad : function()
6471     {
6472 //        Roo.log('ds onload');
6473         this.clear();
6474         
6475         var _this = this;
6476         var cm = this.cm;
6477         var ds = this.store;
6478         
6479         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6480             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6481             if (_this.store.sortInfo) {
6482                     
6483                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6484                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6485                 }
6486                 
6487                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6488                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6489                 }
6490             }
6491         });
6492         
6493         var tbody =  this.mainBody;
6494               
6495         if(ds.getCount() > 0){
6496             ds.data.each(function(d,rowIndex){
6497                 var row =  this.renderRow(cm, ds, rowIndex);
6498                 
6499                 tbody.createChild(row);
6500                 
6501                 var _this = this;
6502                 
6503                 if(row.cellObjects.length){
6504                     Roo.each(row.cellObjects, function(r){
6505                         _this.renderCellObject(r);
6506                     })
6507                 }
6508                 
6509             }, this);
6510         }
6511         
6512         Roo.each(this.el.select('tbody td', true).elements, function(e){
6513             e.on('mouseover', _this.onMouseover, _this);
6514         });
6515         
6516         Roo.each(this.el.select('tbody td', true).elements, function(e){
6517             e.on('mouseout', _this.onMouseout, _this);
6518         });
6519         this.fireEvent('rowsrendered', this);
6520         //if(this.loadMask){
6521         //    this.maskEl.hide();
6522         //}
6523         
6524         this.autoSize();
6525     },
6526     
6527     
6528     onUpdate : function(ds,record)
6529     {
6530         this.refreshRow(record);
6531         this.autoSize();
6532     },
6533     
6534     onRemove : function(ds, record, index, isUpdate){
6535         if(isUpdate !== true){
6536             this.fireEvent("beforerowremoved", this, index, record);
6537         }
6538         var bt = this.mainBody.dom;
6539         
6540         var rows = this.el.select('tbody > tr', true).elements;
6541         
6542         if(typeof(rows[index]) != 'undefined'){
6543             bt.removeChild(rows[index].dom);
6544         }
6545         
6546 //        if(bt.rows[index]){
6547 //            bt.removeChild(bt.rows[index]);
6548 //        }
6549         
6550         if(isUpdate !== true){
6551             //this.stripeRows(index);
6552             //this.syncRowHeights(index, index);
6553             //this.layout();
6554             this.fireEvent("rowremoved", this, index, record);
6555         }
6556     },
6557     
6558     onAdd : function(ds, records, rowIndex)
6559     {
6560         //Roo.log('on Add called');
6561         // - note this does not handle multiple adding very well..
6562         var bt = this.mainBody.dom;
6563         for (var i =0 ; i < records.length;i++) {
6564             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6565             //Roo.log(records[i]);
6566             //Roo.log(this.store.getAt(rowIndex+i));
6567             this.insertRow(this.store, rowIndex + i, false);
6568             return;
6569         }
6570         
6571     },
6572     
6573     
6574     refreshRow : function(record){
6575         var ds = this.store, index;
6576         if(typeof record == 'number'){
6577             index = record;
6578             record = ds.getAt(index);
6579         }else{
6580             index = ds.indexOf(record);
6581         }
6582         this.insertRow(ds, index, true);
6583         this.autoSize();
6584         this.onRemove(ds, record, index+1, true);
6585         this.autoSize();
6586         //this.syncRowHeights(index, index);
6587         //this.layout();
6588         this.fireEvent("rowupdated", this, index, record);
6589     },
6590     
6591     insertRow : function(dm, rowIndex, isUpdate){
6592         
6593         if(!isUpdate){
6594             this.fireEvent("beforerowsinserted", this, rowIndex);
6595         }
6596             //var s = this.getScrollState();
6597         var row = this.renderRow(this.cm, this.store, rowIndex);
6598         // insert before rowIndex..
6599         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6600         
6601         var _this = this;
6602                 
6603         if(row.cellObjects.length){
6604             Roo.each(row.cellObjects, function(r){
6605                 _this.renderCellObject(r);
6606             })
6607         }
6608             
6609         if(!isUpdate){
6610             this.fireEvent("rowsinserted", this, rowIndex);
6611             //this.syncRowHeights(firstRow, lastRow);
6612             //this.stripeRows(firstRow);
6613             //this.layout();
6614         }
6615         
6616     },
6617     
6618     
6619     getRowDom : function(rowIndex)
6620     {
6621         var rows = this.el.select('tbody > tr', true).elements;
6622         
6623         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6624         
6625     },
6626     // returns the object tree for a tr..
6627   
6628     
6629     renderRow : function(cm, ds, rowIndex) 
6630     {
6631         
6632         var d = ds.getAt(rowIndex);
6633         
6634         var row = {
6635             tag : 'tr',
6636             cn : []
6637         };
6638             
6639         var cellObjects = [];
6640         
6641         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6642             var config = cm.config[i];
6643             
6644             var renderer = cm.getRenderer(i);
6645             var value = '';
6646             var id = false;
6647             
6648             if(typeof(renderer) !== 'undefined'){
6649                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6650             }
6651             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6652             // and are rendered into the cells after the row is rendered - using the id for the element.
6653             
6654             if(typeof(value) === 'object'){
6655                 id = Roo.id();
6656                 cellObjects.push({
6657                     container : id,
6658                     cfg : value 
6659                 })
6660             }
6661             
6662             var rowcfg = {
6663                 record: d,
6664                 rowIndex : rowIndex,
6665                 colIndex : i,
6666                 rowClass : ''
6667             };
6668
6669             this.fireEvent('rowclass', this, rowcfg);
6670             
6671             var td = {
6672                 tag: 'td',
6673                 cls : rowcfg.rowClass,
6674                 style: '',
6675                 html: (typeof(value) === 'object') ? '' : value
6676             };
6677             
6678             if (id) {
6679                 td.id = id;
6680             }
6681             
6682             if(typeof(config.colspan) != 'undefined'){
6683                 td.colspan = config.colspan;
6684             }
6685             
6686             if(typeof(config.hidden) != 'undefined' && config.hidden){
6687                 td.style += ' display:none;';
6688             }
6689             
6690             if(typeof(config.align) != 'undefined' && config.align.length){
6691                 td.style += ' text-align:' + config.align + ';';
6692             }
6693             
6694             if(typeof(config.width) != 'undefined'){
6695                 td.style += ' width:' +  config.width + 'px;';
6696             }
6697             
6698             if(typeof(config.cursor) != 'undefined'){
6699                 td.style += ' cursor:' +  config.cursor + ';';
6700             }
6701             
6702             if(typeof(config.cls) != 'undefined'){
6703                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6704             }
6705             
6706             ['xs','sm','md','lg'].map(function(size){
6707                 
6708                 if(typeof(config[size]) == 'undefined'){
6709                     return;
6710                 }
6711                 
6712                 if (!config[size]) { // 0 = hidden
6713                     td.cls += ' hidden-' + size;
6714                     return;
6715                 }
6716                 
6717                 td.cls += ' col-' + size + '-' + config[size];
6718
6719             });
6720              
6721             row.cn.push(td);
6722            
6723         }
6724         
6725         row.cellObjects = cellObjects;
6726         
6727         return row;
6728           
6729     },
6730     
6731     
6732     
6733     onBeforeLoad : function()
6734     {
6735         //Roo.log('ds onBeforeLoad');
6736         
6737         //this.clear();
6738         
6739         //if(this.loadMask){
6740         //    this.maskEl.show();
6741         //}
6742     },
6743      /**
6744      * Remove all rows
6745      */
6746     clear : function()
6747     {
6748         this.el.select('tbody', true).first().dom.innerHTML = '';
6749     },
6750     /**
6751      * Show or hide a row.
6752      * @param {Number} rowIndex to show or hide
6753      * @param {Boolean} state hide
6754      */
6755     setRowVisibility : function(rowIndex, state)
6756     {
6757         var bt = this.mainBody.dom;
6758         
6759         var rows = this.el.select('tbody > tr', true).elements;
6760         
6761         if(typeof(rows[rowIndex]) == 'undefined'){
6762             return;
6763         }
6764         rows[rowIndex].dom.style.display = state ? '' : 'none';
6765     },
6766     
6767     
6768     getSelectionModel : function(){
6769         if(!this.selModel){
6770             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6771         }
6772         return this.selModel;
6773     },
6774     /*
6775      * Render the Roo.bootstrap object from renderder
6776      */
6777     renderCellObject : function(r)
6778     {
6779         var _this = this;
6780         
6781         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6782         
6783         var t = r.cfg.render(r.container);
6784         
6785         if(r.cfg.cn){
6786             Roo.each(r.cfg.cn, function(c){
6787                 var child = {
6788                     container: t.getChildContainer(),
6789                     cfg: c
6790                 };
6791                 _this.renderCellObject(child);
6792             })
6793         }
6794     },
6795     
6796     getRowIndex : function(row)
6797     {
6798         var rowIndex = -1;
6799         
6800         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6801             if(el != row){
6802                 return;
6803             }
6804             
6805             rowIndex = index;
6806         });
6807         
6808         return rowIndex;
6809     },
6810      /**
6811      * Returns the grid's underlying element = used by panel.Grid
6812      * @return {Element} The element
6813      */
6814     getGridEl : function(){
6815         return this.el;
6816     },
6817      /**
6818      * Forces a resize - used by panel.Grid
6819      * @return {Element} The element
6820      */
6821     autoSize : function()
6822     {
6823         //var ctr = Roo.get(this.container.dom.parentElement);
6824         var ctr = Roo.get(this.el.dom);
6825         
6826         var thd = this.getGridEl().select('thead',true).first();
6827         var tbd = this.getGridEl().select('tbody', true).first();
6828         var tfd = this.getGridEl().select('tfoot', true).first();
6829         
6830         var cw = ctr.getWidth();
6831         
6832         if (tbd) {
6833             
6834             tbd.setSize(ctr.getWidth(),
6835                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6836             );
6837             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6838             cw -= barsize;
6839         }
6840         cw = Math.max(cw, this.totalWidth);
6841         this.getGridEl().select('tr',true).setWidth(cw);
6842         // resize 'expandable coloumn?
6843         
6844         return; // we doe not have a view in this design..
6845         
6846     },
6847     onBodyScroll: function()
6848     {
6849         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6850         this.mainHead.setStyle({
6851             'position' : 'relative',
6852             'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6853         });
6854         
6855         if(this.lazyLoad){
6856             
6857             var scrollHeight = this.mainBody.dom.scrollHeight;
6858             
6859             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6860             
6861             var height = this.mainBody.getHeight();
6862             
6863             if(scrollHeight - height == scrollTop) {
6864                 
6865                 var total = this.ds.getTotalCount();
6866                 
6867                 if(this.footer.cursor + this.footer.pageSize < total){
6868                     
6869                     this.footer.ds.load({
6870                         params : {
6871                             start : this.footer.cursor + this.footer.pageSize,
6872                             limit : this.footer.pageSize
6873                         },
6874                         add : true
6875                     });
6876                 }
6877             }
6878             
6879         }
6880     }
6881 });
6882
6883  
6884
6885  /*
6886  * - LGPL
6887  *
6888  * table cell
6889  * 
6890  */
6891
6892 /**
6893  * @class Roo.bootstrap.TableCell
6894  * @extends Roo.bootstrap.Component
6895  * Bootstrap TableCell class
6896  * @cfg {String} html cell contain text
6897  * @cfg {String} cls cell class
6898  * @cfg {String} tag cell tag (td|th) default td
6899  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6900  * @cfg {String} align Aligns the content in a cell
6901  * @cfg {String} axis Categorizes cells
6902  * @cfg {String} bgcolor Specifies the background color of a cell
6903  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6904  * @cfg {Number} colspan Specifies the number of columns a cell should span
6905  * @cfg {String} headers Specifies one or more header cells a cell is related to
6906  * @cfg {Number} height Sets the height of a cell
6907  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6908  * @cfg {Number} rowspan Sets the number of rows a cell should span
6909  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6910  * @cfg {String} valign Vertical aligns the content in a cell
6911  * @cfg {Number} width Specifies the width of a cell
6912  * 
6913  * @constructor
6914  * Create a new TableCell
6915  * @param {Object} config The config object
6916  */
6917
6918 Roo.bootstrap.TableCell = function(config){
6919     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6920 };
6921
6922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6923     
6924     html: false,
6925     cls: false,
6926     tag: false,
6927     abbr: false,
6928     align: false,
6929     axis: false,
6930     bgcolor: false,
6931     charoff: false,
6932     colspan: false,
6933     headers: false,
6934     height: false,
6935     nowrap: false,
6936     rowspan: false,
6937     scope: false,
6938     valign: false,
6939     width: false,
6940     
6941     
6942     getAutoCreate : function(){
6943         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6944         
6945         cfg = {
6946             tag: 'td'
6947         };
6948         
6949         if(this.tag){
6950             cfg.tag = this.tag;
6951         }
6952         
6953         if (this.html) {
6954             cfg.html=this.html
6955         }
6956         if (this.cls) {
6957             cfg.cls=this.cls
6958         }
6959         if (this.abbr) {
6960             cfg.abbr=this.abbr
6961         }
6962         if (this.align) {
6963             cfg.align=this.align
6964         }
6965         if (this.axis) {
6966             cfg.axis=this.axis
6967         }
6968         if (this.bgcolor) {
6969             cfg.bgcolor=this.bgcolor
6970         }
6971         if (this.charoff) {
6972             cfg.charoff=this.charoff
6973         }
6974         if (this.colspan) {
6975             cfg.colspan=this.colspan
6976         }
6977         if (this.headers) {
6978             cfg.headers=this.headers
6979         }
6980         if (this.height) {
6981             cfg.height=this.height
6982         }
6983         if (this.nowrap) {
6984             cfg.nowrap=this.nowrap
6985         }
6986         if (this.rowspan) {
6987             cfg.rowspan=this.rowspan
6988         }
6989         if (this.scope) {
6990             cfg.scope=this.scope
6991         }
6992         if (this.valign) {
6993             cfg.valign=this.valign
6994         }
6995         if (this.width) {
6996             cfg.width=this.width
6997         }
6998         
6999         
7000         return cfg;
7001     }
7002    
7003 });
7004
7005  
7006
7007  /*
7008  * - LGPL
7009  *
7010  * table row
7011  * 
7012  */
7013
7014 /**
7015  * @class Roo.bootstrap.TableRow
7016  * @extends Roo.bootstrap.Component
7017  * Bootstrap TableRow class
7018  * @cfg {String} cls row class
7019  * @cfg {String} align Aligns the content in a table row
7020  * @cfg {String} bgcolor Specifies a background color for a table row
7021  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7022  * @cfg {String} valign Vertical aligns the content in a table row
7023  * 
7024  * @constructor
7025  * Create a new TableRow
7026  * @param {Object} config The config object
7027  */
7028
7029 Roo.bootstrap.TableRow = function(config){
7030     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7031 };
7032
7033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7034     
7035     cls: false,
7036     align: false,
7037     bgcolor: false,
7038     charoff: false,
7039     valign: false,
7040     
7041     getAutoCreate : function(){
7042         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7043         
7044         cfg = {
7045             tag: 'tr'
7046         };
7047             
7048         if(this.cls){
7049             cfg.cls = this.cls;
7050         }
7051         if(this.align){
7052             cfg.align = this.align;
7053         }
7054         if(this.bgcolor){
7055             cfg.bgcolor = this.bgcolor;
7056         }
7057         if(this.charoff){
7058             cfg.charoff = this.charoff;
7059         }
7060         if(this.valign){
7061             cfg.valign = this.valign;
7062         }
7063         
7064         return cfg;
7065     }
7066    
7067 });
7068
7069  
7070
7071  /*
7072  * - LGPL
7073  *
7074  * table body
7075  * 
7076  */
7077
7078 /**
7079  * @class Roo.bootstrap.TableBody
7080  * @extends Roo.bootstrap.Component
7081  * Bootstrap TableBody class
7082  * @cfg {String} cls element class
7083  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7084  * @cfg {String} align Aligns the content inside the element
7085  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7086  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7087  * 
7088  * @constructor
7089  * Create a new TableBody
7090  * @param {Object} config The config object
7091  */
7092
7093 Roo.bootstrap.TableBody = function(config){
7094     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7095 };
7096
7097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7098     
7099     cls: false,
7100     tag: false,
7101     align: false,
7102     charoff: false,
7103     valign: false,
7104     
7105     getAutoCreate : function(){
7106         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7107         
7108         cfg = {
7109             tag: 'tbody'
7110         };
7111             
7112         if (this.cls) {
7113             cfg.cls=this.cls
7114         }
7115         if(this.tag){
7116             cfg.tag = this.tag;
7117         }
7118         
7119         if(this.align){
7120             cfg.align = this.align;
7121         }
7122         if(this.charoff){
7123             cfg.charoff = this.charoff;
7124         }
7125         if(this.valign){
7126             cfg.valign = this.valign;
7127         }
7128         
7129         return cfg;
7130     }
7131     
7132     
7133 //    initEvents : function()
7134 //    {
7135 //        
7136 //        if(!this.store){
7137 //            return;
7138 //        }
7139 //        
7140 //        this.store = Roo.factory(this.store, Roo.data);
7141 //        this.store.on('load', this.onLoad, this);
7142 //        
7143 //        this.store.load();
7144 //        
7145 //    },
7146 //    
7147 //    onLoad: function () 
7148 //    {   
7149 //        this.fireEvent('load', this);
7150 //    }
7151 //    
7152 //   
7153 });
7154
7155  
7156
7157  /*
7158  * Based on:
7159  * Ext JS Library 1.1.1
7160  * Copyright(c) 2006-2007, Ext JS, LLC.
7161  *
7162  * Originally Released Under LGPL - original licence link has changed is not relivant.
7163  *
7164  * Fork - LGPL
7165  * <script type="text/javascript">
7166  */
7167
7168 // as we use this in bootstrap.
7169 Roo.namespace('Roo.form');
7170  /**
7171  * @class Roo.form.Action
7172  * Internal Class used to handle form actions
7173  * @constructor
7174  * @param {Roo.form.BasicForm} el The form element or its id
7175  * @param {Object} config Configuration options
7176  */
7177
7178  
7179  
7180 // define the action interface
7181 Roo.form.Action = function(form, options){
7182     this.form = form;
7183     this.options = options || {};
7184 };
7185 /**
7186  * Client Validation Failed
7187  * @const 
7188  */
7189 Roo.form.Action.CLIENT_INVALID = 'client';
7190 /**
7191  * Server Validation Failed
7192  * @const 
7193  */
7194 Roo.form.Action.SERVER_INVALID = 'server';
7195  /**
7196  * Connect to Server Failed
7197  * @const 
7198  */
7199 Roo.form.Action.CONNECT_FAILURE = 'connect';
7200 /**
7201  * Reading Data from Server Failed
7202  * @const 
7203  */
7204 Roo.form.Action.LOAD_FAILURE = 'load';
7205
7206 Roo.form.Action.prototype = {
7207     type : 'default',
7208     failureType : undefined,
7209     response : undefined,
7210     result : undefined,
7211
7212     // interface method
7213     run : function(options){
7214
7215     },
7216
7217     // interface method
7218     success : function(response){
7219
7220     },
7221
7222     // interface method
7223     handleResponse : function(response){
7224
7225     },
7226
7227     // default connection failure
7228     failure : function(response){
7229         
7230         this.response = response;
7231         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7232         this.form.afterAction(this, false);
7233     },
7234
7235     processResponse : function(response){
7236         this.response = response;
7237         if(!response.responseText){
7238             return true;
7239         }
7240         this.result = this.handleResponse(response);
7241         return this.result;
7242     },
7243
7244     // utility functions used internally
7245     getUrl : function(appendParams){
7246         var url = this.options.url || this.form.url || this.form.el.dom.action;
7247         if(appendParams){
7248             var p = this.getParams();
7249             if(p){
7250                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7251             }
7252         }
7253         return url;
7254     },
7255
7256     getMethod : function(){
7257         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7258     },
7259
7260     getParams : function(){
7261         var bp = this.form.baseParams;
7262         var p = this.options.params;
7263         if(p){
7264             if(typeof p == "object"){
7265                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7266             }else if(typeof p == 'string' && bp){
7267                 p += '&' + Roo.urlEncode(bp);
7268             }
7269         }else if(bp){
7270             p = Roo.urlEncode(bp);
7271         }
7272         return p;
7273     },
7274
7275     createCallback : function(){
7276         return {
7277             success: this.success,
7278             failure: this.failure,
7279             scope: this,
7280             timeout: (this.form.timeout*1000),
7281             upload: this.form.fileUpload ? this.success : undefined
7282         };
7283     }
7284 };
7285
7286 Roo.form.Action.Submit = function(form, options){
7287     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7288 };
7289
7290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7291     type : 'submit',
7292
7293     haveProgress : false,
7294     uploadComplete : false,
7295     
7296     // uploadProgress indicator.
7297     uploadProgress : function()
7298     {
7299         if (!this.form.progressUrl) {
7300             return;
7301         }
7302         
7303         if (!this.haveProgress) {
7304             Roo.MessageBox.progress("Uploading", "Uploading");
7305         }
7306         if (this.uploadComplete) {
7307            Roo.MessageBox.hide();
7308            return;
7309         }
7310         
7311         this.haveProgress = true;
7312    
7313         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7314         
7315         var c = new Roo.data.Connection();
7316         c.request({
7317             url : this.form.progressUrl,
7318             params: {
7319                 id : uid
7320             },
7321             method: 'GET',
7322             success : function(req){
7323                //console.log(data);
7324                 var rdata = false;
7325                 var edata;
7326                 try  {
7327                    rdata = Roo.decode(req.responseText)
7328                 } catch (e) {
7329                     Roo.log("Invalid data from server..");
7330                     Roo.log(edata);
7331                     return;
7332                 }
7333                 if (!rdata || !rdata.success) {
7334                     Roo.log(rdata);
7335                     Roo.MessageBox.alert(Roo.encode(rdata));
7336                     return;
7337                 }
7338                 var data = rdata.data;
7339                 
7340                 if (this.uploadComplete) {
7341                    Roo.MessageBox.hide();
7342                    return;
7343                 }
7344                    
7345                 if (data){
7346                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7347                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7348                     );
7349                 }
7350                 this.uploadProgress.defer(2000,this);
7351             },
7352        
7353             failure: function(data) {
7354                 Roo.log('progress url failed ');
7355                 Roo.log(data);
7356             },
7357             scope : this
7358         });
7359            
7360     },
7361     
7362     
7363     run : function()
7364     {
7365         // run get Values on the form, so it syncs any secondary forms.
7366         this.form.getValues();
7367         
7368         var o = this.options;
7369         var method = this.getMethod();
7370         var isPost = method == 'POST';
7371         if(o.clientValidation === false || this.form.isValid()){
7372             
7373             if (this.form.progressUrl) {
7374                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7375                     (new Date() * 1) + '' + Math.random());
7376                     
7377             } 
7378             
7379             
7380             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7381                 form:this.form.el.dom,
7382                 url:this.getUrl(!isPost),
7383                 method: method,
7384                 params:isPost ? this.getParams() : null,
7385                 isUpload: this.form.fileUpload
7386             }));
7387             
7388             this.uploadProgress();
7389
7390         }else if (o.clientValidation !== false){ // client validation failed
7391             this.failureType = Roo.form.Action.CLIENT_INVALID;
7392             this.form.afterAction(this, false);
7393         }
7394     },
7395
7396     success : function(response)
7397     {
7398         this.uploadComplete= true;
7399         if (this.haveProgress) {
7400             Roo.MessageBox.hide();
7401         }
7402         
7403         
7404         var result = this.processResponse(response);
7405         if(result === true || result.success){
7406             this.form.afterAction(this, true);
7407             return;
7408         }
7409         if(result.errors){
7410             this.form.markInvalid(result.errors);
7411             this.failureType = Roo.form.Action.SERVER_INVALID;
7412         }
7413         this.form.afterAction(this, false);
7414     },
7415     failure : function(response)
7416     {
7417         this.uploadComplete= true;
7418         if (this.haveProgress) {
7419             Roo.MessageBox.hide();
7420         }
7421         
7422         this.response = response;
7423         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7424         this.form.afterAction(this, false);
7425     },
7426     
7427     handleResponse : function(response){
7428         if(this.form.errorReader){
7429             var rs = this.form.errorReader.read(response);
7430             var errors = [];
7431             if(rs.records){
7432                 for(var i = 0, len = rs.records.length; i < len; i++) {
7433                     var r = rs.records[i];
7434                     errors[i] = r.data;
7435                 }
7436             }
7437             if(errors.length < 1){
7438                 errors = null;
7439             }
7440             return {
7441                 success : rs.success,
7442                 errors : errors
7443             };
7444         }
7445         var ret = false;
7446         try {
7447             ret = Roo.decode(response.responseText);
7448         } catch (e) {
7449             ret = {
7450                 success: false,
7451                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7452                 errors : []
7453             };
7454         }
7455         return ret;
7456         
7457     }
7458 });
7459
7460
7461 Roo.form.Action.Load = function(form, options){
7462     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7463     this.reader = this.form.reader;
7464 };
7465
7466 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7467     type : 'load',
7468
7469     run : function(){
7470         
7471         Roo.Ajax.request(Roo.apply(
7472                 this.createCallback(), {
7473                     method:this.getMethod(),
7474                     url:this.getUrl(false),
7475                     params:this.getParams()
7476         }));
7477     },
7478
7479     success : function(response){
7480         
7481         var result = this.processResponse(response);
7482         if(result === true || !result.success || !result.data){
7483             this.failureType = Roo.form.Action.LOAD_FAILURE;
7484             this.form.afterAction(this, false);
7485             return;
7486         }
7487         this.form.clearInvalid();
7488         this.form.setValues(result.data);
7489         this.form.afterAction(this, true);
7490     },
7491
7492     handleResponse : function(response){
7493         if(this.form.reader){
7494             var rs = this.form.reader.read(response);
7495             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7496             return {
7497                 success : rs.success,
7498                 data : data
7499             };
7500         }
7501         return Roo.decode(response.responseText);
7502     }
7503 });
7504
7505 Roo.form.Action.ACTION_TYPES = {
7506     'load' : Roo.form.Action.Load,
7507     'submit' : Roo.form.Action.Submit
7508 };/*
7509  * - LGPL
7510  *
7511  * form
7512  *
7513  */
7514
7515 /**
7516  * @class Roo.bootstrap.Form
7517  * @extends Roo.bootstrap.Component
7518  * Bootstrap Form class
7519  * @cfg {String} method  GET | POST (default POST)
7520  * @cfg {String} labelAlign top | left (default top)
7521  * @cfg {String} align left  | right - for navbars
7522  * @cfg {Boolean} loadMask load mask when submit (default true)
7523
7524  *
7525  * @constructor
7526  * Create a new Form
7527  * @param {Object} config The config object
7528  */
7529
7530
7531 Roo.bootstrap.Form = function(config){
7532     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7533     
7534     Roo.bootstrap.Form.popover.apply();
7535     
7536     this.addEvents({
7537         /**
7538          * @event clientvalidation
7539          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7540          * @param {Form} this
7541          * @param {Boolean} valid true if the form has passed client-side validation
7542          */
7543         clientvalidation: true,
7544         /**
7545          * @event beforeaction
7546          * Fires before any action is performed. Return false to cancel the action.
7547          * @param {Form} this
7548          * @param {Action} action The action to be performed
7549          */
7550         beforeaction: true,
7551         /**
7552          * @event actionfailed
7553          * Fires when an action fails.
7554          * @param {Form} this
7555          * @param {Action} action The action that failed
7556          */
7557         actionfailed : true,
7558         /**
7559          * @event actioncomplete
7560          * Fires when an action is completed.
7561          * @param {Form} this
7562          * @param {Action} action The action that completed
7563          */
7564         actioncomplete : true
7565     });
7566
7567 };
7568
7569 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7570
7571      /**
7572      * @cfg {String} method
7573      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7574      */
7575     method : 'POST',
7576     /**
7577      * @cfg {String} url
7578      * The URL to use for form actions if one isn't supplied in the action options.
7579      */
7580     /**
7581      * @cfg {Boolean} fileUpload
7582      * Set to true if this form is a file upload.
7583      */
7584
7585     /**
7586      * @cfg {Object} baseParams
7587      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7588      */
7589
7590     /**
7591      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7592      */
7593     timeout: 30,
7594     /**
7595      * @cfg {Sting} align (left|right) for navbar forms
7596      */
7597     align : 'left',
7598
7599     // private
7600     activeAction : null,
7601
7602     /**
7603      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7604      * element by passing it or its id or mask the form itself by passing in true.
7605      * @type Mixed
7606      */
7607     waitMsgTarget : false,
7608
7609     loadMask : true,
7610     
7611     /**
7612      * @cfg {Boolean} errorMask (true|false) default false
7613      */
7614     errorMask : false,
7615     
7616     /**
7617      * @cfg {Number} maskOffset Default 100
7618      */
7619     maskOffset : 100,
7620
7621     getAutoCreate : function(){
7622
7623         var cfg = {
7624             tag: 'form',
7625             method : this.method || 'POST',
7626             id : this.id || Roo.id(),
7627             cls : ''
7628         };
7629         if (this.parent().xtype.match(/^Nav/)) {
7630             cfg.cls = 'navbar-form navbar-' + this.align;
7631
7632         }
7633
7634         if (this.labelAlign == 'left' ) {
7635             cfg.cls += ' form-horizontal';
7636         }
7637
7638
7639         return cfg;
7640     },
7641     initEvents : function()
7642     {
7643         this.el.on('submit', this.onSubmit, this);
7644         // this was added as random key presses on the form where triggering form submit.
7645         this.el.on('keypress', function(e) {
7646             if (e.getCharCode() != 13) {
7647                 return true;
7648             }
7649             // we might need to allow it for textareas.. and some other items.
7650             // check e.getTarget().
7651
7652             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7653                 return true;
7654             }
7655
7656             Roo.log("keypress blocked");
7657
7658             e.preventDefault();
7659             return false;
7660         });
7661         
7662     },
7663     // private
7664     onSubmit : function(e){
7665         e.stopEvent();
7666     },
7667
7668      /**
7669      * Returns true if client-side validation on the form is successful.
7670      * @return Boolean
7671      */
7672     isValid : function(){
7673         var items = this.getItems();
7674         var valid = true;
7675         var target = false;
7676         
7677         items.each(function(f){
7678             if(f.validate()){
7679                 return;
7680             }
7681             valid = false;
7682
7683             if(!target && f.el.isVisible(true)){
7684                 target = f;
7685             }
7686            
7687         });
7688         
7689         if(this.errorMask && !valid){
7690             Roo.bootstrap.Form.popover.mask(this, target);
7691         }
7692         
7693         return valid;
7694     },
7695     
7696     /**
7697      * Returns true if any fields in this form have changed since their original load.
7698      * @return Boolean
7699      */
7700     isDirty : function(){
7701         var dirty = false;
7702         var items = this.getItems();
7703         items.each(function(f){
7704            if(f.isDirty()){
7705                dirty = true;
7706                return false;
7707            }
7708            return true;
7709         });
7710         return dirty;
7711     },
7712      /**
7713      * Performs a predefined action (submit or load) or custom actions you define on this form.
7714      * @param {String} actionName The name of the action type
7715      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7716      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7717      * accept other config options):
7718      * <pre>
7719 Property          Type             Description
7720 ----------------  ---------------  ----------------------------------------------------------------------------------
7721 url               String           The url for the action (defaults to the form's url)
7722 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7723 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7724 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7725                                    validate the form on the client (defaults to false)
7726      * </pre>
7727      * @return {BasicForm} this
7728      */
7729     doAction : function(action, options){
7730         if(typeof action == 'string'){
7731             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7732         }
7733         if(this.fireEvent('beforeaction', this, action) !== false){
7734             this.beforeAction(action);
7735             action.run.defer(100, action);
7736         }
7737         return this;
7738     },
7739
7740     // private
7741     beforeAction : function(action){
7742         var o = action.options;
7743
7744         if(this.loadMask){
7745             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7746         }
7747         // not really supported yet.. ??
7748
7749         //if(this.waitMsgTarget === true){
7750         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7751         //}else if(this.waitMsgTarget){
7752         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7753         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7754         //}else {
7755         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7756        // }
7757
7758     },
7759
7760     // private
7761     afterAction : function(action, success){
7762         this.activeAction = null;
7763         var o = action.options;
7764
7765         //if(this.waitMsgTarget === true){
7766             this.el.unmask();
7767         //}else if(this.waitMsgTarget){
7768         //    this.waitMsgTarget.unmask();
7769         //}else{
7770         //    Roo.MessageBox.updateProgress(1);
7771         //    Roo.MessageBox.hide();
7772        // }
7773         //
7774         if(success){
7775             if(o.reset){
7776                 this.reset();
7777             }
7778             Roo.callback(o.success, o.scope, [this, action]);
7779             this.fireEvent('actioncomplete', this, action);
7780
7781         }else{
7782
7783             // failure condition..
7784             // we have a scenario where updates need confirming.
7785             // eg. if a locking scenario exists..
7786             // we look for { errors : { needs_confirm : true }} in the response.
7787             if (
7788                 (typeof(action.result) != 'undefined')  &&
7789                 (typeof(action.result.errors) != 'undefined')  &&
7790                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7791            ){
7792                 var _t = this;
7793                 Roo.log("not supported yet");
7794                  /*
7795
7796                 Roo.MessageBox.confirm(
7797                     "Change requires confirmation",
7798                     action.result.errorMsg,
7799                     function(r) {
7800                         if (r != 'yes') {
7801                             return;
7802                         }
7803                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7804                     }
7805
7806                 );
7807                 */
7808
7809
7810                 return;
7811             }
7812
7813             Roo.callback(o.failure, o.scope, [this, action]);
7814             // show an error message if no failed handler is set..
7815             if (!this.hasListener('actionfailed')) {
7816                 Roo.log("need to add dialog support");
7817                 /*
7818                 Roo.MessageBox.alert("Error",
7819                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7820                         action.result.errorMsg :
7821                         "Saving Failed, please check your entries or try again"
7822                 );
7823                 */
7824             }
7825
7826             this.fireEvent('actionfailed', this, action);
7827         }
7828
7829     },
7830     /**
7831      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7832      * @param {String} id The value to search for
7833      * @return Field
7834      */
7835     findField : function(id){
7836         var items = this.getItems();
7837         var field = items.get(id);
7838         if(!field){
7839              items.each(function(f){
7840                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7841                     field = f;
7842                     return false;
7843                 }
7844                 return true;
7845             });
7846         }
7847         return field || null;
7848     },
7849      /**
7850      * Mark fields in this form invalid in bulk.
7851      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7852      * @return {BasicForm} this
7853      */
7854     markInvalid : function(errors){
7855         if(errors instanceof Array){
7856             for(var i = 0, len = errors.length; i < len; i++){
7857                 var fieldError = errors[i];
7858                 var f = this.findField(fieldError.id);
7859                 if(f){
7860                     f.markInvalid(fieldError.msg);
7861                 }
7862             }
7863         }else{
7864             var field, id;
7865             for(id in errors){
7866                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7867                     field.markInvalid(errors[id]);
7868                 }
7869             }
7870         }
7871         //Roo.each(this.childForms || [], function (f) {
7872         //    f.markInvalid(errors);
7873         //});
7874
7875         return this;
7876     },
7877
7878     /**
7879      * Set values for fields in this form in bulk.
7880      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7881      * @return {BasicForm} this
7882      */
7883     setValues : function(values){
7884         if(values instanceof Array){ // array of objects
7885             for(var i = 0, len = values.length; i < len; i++){
7886                 var v = values[i];
7887                 var f = this.findField(v.id);
7888                 if(f){
7889                     f.setValue(v.value);
7890                     if(this.trackResetOnLoad){
7891                         f.originalValue = f.getValue();
7892                     }
7893                 }
7894             }
7895         }else{ // object hash
7896             var field, id;
7897             for(id in values){
7898                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7899
7900                     if (field.setFromData &&
7901                         field.valueField &&
7902                         field.displayField &&
7903                         // combos' with local stores can
7904                         // be queried via setValue()
7905                         // to set their value..
7906                         (field.store && !field.store.isLocal)
7907                         ) {
7908                         // it's a combo
7909                         var sd = { };
7910                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7911                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7912                         field.setFromData(sd);
7913
7914                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
7915                         
7916                         field.setFromData(values);
7917                         
7918                     } else {
7919                         field.setValue(values[id]);
7920                     }
7921
7922
7923                     if(this.trackResetOnLoad){
7924                         field.originalValue = field.getValue();
7925                     }
7926                 }
7927             }
7928         }
7929
7930         //Roo.each(this.childForms || [], function (f) {
7931         //    f.setValues(values);
7932         //});
7933
7934         return this;
7935     },
7936
7937     /**
7938      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7939      * they are returned as an array.
7940      * @param {Boolean} asString
7941      * @return {Object}
7942      */
7943     getValues : function(asString){
7944         //if (this.childForms) {
7945             // copy values from the child forms
7946         //    Roo.each(this.childForms, function (f) {
7947         //        this.setValues(f.getValues());
7948         //    }, this);
7949         //}
7950
7951
7952
7953         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7954         if(asString === true){
7955             return fs;
7956         }
7957         return Roo.urlDecode(fs);
7958     },
7959
7960     /**
7961      * Returns the fields in this form as an object with key/value pairs.
7962      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7963      * @return {Object}
7964      */
7965     getFieldValues : function(with_hidden)
7966     {
7967         var items = this.getItems();
7968         var ret = {};
7969         items.each(function(f){
7970             
7971             if (!f.getName()) {
7972                 return;
7973             }
7974             
7975             var v = f.getValue();
7976             
7977             if (f.inputType =='radio') {
7978                 if (typeof(ret[f.getName()]) == 'undefined') {
7979                     ret[f.getName()] = ''; // empty..
7980                 }
7981
7982                 if (!f.el.dom.checked) {
7983                     return;
7984
7985                 }
7986                 v = f.el.dom.value;
7987
7988             }
7989             
7990             if(f.xtype == 'MoneyField'){
7991                 ret[f.currencyName] = f.getCurrency();
7992             }
7993
7994             // not sure if this supported any more..
7995             if ((typeof(v) == 'object') && f.getRawValue) {
7996                 v = f.getRawValue() ; // dates..
7997             }
7998             // combo boxes where name != hiddenName...
7999             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8000                 ret[f.name] = f.getRawValue();
8001             }
8002             ret[f.getName()] = v;
8003         });
8004
8005         return ret;
8006     },
8007
8008     /**
8009      * Clears all invalid messages in this form.
8010      * @return {BasicForm} this
8011      */
8012     clearInvalid : function(){
8013         var items = this.getItems();
8014
8015         items.each(function(f){
8016            f.clearInvalid();
8017         });
8018
8019
8020
8021         return this;
8022     },
8023
8024     /**
8025      * Resets this form.
8026      * @return {BasicForm} this
8027      */
8028     reset : function(){
8029         var items = this.getItems();
8030         items.each(function(f){
8031             f.reset();
8032         });
8033
8034         Roo.each(this.childForms || [], function (f) {
8035             f.reset();
8036         });
8037
8038
8039         return this;
8040     },
8041     getItems : function()
8042     {
8043         var r=new Roo.util.MixedCollection(false, function(o){
8044             return o.id || (o.id = Roo.id());
8045         });
8046         var iter = function(el) {
8047             if (el.inputEl) {
8048                 r.add(el);
8049             }
8050             if (!el.items) {
8051                 return;
8052             }
8053             Roo.each(el.items,function(e) {
8054                 iter(e);
8055             });
8056
8057
8058         };
8059
8060         iter(this);
8061         return r;
8062
8063
8064
8065
8066     }
8067
8068 });
8069
8070 Roo.apply(Roo.bootstrap.Form, {
8071     
8072     popover : {
8073         
8074         padding : 5,
8075         
8076         isApplied : false,
8077         
8078         isMasked : false,
8079         
8080         form : false,
8081         
8082         target : false,
8083         
8084         toolTip : false,
8085         
8086         intervalID : false,
8087         
8088         maskEl : false,
8089         
8090         apply : function()
8091         {
8092             if(this.isApplied){
8093                 return;
8094             }
8095             
8096             this.maskEl = {
8097                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8098                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8099                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8100                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8101             };
8102             
8103             this.maskEl.top.enableDisplayMode("block");
8104             this.maskEl.left.enableDisplayMode("block");
8105             this.maskEl.bottom.enableDisplayMode("block");
8106             this.maskEl.right.enableDisplayMode("block");
8107             
8108             this.toolTip = new Roo.bootstrap.Tooltip({
8109                 cls : 'roo-form-error-popover',
8110                 alignment : {
8111                     'left' : ['r-l', [-2,0], 'right'],
8112                     'right' : ['l-r', [2,0], 'left'],
8113                     'bottom' : ['tl-bl', [0,2], 'top'],
8114                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8115                 }
8116             });
8117             
8118             this.toolTip.render(Roo.get(document.body));
8119
8120             this.toolTip.el.enableDisplayMode("block");
8121             
8122             Roo.get(document.body).on('click', function(){
8123                 this.unmask();
8124             }, this);
8125             
8126             Roo.get(document.body).on('touchstart', function(){
8127                 this.unmask();
8128             }, this);
8129             
8130             this.isApplied = true
8131         },
8132         
8133         mask : function(form, target)
8134         {
8135             this.form = form;
8136             
8137             this.target = target;
8138             
8139             if(!this.form.errorMask || !target.el){
8140                 return;
8141             }
8142             
8143             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8144             
8145             Roo.log(scrollable);
8146             
8147             var ot = this.target.el.calcOffsetsTo(scrollable);
8148             
8149             var scrollTo = ot[1] - this.form.maskOffset;
8150             
8151             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8152             
8153             scrollable.scrollTo('top', scrollTo);
8154             
8155             var box = this.target.el.getBox();
8156             Roo.log(box);
8157             var zIndex = Roo.bootstrap.Modal.zIndex++;
8158
8159             
8160             this.maskEl.top.setStyle('position', 'absolute');
8161             this.maskEl.top.setStyle('z-index', zIndex);
8162             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8163             this.maskEl.top.setLeft(0);
8164             this.maskEl.top.setTop(0);
8165             this.maskEl.top.show();
8166             
8167             this.maskEl.left.setStyle('position', 'absolute');
8168             this.maskEl.left.setStyle('z-index', zIndex);
8169             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8170             this.maskEl.left.setLeft(0);
8171             this.maskEl.left.setTop(box.y - this.padding);
8172             this.maskEl.left.show();
8173
8174             this.maskEl.bottom.setStyle('position', 'absolute');
8175             this.maskEl.bottom.setStyle('z-index', zIndex);
8176             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8177             this.maskEl.bottom.setLeft(0);
8178             this.maskEl.bottom.setTop(box.bottom + this.padding);
8179             this.maskEl.bottom.show();
8180
8181             this.maskEl.right.setStyle('position', 'absolute');
8182             this.maskEl.right.setStyle('z-index', zIndex);
8183             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8184             this.maskEl.right.setLeft(box.right + this.padding);
8185             this.maskEl.right.setTop(box.y - this.padding);
8186             this.maskEl.right.show();
8187
8188             this.toolTip.bindEl = this.target.el;
8189
8190             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8191
8192             var tip = this.target.blankText;
8193
8194             if(this.target.getValue() !== '' ) {
8195                 
8196                 if (this.target.invalidText.length) {
8197                     tip = this.target.invalidText;
8198                 } else if (this.target.regexText.length){
8199                     tip = this.target.regexText;
8200                 }
8201             }
8202
8203             this.toolTip.show(tip);
8204
8205             this.intervalID = window.setInterval(function() {
8206                 Roo.bootstrap.Form.popover.unmask();
8207             }, 10000);
8208
8209             window.onwheel = function(){ return false;};
8210             
8211             (function(){ this.isMasked = true; }).defer(500, this);
8212             
8213         },
8214         
8215         unmask : function()
8216         {
8217             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8218                 return;
8219             }
8220             
8221             this.maskEl.top.setStyle('position', 'absolute');
8222             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8223             this.maskEl.top.hide();
8224
8225             this.maskEl.left.setStyle('position', 'absolute');
8226             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8227             this.maskEl.left.hide();
8228
8229             this.maskEl.bottom.setStyle('position', 'absolute');
8230             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8231             this.maskEl.bottom.hide();
8232
8233             this.maskEl.right.setStyle('position', 'absolute');
8234             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8235             this.maskEl.right.hide();
8236             
8237             this.toolTip.hide();
8238             
8239             this.toolTip.el.hide();
8240             
8241             window.onwheel = function(){ return true;};
8242             
8243             if(this.intervalID){
8244                 window.clearInterval(this.intervalID);
8245                 this.intervalID = false;
8246             }
8247             
8248             this.isMasked = false;
8249             
8250         }
8251         
8252     }
8253     
8254 });
8255
8256 /*
8257  * Based on:
8258  * Ext JS Library 1.1.1
8259  * Copyright(c) 2006-2007, Ext JS, LLC.
8260  *
8261  * Originally Released Under LGPL - original licence link has changed is not relivant.
8262  *
8263  * Fork - LGPL
8264  * <script type="text/javascript">
8265  */
8266 /**
8267  * @class Roo.form.VTypes
8268  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8269  * @singleton
8270  */
8271 Roo.form.VTypes = function(){
8272     // closure these in so they are only created once.
8273     var alpha = /^[a-zA-Z_]+$/;
8274     var alphanum = /^[a-zA-Z0-9_]+$/;
8275     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8276     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8277
8278     // All these messages and functions are configurable
8279     return {
8280         /**
8281          * The function used to validate email addresses
8282          * @param {String} value The email address
8283          */
8284         'email' : function(v){
8285             return email.test(v);
8286         },
8287         /**
8288          * The error text to display when the email validation function returns false
8289          * @type String
8290          */
8291         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8292         /**
8293          * The keystroke filter mask to be applied on email input
8294          * @type RegExp
8295          */
8296         'emailMask' : /[a-z0-9_\.\-@]/i,
8297
8298         /**
8299          * The function used to validate URLs
8300          * @param {String} value The URL
8301          */
8302         'url' : function(v){
8303             return url.test(v);
8304         },
8305         /**
8306          * The error text to display when the url validation function returns false
8307          * @type String
8308          */
8309         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8310         
8311         /**
8312          * The function used to validate alpha values
8313          * @param {String} value The value
8314          */
8315         'alpha' : function(v){
8316             return alpha.test(v);
8317         },
8318         /**
8319          * The error text to display when the alpha validation function returns false
8320          * @type String
8321          */
8322         'alphaText' : 'This field should only contain letters and _',
8323         /**
8324          * The keystroke filter mask to be applied on alpha input
8325          * @type RegExp
8326          */
8327         'alphaMask' : /[a-z_]/i,
8328
8329         /**
8330          * The function used to validate alphanumeric values
8331          * @param {String} value The value
8332          */
8333         'alphanum' : function(v){
8334             return alphanum.test(v);
8335         },
8336         /**
8337          * The error text to display when the alphanumeric validation function returns false
8338          * @type String
8339          */
8340         'alphanumText' : 'This field should only contain letters, numbers and _',
8341         /**
8342          * The keystroke filter mask to be applied on alphanumeric input
8343          * @type RegExp
8344          */
8345         'alphanumMask' : /[a-z0-9_]/i
8346     };
8347 }();/*
8348  * - LGPL
8349  *
8350  * Input
8351  * 
8352  */
8353
8354 /**
8355  * @class Roo.bootstrap.Input
8356  * @extends Roo.bootstrap.Component
8357  * Bootstrap Input class
8358  * @cfg {Boolean} disabled is it disabled
8359  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8360  * @cfg {String} name name of the input
8361  * @cfg {string} fieldLabel - the label associated
8362  * @cfg {string} placeholder - placeholder to put in text.
8363  * @cfg {string}  before - input group add on before
8364  * @cfg {string} after - input group add on after
8365  * @cfg {string} size - (lg|sm) or leave empty..
8366  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8367  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8368  * @cfg {Number} md colspan out of 12 for computer-sized screens
8369  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8370  * @cfg {string} value default value of the input
8371  * @cfg {Number} labelWidth set the width of label 
8372  * @cfg {Number} labellg set the width of label (1-12)
8373  * @cfg {Number} labelmd set the width of label (1-12)
8374  * @cfg {Number} labelsm set the width of label (1-12)
8375  * @cfg {Number} labelxs set the width of label (1-12)
8376  * @cfg {String} labelAlign (top|left)
8377  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8378  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8379  * @cfg {String} indicatorpos (left|right) default left
8380
8381  * @cfg {String} align (left|center|right) Default left
8382  * @cfg {Boolean} forceFeedback (true|false) Default false
8383  * 
8384  * 
8385  * 
8386  * 
8387  * @constructor
8388  * Create a new Input
8389  * @param {Object} config The config object
8390  */
8391
8392 Roo.bootstrap.Input = function(config){
8393     
8394     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8395     
8396     this.addEvents({
8397         /**
8398          * @event focus
8399          * Fires when this field receives input focus.
8400          * @param {Roo.form.Field} this
8401          */
8402         focus : true,
8403         /**
8404          * @event blur
8405          * Fires when this field loses input focus.
8406          * @param {Roo.form.Field} this
8407          */
8408         blur : true,
8409         /**
8410          * @event specialkey
8411          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8412          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8413          * @param {Roo.form.Field} this
8414          * @param {Roo.EventObject} e The event object
8415          */
8416         specialkey : true,
8417         /**
8418          * @event change
8419          * Fires just before the field blurs if the field value has changed.
8420          * @param {Roo.form.Field} this
8421          * @param {Mixed} newValue The new value
8422          * @param {Mixed} oldValue The original value
8423          */
8424         change : true,
8425         /**
8426          * @event invalid
8427          * Fires after the field has been marked as invalid.
8428          * @param {Roo.form.Field} this
8429          * @param {String} msg The validation message
8430          */
8431         invalid : true,
8432         /**
8433          * @event valid
8434          * Fires after the field has been validated with no errors.
8435          * @param {Roo.form.Field} this
8436          */
8437         valid : true,
8438          /**
8439          * @event keyup
8440          * Fires after the key up
8441          * @param {Roo.form.Field} this
8442          * @param {Roo.EventObject}  e The event Object
8443          */
8444         keyup : true
8445     });
8446 };
8447
8448 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8449      /**
8450      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8451       automatic validation (defaults to "keyup").
8452      */
8453     validationEvent : "keyup",
8454      /**
8455      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8456      */
8457     validateOnBlur : true,
8458     /**
8459      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8460      */
8461     validationDelay : 250,
8462      /**
8463      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8464      */
8465     focusClass : "x-form-focus",  // not needed???
8466     
8467        
8468     /**
8469      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8470      */
8471     invalidClass : "has-warning",
8472     
8473     /**
8474      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8475      */
8476     validClass : "has-success",
8477     
8478     /**
8479      * @cfg {Boolean} hasFeedback (true|false) default true
8480      */
8481     hasFeedback : true,
8482     
8483     /**
8484      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8485      */
8486     invalidFeedbackClass : "glyphicon-warning-sign",
8487     
8488     /**
8489      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8490      */
8491     validFeedbackClass : "glyphicon-ok",
8492     
8493     /**
8494      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8495      */
8496     selectOnFocus : false,
8497     
8498      /**
8499      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8500      */
8501     maskRe : null,
8502        /**
8503      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8504      */
8505     vtype : null,
8506     
8507       /**
8508      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8509      */
8510     disableKeyFilter : false,
8511     
8512        /**
8513      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8514      */
8515     disabled : false,
8516      /**
8517      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8518      */
8519     allowBlank : true,
8520     /**
8521      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8522      */
8523     blankText : "Please complete this mandatory field",
8524     
8525      /**
8526      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8527      */
8528     minLength : 0,
8529     /**
8530      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8531      */
8532     maxLength : Number.MAX_VALUE,
8533     /**
8534      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8535      */
8536     minLengthText : "The minimum length for this field is {0}",
8537     /**
8538      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8539      */
8540     maxLengthText : "The maximum length for this field is {0}",
8541   
8542     
8543     /**
8544      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8545      * If available, this function will be called only after the basic validators all return true, and will be passed the
8546      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8547      */
8548     validator : null,
8549     /**
8550      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8551      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8552      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8553      */
8554     regex : null,
8555     /**
8556      * @cfg {String} regexText -- Depricated - use Invalid Text
8557      */
8558     regexText : "",
8559     
8560     /**
8561      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8562      */
8563     invalidText : "",
8564     
8565     
8566     
8567     autocomplete: false,
8568     
8569     
8570     fieldLabel : '',
8571     inputType : 'text',
8572     
8573     name : false,
8574     placeholder: false,
8575     before : false,
8576     after : false,
8577     size : false,
8578     hasFocus : false,
8579     preventMark: false,
8580     isFormField : true,
8581     value : '',
8582     labelWidth : 2,
8583     labelAlign : false,
8584     readOnly : false,
8585     align : false,
8586     formatedValue : false,
8587     forceFeedback : false,
8588     
8589     indicatorpos : 'left',
8590     
8591     labellg : 0,
8592     labelmd : 0,
8593     labelsm : 0,
8594     labelxs : 0,
8595     
8596     parentLabelAlign : function()
8597     {
8598         var parent = this;
8599         while (parent.parent()) {
8600             parent = parent.parent();
8601             if (typeof(parent.labelAlign) !='undefined') {
8602                 return parent.labelAlign;
8603             }
8604         }
8605         return 'left';
8606         
8607     },
8608     
8609     getAutoCreate : function()
8610     {
8611         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8612         
8613         var id = Roo.id();
8614         
8615         var cfg = {};
8616         
8617         if(this.inputType != 'hidden'){
8618             cfg.cls = 'form-group' //input-group
8619         }
8620         
8621         var input =  {
8622             tag: 'input',
8623             id : id,
8624             type : this.inputType,
8625             value : this.value,
8626             cls : 'form-control',
8627             placeholder : this.placeholder || '',
8628             autocomplete : this.autocomplete || 'new-password'
8629         };
8630         
8631         if(this.align){
8632             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8633         }
8634         
8635         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8636             input.maxLength = this.maxLength;
8637         }
8638         
8639         if (this.disabled) {
8640             input.disabled=true;
8641         }
8642         
8643         if (this.readOnly) {
8644             input.readonly=true;
8645         }
8646         
8647         if (this.name) {
8648             input.name = this.name;
8649         }
8650         
8651         if (this.size) {
8652             input.cls += ' input-' + this.size;
8653         }
8654         
8655         var settings=this;
8656         ['xs','sm','md','lg'].map(function(size){
8657             if (settings[size]) {
8658                 cfg.cls += ' col-' + size + '-' + settings[size];
8659             }
8660         });
8661         
8662         var inputblock = input;
8663         
8664         var feedback = {
8665             tag: 'span',
8666             cls: 'glyphicon form-control-feedback'
8667         };
8668             
8669         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8670             
8671             inputblock = {
8672                 cls : 'has-feedback',
8673                 cn :  [
8674                     input,
8675                     feedback
8676                 ] 
8677             };  
8678         }
8679         
8680         if (this.before || this.after) {
8681             
8682             inputblock = {
8683                 cls : 'input-group',
8684                 cn :  [] 
8685             };
8686             
8687             if (this.before && typeof(this.before) == 'string') {
8688                 
8689                 inputblock.cn.push({
8690                     tag :'span',
8691                     cls : 'roo-input-before input-group-addon',
8692                     html : this.before
8693                 });
8694             }
8695             if (this.before && typeof(this.before) == 'object') {
8696                 this.before = Roo.factory(this.before);
8697                 
8698                 inputblock.cn.push({
8699                     tag :'span',
8700                     cls : 'roo-input-before input-group-' +
8701                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8702                 });
8703             }
8704             
8705             inputblock.cn.push(input);
8706             
8707             if (this.after && typeof(this.after) == 'string') {
8708                 inputblock.cn.push({
8709                     tag :'span',
8710                     cls : 'roo-input-after input-group-addon',
8711                     html : this.after
8712                 });
8713             }
8714             if (this.after && typeof(this.after) == 'object') {
8715                 this.after = Roo.factory(this.after);
8716                 
8717                 inputblock.cn.push({
8718                     tag :'span',
8719                     cls : 'roo-input-after input-group-' +
8720                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8721                 });
8722             }
8723             
8724             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8725                 inputblock.cls += ' has-feedback';
8726                 inputblock.cn.push(feedback);
8727             }
8728         };
8729         
8730         if (align ==='left' && this.fieldLabel.length) {
8731             
8732             cfg.cls += ' roo-form-group-label-left';
8733             
8734             cfg.cn = [
8735                 {
8736                     tag : 'i',
8737                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8738                     tooltip : 'This field is required'
8739                 },
8740                 {
8741                     tag: 'label',
8742                     'for' :  id,
8743                     cls : 'control-label',
8744                     html : this.fieldLabel
8745
8746                 },
8747                 {
8748                     cls : "", 
8749                     cn: [
8750                         inputblock
8751                     ]
8752                 }
8753             ];
8754             
8755             var labelCfg = cfg.cn[1];
8756             var contentCfg = cfg.cn[2];
8757             
8758             if(this.indicatorpos == 'right'){
8759                 cfg.cn = [
8760                     {
8761                         tag: 'label',
8762                         'for' :  id,
8763                         cls : 'control-label',
8764                         cn : [
8765                             {
8766                                 tag : 'span',
8767                                 html : this.fieldLabel
8768                             },
8769                             {
8770                                 tag : 'i',
8771                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8772                                 tooltip : 'This field is required'
8773                             }
8774                         ]
8775                     },
8776                     {
8777                         cls : "",
8778                         cn: [
8779                             inputblock
8780                         ]
8781                     }
8782
8783                 ];
8784                 
8785                 labelCfg = cfg.cn[0];
8786                 contentCfg = cfg.cn[1];
8787             
8788             }
8789             
8790             if(this.labelWidth > 12){
8791                 labelCfg.style = "width: " + this.labelWidth + 'px';
8792             }
8793             
8794             if(this.labelWidth < 13 && this.labelmd == 0){
8795                 this.labelmd = this.labelWidth;
8796             }
8797             
8798             if(this.labellg > 0){
8799                 labelCfg.cls += ' col-lg-' + this.labellg;
8800                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8801             }
8802             
8803             if(this.labelmd > 0){
8804                 labelCfg.cls += ' col-md-' + this.labelmd;
8805                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8806             }
8807             
8808             if(this.labelsm > 0){
8809                 labelCfg.cls += ' col-sm-' + this.labelsm;
8810                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8811             }
8812             
8813             if(this.labelxs > 0){
8814                 labelCfg.cls += ' col-xs-' + this.labelxs;
8815                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8816             }
8817             
8818             
8819         } else if ( this.fieldLabel.length) {
8820                 
8821             cfg.cn = [
8822                 {
8823                     tag : 'i',
8824                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8825                     tooltip : 'This field is required'
8826                 },
8827                 {
8828                     tag: 'label',
8829                    //cls : 'input-group-addon',
8830                     html : this.fieldLabel
8831
8832                 },
8833
8834                inputblock
8835
8836            ];
8837            
8838            if(this.indicatorpos == 'right'){
8839                 
8840                 cfg.cn = [
8841                     {
8842                         tag: 'label',
8843                        //cls : 'input-group-addon',
8844                         html : this.fieldLabel
8845
8846                     },
8847                     {
8848                         tag : 'i',
8849                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8850                         tooltip : 'This field is required'
8851                     },
8852
8853                    inputblock
8854
8855                ];
8856
8857             }
8858
8859         } else {
8860             
8861             cfg.cn = [
8862
8863                     inputblock
8864
8865             ];
8866                 
8867                 
8868         };
8869         
8870         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8871            cfg.cls += ' navbar-form';
8872         }
8873         
8874         if (this.parentType === 'NavGroup') {
8875            cfg.cls += ' navbar-form';
8876            cfg.tag = 'li';
8877         }
8878         
8879         return cfg;
8880         
8881     },
8882     /**
8883      * return the real input element.
8884      */
8885     inputEl: function ()
8886     {
8887         return this.el.select('input.form-control',true).first();
8888     },
8889     
8890     tooltipEl : function()
8891     {
8892         return this.inputEl();
8893     },
8894     
8895     indicatorEl : function()
8896     {
8897         var indicator = this.el.select('i.roo-required-indicator',true).first();
8898         
8899         if(!indicator){
8900             return false;
8901         }
8902         
8903         return indicator;
8904         
8905     },
8906     
8907     setDisabled : function(v)
8908     {
8909         var i  = this.inputEl().dom;
8910         if (!v) {
8911             i.removeAttribute('disabled');
8912             return;
8913             
8914         }
8915         i.setAttribute('disabled','true');
8916     },
8917     initEvents : function()
8918     {
8919           
8920         this.inputEl().on("keydown" , this.fireKey,  this);
8921         this.inputEl().on("focus", this.onFocus,  this);
8922         this.inputEl().on("blur", this.onBlur,  this);
8923         
8924         this.inputEl().relayEvent('keyup', this);
8925         
8926         this.indicator = this.indicatorEl();
8927         
8928         if(this.indicator){
8929             this.indicator.addClass('invisible');
8930             
8931         }
8932  
8933         // reference to original value for reset
8934         this.originalValue = this.getValue();
8935         //Roo.form.TextField.superclass.initEvents.call(this);
8936         if(this.validationEvent == 'keyup'){
8937             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8938             this.inputEl().on('keyup', this.filterValidation, this);
8939         }
8940         else if(this.validationEvent !== false){
8941             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8942         }
8943         
8944         if(this.selectOnFocus){
8945             this.on("focus", this.preFocus, this);
8946             
8947         }
8948         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8949             this.inputEl().on("keypress", this.filterKeys, this);
8950         } else {
8951             this.inputEl().relayEvent('keypress', this);
8952         }
8953        /* if(this.grow){
8954             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8955             this.el.on("click", this.autoSize,  this);
8956         }
8957         */
8958         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8959             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8960         }
8961         
8962         if (typeof(this.before) == 'object') {
8963             this.before.render(this.el.select('.roo-input-before',true).first());
8964         }
8965         if (typeof(this.after) == 'object') {
8966             this.after.render(this.el.select('.roo-input-after',true).first());
8967         }
8968         
8969         
8970     },
8971     filterValidation : function(e){
8972         if(!e.isNavKeyPress()){
8973             this.validationTask.delay(this.validationDelay);
8974         }
8975     },
8976      /**
8977      * Validates the field value
8978      * @return {Boolean} True if the value is valid, else false
8979      */
8980     validate : function(){
8981         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8982         if(this.disabled || this.validateValue(this.getRawValue())){
8983             this.markValid();
8984             return true;
8985         }
8986         
8987         this.markInvalid();
8988         return false;
8989     },
8990     
8991     
8992     /**
8993      * Validates a value according to the field's validation rules and marks the field as invalid
8994      * if the validation fails
8995      * @param {Mixed} value The value to validate
8996      * @return {Boolean} True if the value is valid, else false
8997      */
8998     validateValue : function(value){
8999         if(value.length < 1)  { // if it's blank
9000             if(this.allowBlank){
9001                 return true;
9002             }            
9003             return this.inputEl().hasClass('hide') ? true : false;
9004         }
9005         
9006         if(value.length < this.minLength){
9007             return false;
9008         }
9009         if(value.length > this.maxLength){
9010             return false;
9011         }
9012         if(this.vtype){
9013             var vt = Roo.form.VTypes;
9014             if(!vt[this.vtype](value, this)){
9015                 return false;
9016             }
9017         }
9018         if(typeof this.validator == "function"){
9019             var msg = this.validator(value);
9020             if(msg !== true){
9021                 return false;
9022             }
9023             if (typeof(msg) == 'string') {
9024                 this.invalidText = msg;
9025             }
9026         }
9027         
9028         if(this.regex && !this.regex.test(value)){
9029             return false;
9030         }
9031         
9032         return true;
9033     },
9034
9035     
9036     
9037      // private
9038     fireKey : function(e){
9039         //Roo.log('field ' + e.getKey());
9040         if(e.isNavKeyPress()){
9041             this.fireEvent("specialkey", this, e);
9042         }
9043     },
9044     focus : function (selectText){
9045         if(this.rendered){
9046             this.inputEl().focus();
9047             if(selectText === true){
9048                 this.inputEl().dom.select();
9049             }
9050         }
9051         return this;
9052     } ,
9053     
9054     onFocus : function(){
9055         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9056            // this.el.addClass(this.focusClass);
9057         }
9058         if(!this.hasFocus){
9059             this.hasFocus = true;
9060             this.startValue = this.getValue();
9061             this.fireEvent("focus", this);
9062         }
9063     },
9064     
9065     beforeBlur : Roo.emptyFn,
9066
9067     
9068     // private
9069     onBlur : function(){
9070         this.beforeBlur();
9071         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9072             //this.el.removeClass(this.focusClass);
9073         }
9074         this.hasFocus = false;
9075         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9076             this.validate();
9077         }
9078         var v = this.getValue();
9079         if(String(v) !== String(this.startValue)){
9080             this.fireEvent('change', this, v, this.startValue);
9081         }
9082         this.fireEvent("blur", this);
9083     },
9084     
9085     /**
9086      * Resets the current field value to the originally loaded value and clears any validation messages
9087      */
9088     reset : function(){
9089         this.setValue(this.originalValue);
9090         this.validate();
9091     },
9092      /**
9093      * Returns the name of the field
9094      * @return {Mixed} name The name field
9095      */
9096     getName: function(){
9097         return this.name;
9098     },
9099      /**
9100      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9101      * @return {Mixed} value The field value
9102      */
9103     getValue : function(){
9104         
9105         var v = this.inputEl().getValue();
9106         
9107         return v;
9108     },
9109     /**
9110      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9111      * @return {Mixed} value The field value
9112      */
9113     getRawValue : function(){
9114         var v = this.inputEl().getValue();
9115         
9116         return v;
9117     },
9118     
9119     /**
9120      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9121      * @param {Mixed} value The value to set
9122      */
9123     setRawValue : function(v){
9124         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9125     },
9126     
9127     selectText : function(start, end){
9128         var v = this.getRawValue();
9129         if(v.length > 0){
9130             start = start === undefined ? 0 : start;
9131             end = end === undefined ? v.length : end;
9132             var d = this.inputEl().dom;
9133             if(d.setSelectionRange){
9134                 d.setSelectionRange(start, end);
9135             }else if(d.createTextRange){
9136                 var range = d.createTextRange();
9137                 range.moveStart("character", start);
9138                 range.moveEnd("character", v.length-end);
9139                 range.select();
9140             }
9141         }
9142     },
9143     
9144     /**
9145      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9146      * @param {Mixed} value The value to set
9147      */
9148     setValue : function(v){
9149         this.value = v;
9150         if(this.rendered){
9151             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9152             this.validate();
9153         }
9154     },
9155     
9156     /*
9157     processValue : function(value){
9158         if(this.stripCharsRe){
9159             var newValue = value.replace(this.stripCharsRe, '');
9160             if(newValue !== value){
9161                 this.setRawValue(newValue);
9162                 return newValue;
9163             }
9164         }
9165         return value;
9166     },
9167   */
9168     preFocus : function(){
9169         
9170         if(this.selectOnFocus){
9171             this.inputEl().dom.select();
9172         }
9173     },
9174     filterKeys : function(e){
9175         var k = e.getKey();
9176         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9177             return;
9178         }
9179         var c = e.getCharCode(), cc = String.fromCharCode(c);
9180         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9181             return;
9182         }
9183         if(!this.maskRe.test(cc)){
9184             e.stopEvent();
9185         }
9186     },
9187      /**
9188      * Clear any invalid styles/messages for this field
9189      */
9190     clearInvalid : function(){
9191         
9192         if(!this.el || this.preventMark){ // not rendered
9193             return;
9194         }
9195         
9196      
9197         this.el.removeClass(this.invalidClass);
9198         
9199         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9200             
9201             var feedback = this.el.select('.form-control-feedback', true).first();
9202             
9203             if(feedback){
9204                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9205             }
9206             
9207         }
9208         
9209         this.fireEvent('valid', this);
9210     },
9211     
9212      /**
9213      * Mark this field as valid
9214      */
9215     markValid : function()
9216     {
9217         if(!this.el  || this.preventMark){ // not rendered...
9218             return;
9219         }
9220         
9221         this.el.removeClass([this.invalidClass, this.validClass]);
9222         
9223         var feedback = this.el.select('.form-control-feedback', true).first();
9224             
9225         if(feedback){
9226             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9227         }
9228
9229         if(this.disabled){
9230             return;
9231         }
9232         
9233         if(this.allowBlank && !this.getRawValue().length){
9234             return;
9235         }
9236         
9237         if(this.indicator){
9238             this.indicator.removeClass('visible');
9239             this.indicator.addClass('invisible');
9240         }
9241         
9242         this.el.addClass(this.validClass);
9243         
9244         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9245             
9246             var feedback = this.el.select('.form-control-feedback', true).first();
9247             
9248             if(feedback){
9249                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9250                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9251             }
9252             
9253         }
9254         
9255         this.fireEvent('valid', this);
9256     },
9257     
9258      /**
9259      * Mark this field as invalid
9260      * @param {String} msg The validation message
9261      */
9262     markInvalid : function(msg)
9263     {
9264         if(!this.el  || this.preventMark){ // not rendered
9265             return;
9266         }
9267         
9268         this.el.removeClass([this.invalidClass, this.validClass]);
9269         
9270         var feedback = this.el.select('.form-control-feedback', true).first();
9271             
9272         if(feedback){
9273             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9274         }
9275
9276         if(this.disabled){
9277             return;
9278         }
9279         
9280         if(this.allowBlank && !this.getRawValue().length){
9281             return;
9282         }
9283         
9284         if(this.indicator){
9285             this.indicator.removeClass('invisible');
9286             this.indicator.addClass('visible');
9287         }
9288         
9289         this.el.addClass(this.invalidClass);
9290         
9291         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9292             
9293             var feedback = this.el.select('.form-control-feedback', true).first();
9294             
9295             if(feedback){
9296                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9297                 
9298                 if(this.getValue().length || this.forceFeedback){
9299                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9300                 }
9301                 
9302             }
9303             
9304         }
9305         
9306         this.fireEvent('invalid', this, msg);
9307     },
9308     // private
9309     SafariOnKeyDown : function(event)
9310     {
9311         // this is a workaround for a password hang bug on chrome/ webkit.
9312         if (this.inputEl().dom.type != 'password') {
9313             return;
9314         }
9315         
9316         var isSelectAll = false;
9317         
9318         if(this.inputEl().dom.selectionEnd > 0){
9319             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9320         }
9321         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9322             event.preventDefault();
9323             this.setValue('');
9324             return;
9325         }
9326         
9327         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9328             
9329             event.preventDefault();
9330             // this is very hacky as keydown always get's upper case.
9331             //
9332             var cc = String.fromCharCode(event.getCharCode());
9333             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9334             
9335         }
9336     },
9337     adjustWidth : function(tag, w){
9338         tag = tag.toLowerCase();
9339         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9340             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9341                 if(tag == 'input'){
9342                     return w + 2;
9343                 }
9344                 if(tag == 'textarea'){
9345                     return w-2;
9346                 }
9347             }else if(Roo.isOpera){
9348                 if(tag == 'input'){
9349                     return w + 2;
9350                 }
9351                 if(tag == 'textarea'){
9352                     return w-2;
9353                 }
9354             }
9355         }
9356         return w;
9357     },
9358     
9359     setFieldLabel : function(v)
9360     {
9361         this.fieldLabel = v;
9362         
9363         if(this.rendered){
9364             this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9365         }
9366     }
9367 });
9368
9369  
9370 /*
9371  * - LGPL
9372  *
9373  * Input
9374  * 
9375  */
9376
9377 /**
9378  * @class Roo.bootstrap.TextArea
9379  * @extends Roo.bootstrap.Input
9380  * Bootstrap TextArea class
9381  * @cfg {Number} cols Specifies the visible width of a text area
9382  * @cfg {Number} rows Specifies the visible number of lines in a text area
9383  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9384  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9385  * @cfg {string} html text
9386  * 
9387  * @constructor
9388  * Create a new TextArea
9389  * @param {Object} config The config object
9390  */
9391
9392 Roo.bootstrap.TextArea = function(config){
9393     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9394    
9395 };
9396
9397 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9398      
9399     cols : false,
9400     rows : 5,
9401     readOnly : false,
9402     warp : 'soft',
9403     resize : false,
9404     value: false,
9405     html: false,
9406     
9407     getAutoCreate : function(){
9408         
9409         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9410         
9411         var id = Roo.id();
9412         
9413         var cfg = {};
9414         
9415         if(this.inputType != 'hidden'){
9416             cfg.cls = 'form-group' //input-group
9417         }
9418         
9419         var input =  {
9420             tag: 'textarea',
9421             id : id,
9422             warp : this.warp,
9423             rows : this.rows,
9424             value : this.value || '',
9425             html: this.html || '',
9426             cls : 'form-control',
9427             placeholder : this.placeholder || '' 
9428             
9429         };
9430         
9431         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9432             input.maxLength = this.maxLength;
9433         }
9434         
9435         if(this.resize){
9436             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9437         }
9438         
9439         if(this.cols){
9440             input.cols = this.cols;
9441         }
9442         
9443         if (this.readOnly) {
9444             input.readonly = true;
9445         }
9446         
9447         if (this.name) {
9448             input.name = this.name;
9449         }
9450         
9451         if (this.size) {
9452             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9453         }
9454         
9455         var settings=this;
9456         ['xs','sm','md','lg'].map(function(size){
9457             if (settings[size]) {
9458                 cfg.cls += ' col-' + size + '-' + settings[size];
9459             }
9460         });
9461         
9462         var inputblock = input;
9463         
9464         if(this.hasFeedback && !this.allowBlank){
9465             
9466             var feedback = {
9467                 tag: 'span',
9468                 cls: 'glyphicon form-control-feedback'
9469             };
9470
9471             inputblock = {
9472                 cls : 'has-feedback',
9473                 cn :  [
9474                     input,
9475                     feedback
9476                 ] 
9477             };  
9478         }
9479         
9480         
9481         if (this.before || this.after) {
9482             
9483             inputblock = {
9484                 cls : 'input-group',
9485                 cn :  [] 
9486             };
9487             if (this.before) {
9488                 inputblock.cn.push({
9489                     tag :'span',
9490                     cls : 'input-group-addon',
9491                     html : this.before
9492                 });
9493             }
9494             
9495             inputblock.cn.push(input);
9496             
9497             if(this.hasFeedback && !this.allowBlank){
9498                 inputblock.cls += ' has-feedback';
9499                 inputblock.cn.push(feedback);
9500             }
9501             
9502             if (this.after) {
9503                 inputblock.cn.push({
9504                     tag :'span',
9505                     cls : 'input-group-addon',
9506                     html : this.after
9507                 });
9508             }
9509             
9510         }
9511         
9512         if (align ==='left' && this.fieldLabel.length) {
9513             cfg.cn = [
9514                 {
9515                     tag: 'label',
9516                     'for' :  id,
9517                     cls : 'control-label',
9518                     html : this.fieldLabel
9519                 },
9520                 {
9521                     cls : "",
9522                     cn: [
9523                         inputblock
9524                     ]
9525                 }
9526
9527             ];
9528             
9529             if(this.labelWidth > 12){
9530                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9531             }
9532
9533             if(this.labelWidth < 13 && this.labelmd == 0){
9534                 this.labelmd = this.labelWidth;
9535             }
9536
9537             if(this.labellg > 0){
9538                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9539                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9540             }
9541
9542             if(this.labelmd > 0){
9543                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9544                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9545             }
9546
9547             if(this.labelsm > 0){
9548                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9549                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9550             }
9551
9552             if(this.labelxs > 0){
9553                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9554                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9555             }
9556             
9557         } else if ( this.fieldLabel.length) {
9558             cfg.cn = [
9559
9560                {
9561                    tag: 'label',
9562                    //cls : 'input-group-addon',
9563                    html : this.fieldLabel
9564
9565                },
9566
9567                inputblock
9568
9569            ];
9570
9571         } else {
9572
9573             cfg.cn = [
9574
9575                 inputblock
9576
9577             ];
9578                 
9579         }
9580         
9581         if (this.disabled) {
9582             input.disabled=true;
9583         }
9584         
9585         return cfg;
9586         
9587     },
9588     /**
9589      * return the real textarea element.
9590      */
9591     inputEl: function ()
9592     {
9593         return this.el.select('textarea.form-control',true).first();
9594     },
9595     
9596     /**
9597      * Clear any invalid styles/messages for this field
9598      */
9599     clearInvalid : function()
9600     {
9601         
9602         if(!this.el || this.preventMark){ // not rendered
9603             return;
9604         }
9605         
9606         var label = this.el.select('label', true).first();
9607         var icon = this.el.select('i.fa-star', true).first();
9608         
9609         if(label && icon){
9610             icon.remove();
9611         }
9612         
9613         this.el.removeClass(this.invalidClass);
9614         
9615         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9616             
9617             var feedback = this.el.select('.form-control-feedback', true).first();
9618             
9619             if(feedback){
9620                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9621             }
9622             
9623         }
9624         
9625         this.fireEvent('valid', this);
9626     },
9627     
9628      /**
9629      * Mark this field as valid
9630      */
9631     markValid : function()
9632     {
9633         if(!this.el  || this.preventMark){ // not rendered
9634             return;
9635         }
9636         
9637         this.el.removeClass([this.invalidClass, this.validClass]);
9638         
9639         var feedback = this.el.select('.form-control-feedback', true).first();
9640             
9641         if(feedback){
9642             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9643         }
9644
9645         if(this.disabled || this.allowBlank){
9646             return;
9647         }
9648         
9649         var label = this.el.select('label', true).first();
9650         var icon = this.el.select('i.fa-star', true).first();
9651         
9652         if(label && icon){
9653             icon.remove();
9654         }
9655         
9656         this.el.addClass(this.validClass);
9657         
9658         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9659             
9660             var feedback = this.el.select('.form-control-feedback', true).first();
9661             
9662             if(feedback){
9663                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9664                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9665             }
9666             
9667         }
9668         
9669         this.fireEvent('valid', this);
9670     },
9671     
9672      /**
9673      * Mark this field as invalid
9674      * @param {String} msg The validation message
9675      */
9676     markInvalid : function(msg)
9677     {
9678         if(!this.el  || this.preventMark){ // not rendered
9679             return;
9680         }
9681         
9682         this.el.removeClass([this.invalidClass, this.validClass]);
9683         
9684         var feedback = this.el.select('.form-control-feedback', true).first();
9685             
9686         if(feedback){
9687             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9688         }
9689
9690         if(this.disabled || this.allowBlank){
9691             return;
9692         }
9693         
9694         var label = this.el.select('label', true).first();
9695         var icon = this.el.select('i.fa-star', true).first();
9696         
9697         if(!this.getValue().length && label && !icon){
9698             this.el.createChild({
9699                 tag : 'i',
9700                 cls : 'text-danger fa fa-lg fa-star',
9701                 tooltip : 'This field is required',
9702                 style : 'margin-right:5px;'
9703             }, label, true);
9704         }
9705
9706         this.el.addClass(this.invalidClass);
9707         
9708         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9709             
9710             var feedback = this.el.select('.form-control-feedback', true).first();
9711             
9712             if(feedback){
9713                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9714                 
9715                 if(this.getValue().length || this.forceFeedback){
9716                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9717                 }
9718                 
9719             }
9720             
9721         }
9722         
9723         this.fireEvent('invalid', this, msg);
9724     }
9725 });
9726
9727  
9728 /*
9729  * - LGPL
9730  *
9731  * trigger field - base class for combo..
9732  * 
9733  */
9734  
9735 /**
9736  * @class Roo.bootstrap.TriggerField
9737  * @extends Roo.bootstrap.Input
9738  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9739  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9740  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9741  * for which you can provide a custom implementation.  For example:
9742  * <pre><code>
9743 var trigger = new Roo.bootstrap.TriggerField();
9744 trigger.onTriggerClick = myTriggerFn;
9745 trigger.applyTo('my-field');
9746 </code></pre>
9747  *
9748  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9749  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9750  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9751  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9752  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9753
9754  * @constructor
9755  * Create a new TriggerField.
9756  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9757  * to the base TextField)
9758  */
9759 Roo.bootstrap.TriggerField = function(config){
9760     this.mimicing = false;
9761     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9762 };
9763
9764 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9765     /**
9766      * @cfg {String} triggerClass A CSS class to apply to the trigger
9767      */
9768      /**
9769      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9770      */
9771     hideTrigger:false,
9772
9773     /**
9774      * @cfg {Boolean} removable (true|false) special filter default false
9775      */
9776     removable : false,
9777     
9778     /** @cfg {Boolean} grow @hide */
9779     /** @cfg {Number} growMin @hide */
9780     /** @cfg {Number} growMax @hide */
9781
9782     /**
9783      * @hide 
9784      * @method
9785      */
9786     autoSize: Roo.emptyFn,
9787     // private
9788     monitorTab : true,
9789     // private
9790     deferHeight : true,
9791
9792     
9793     actionMode : 'wrap',
9794     
9795     caret : false,
9796     
9797     
9798     getAutoCreate : function(){
9799        
9800         var align = this.labelAlign || this.parentLabelAlign();
9801         
9802         var id = Roo.id();
9803         
9804         var cfg = {
9805             cls: 'form-group' //input-group
9806         };
9807         
9808         
9809         var input =  {
9810             tag: 'input',
9811             id : id,
9812             type : this.inputType,
9813             cls : 'form-control',
9814             autocomplete: 'new-password',
9815             placeholder : this.placeholder || '' 
9816             
9817         };
9818         if (this.name) {
9819             input.name = this.name;
9820         }
9821         if (this.size) {
9822             input.cls += ' input-' + this.size;
9823         }
9824         
9825         if (this.disabled) {
9826             input.disabled=true;
9827         }
9828         
9829         var inputblock = input;
9830         
9831         if(this.hasFeedback && !this.allowBlank){
9832             
9833             var feedback = {
9834                 tag: 'span',
9835                 cls: 'glyphicon form-control-feedback'
9836             };
9837             
9838             if(this.removable && !this.editable && !this.tickable){
9839                 inputblock = {
9840                     cls : 'has-feedback',
9841                     cn :  [
9842                         inputblock,
9843                         {
9844                             tag: 'button',
9845                             html : 'x',
9846                             cls : 'roo-combo-removable-btn close'
9847                         },
9848                         feedback
9849                     ] 
9850                 };
9851             } else {
9852                 inputblock = {
9853                     cls : 'has-feedback',
9854                     cn :  [
9855                         inputblock,
9856                         feedback
9857                     ] 
9858                 };
9859             }
9860
9861         } else {
9862             if(this.removable && !this.editable && !this.tickable){
9863                 inputblock = {
9864                     cls : 'roo-removable',
9865                     cn :  [
9866                         inputblock,
9867                         {
9868                             tag: 'button',
9869                             html : 'x',
9870                             cls : 'roo-combo-removable-btn close'
9871                         }
9872                     ] 
9873                 };
9874             }
9875         }
9876         
9877         if (this.before || this.after) {
9878             
9879             inputblock = {
9880                 cls : 'input-group',
9881                 cn :  [] 
9882             };
9883             if (this.before) {
9884                 inputblock.cn.push({
9885                     tag :'span',
9886                     cls : 'input-group-addon',
9887                     html : this.before
9888                 });
9889             }
9890             
9891             inputblock.cn.push(input);
9892             
9893             if(this.hasFeedback && !this.allowBlank){
9894                 inputblock.cls += ' has-feedback';
9895                 inputblock.cn.push(feedback);
9896             }
9897             
9898             if (this.after) {
9899                 inputblock.cn.push({
9900                     tag :'span',
9901                     cls : 'input-group-addon',
9902                     html : this.after
9903                 });
9904             }
9905             
9906         };
9907         
9908         var box = {
9909             tag: 'div',
9910             cn: [
9911                 {
9912                     tag: 'input',
9913                     type : 'hidden',
9914                     cls: 'form-hidden-field'
9915                 },
9916                 inputblock
9917             ]
9918             
9919         };
9920         
9921         if(this.multiple){
9922             box = {
9923                 tag: 'div',
9924                 cn: [
9925                     {
9926                         tag: 'input',
9927                         type : 'hidden',
9928                         cls: 'form-hidden-field'
9929                     },
9930                     {
9931                         tag: 'ul',
9932                         cls: 'roo-select2-choices',
9933                         cn:[
9934                             {
9935                                 tag: 'li',
9936                                 cls: 'roo-select2-search-field',
9937                                 cn: [
9938
9939                                     inputblock
9940                                 ]
9941                             }
9942                         ]
9943                     }
9944                 ]
9945             }
9946         };
9947         
9948         var combobox = {
9949             cls: 'roo-select2-container input-group',
9950             cn: [
9951                 box
9952 //                {
9953 //                    tag: 'ul',
9954 //                    cls: 'typeahead typeahead-long dropdown-menu',
9955 //                    style: 'display:none'
9956 //                }
9957             ]
9958         };
9959         
9960         if(!this.multiple && this.showToggleBtn){
9961             
9962             var caret = {
9963                         tag: 'span',
9964                         cls: 'caret'
9965              };
9966             if (this.caret != false) {
9967                 caret = {
9968                      tag: 'i',
9969                      cls: 'fa fa-' + this.caret
9970                 };
9971                 
9972             }
9973             
9974             combobox.cn.push({
9975                 tag :'span',
9976                 cls : 'input-group-addon btn dropdown-toggle',
9977                 cn : [
9978                     caret,
9979                     {
9980                         tag: 'span',
9981                         cls: 'combobox-clear',
9982                         cn  : [
9983                             {
9984                                 tag : 'i',
9985                                 cls: 'icon-remove'
9986                             }
9987                         ]
9988                     }
9989                 ]
9990
9991             })
9992         }
9993         
9994         if(this.multiple){
9995             combobox.cls += ' roo-select2-container-multi';
9996         }
9997         
9998         if (align ==='left' && this.fieldLabel.length) {
9999             
10000             cfg.cls += ' roo-form-group-label-left';
10001
10002             cfg.cn = [
10003                 {
10004                     tag : 'i',
10005                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10006                     tooltip : 'This field is required'
10007                 },
10008                 {
10009                     tag: 'label',
10010                     'for' :  id,
10011                     cls : 'control-label',
10012                     html : this.fieldLabel
10013
10014                 },
10015                 {
10016                     cls : "", 
10017                     cn: [
10018                         combobox
10019                     ]
10020                 }
10021
10022             ];
10023             
10024             var labelCfg = cfg.cn[1];
10025             var contentCfg = cfg.cn[2];
10026             
10027             if(this.indicatorpos == 'right'){
10028                 cfg.cn = [
10029                     {
10030                         tag: 'label',
10031                         'for' :  id,
10032                         cls : 'control-label',
10033                         cn : [
10034                             {
10035                                 tag : 'span',
10036                                 html : this.fieldLabel
10037                             },
10038                             {
10039                                 tag : 'i',
10040                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10041                                 tooltip : 'This field is required'
10042                             }
10043                         ]
10044                     },
10045                     {
10046                         cls : "", 
10047                         cn: [
10048                             combobox
10049                         ]
10050                     }
10051
10052                 ];
10053                 
10054                 labelCfg = cfg.cn[0];
10055                 contentCfg = cfg.cn[1];
10056             }
10057             
10058             if(this.labelWidth > 12){
10059                 labelCfg.style = "width: " + this.labelWidth + 'px';
10060             }
10061             
10062             if(this.labelWidth < 13 && this.labelmd == 0){
10063                 this.labelmd = this.labelWidth;
10064             }
10065             
10066             if(this.labellg > 0){
10067                 labelCfg.cls += ' col-lg-' + this.labellg;
10068                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10069             }
10070             
10071             if(this.labelmd > 0){
10072                 labelCfg.cls += ' col-md-' + this.labelmd;
10073                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10074             }
10075             
10076             if(this.labelsm > 0){
10077                 labelCfg.cls += ' col-sm-' + this.labelsm;
10078                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10079             }
10080             
10081             if(this.labelxs > 0){
10082                 labelCfg.cls += ' col-xs-' + this.labelxs;
10083                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10084             }
10085             
10086         } else if ( this.fieldLabel.length) {
10087 //                Roo.log(" label");
10088             cfg.cn = [
10089                 {
10090                    tag : 'i',
10091                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10092                    tooltip : 'This field is required'
10093                },
10094                {
10095                    tag: 'label',
10096                    //cls : 'input-group-addon',
10097                    html : this.fieldLabel
10098
10099                },
10100
10101                combobox
10102
10103             ];
10104             
10105             if(this.indicatorpos == 'right'){
10106                 
10107                 cfg.cn = [
10108                     {
10109                        tag: 'label',
10110                        cn : [
10111                            {
10112                                tag : 'span',
10113                                html : this.fieldLabel
10114                            },
10115                            {
10116                               tag : 'i',
10117                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10118                               tooltip : 'This field is required'
10119                            }
10120                        ]
10121
10122                     },
10123                     combobox
10124
10125                 ];
10126
10127             }
10128
10129         } else {
10130             
10131 //                Roo.log(" no label && no align");
10132                 cfg = combobox
10133                      
10134                 
10135         }
10136         
10137         var settings=this;
10138         ['xs','sm','md','lg'].map(function(size){
10139             if (settings[size]) {
10140                 cfg.cls += ' col-' + size + '-' + settings[size];
10141             }
10142         });
10143         
10144         return cfg;
10145         
10146     },
10147     
10148     
10149     
10150     // private
10151     onResize : function(w, h){
10152 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10153 //        if(typeof w == 'number'){
10154 //            var x = w - this.trigger.getWidth();
10155 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10156 //            this.trigger.setStyle('left', x+'px');
10157 //        }
10158     },
10159
10160     // private
10161     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10162
10163     // private
10164     getResizeEl : function(){
10165         return this.inputEl();
10166     },
10167
10168     // private
10169     getPositionEl : function(){
10170         return this.inputEl();
10171     },
10172
10173     // private
10174     alignErrorIcon : function(){
10175         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10176     },
10177
10178     // private
10179     initEvents : function(){
10180         
10181         this.createList();
10182         
10183         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10184         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10185         if(!this.multiple && this.showToggleBtn){
10186             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10187             if(this.hideTrigger){
10188                 this.trigger.setDisplayed(false);
10189             }
10190             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10191         }
10192         
10193         if(this.multiple){
10194             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10195         }
10196         
10197         if(this.removable && !this.editable && !this.tickable){
10198             var close = this.closeTriggerEl();
10199             
10200             if(close){
10201                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10202                 close.on('click', this.removeBtnClick, this, close);
10203             }
10204         }
10205         
10206         //this.trigger.addClassOnOver('x-form-trigger-over');
10207         //this.trigger.addClassOnClick('x-form-trigger-click');
10208         
10209         //if(!this.width){
10210         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10211         //}
10212     },
10213     
10214     closeTriggerEl : function()
10215     {
10216         var close = this.el.select('.roo-combo-removable-btn', true).first();
10217         return close ? close : false;
10218     },
10219     
10220     removeBtnClick : function(e, h, el)
10221     {
10222         e.preventDefault();
10223         
10224         if(this.fireEvent("remove", this) !== false){
10225             this.reset();
10226             this.fireEvent("afterremove", this)
10227         }
10228     },
10229     
10230     createList : function()
10231     {
10232         this.list = Roo.get(document.body).createChild({
10233             tag: 'ul',
10234             cls: 'typeahead typeahead-long dropdown-menu',
10235             style: 'display:none'
10236         });
10237         
10238         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10239         
10240     },
10241
10242     // private
10243     initTrigger : function(){
10244        
10245     },
10246
10247     // private
10248     onDestroy : function(){
10249         if(this.trigger){
10250             this.trigger.removeAllListeners();
10251           //  this.trigger.remove();
10252         }
10253         //if(this.wrap){
10254         //    this.wrap.remove();
10255         //}
10256         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10257     },
10258
10259     // private
10260     onFocus : function(){
10261         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10262         /*
10263         if(!this.mimicing){
10264             this.wrap.addClass('x-trigger-wrap-focus');
10265             this.mimicing = true;
10266             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10267             if(this.monitorTab){
10268                 this.el.on("keydown", this.checkTab, this);
10269             }
10270         }
10271         */
10272     },
10273
10274     // private
10275     checkTab : function(e){
10276         if(e.getKey() == e.TAB){
10277             this.triggerBlur();
10278         }
10279     },
10280
10281     // private
10282     onBlur : function(){
10283         // do nothing
10284     },
10285
10286     // private
10287     mimicBlur : function(e, t){
10288         /*
10289         if(!this.wrap.contains(t) && this.validateBlur()){
10290             this.triggerBlur();
10291         }
10292         */
10293     },
10294
10295     // private
10296     triggerBlur : function(){
10297         this.mimicing = false;
10298         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10299         if(this.monitorTab){
10300             this.el.un("keydown", this.checkTab, this);
10301         }
10302         //this.wrap.removeClass('x-trigger-wrap-focus');
10303         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10304     },
10305
10306     // private
10307     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10308     validateBlur : function(e, t){
10309         return true;
10310     },
10311
10312     // private
10313     onDisable : function(){
10314         this.inputEl().dom.disabled = true;
10315         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10316         //if(this.wrap){
10317         //    this.wrap.addClass('x-item-disabled');
10318         //}
10319     },
10320
10321     // private
10322     onEnable : function(){
10323         this.inputEl().dom.disabled = false;
10324         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10325         //if(this.wrap){
10326         //    this.el.removeClass('x-item-disabled');
10327         //}
10328     },
10329
10330     // private
10331     onShow : function(){
10332         var ae = this.getActionEl();
10333         
10334         if(ae){
10335             ae.dom.style.display = '';
10336             ae.dom.style.visibility = 'visible';
10337         }
10338     },
10339
10340     // private
10341     
10342     onHide : function(){
10343         var ae = this.getActionEl();
10344         ae.dom.style.display = 'none';
10345     },
10346
10347     /**
10348      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10349      * by an implementing function.
10350      * @method
10351      * @param {EventObject} e
10352      */
10353     onTriggerClick : Roo.emptyFn
10354 });
10355  /*
10356  * Based on:
10357  * Ext JS Library 1.1.1
10358  * Copyright(c) 2006-2007, Ext JS, LLC.
10359  *
10360  * Originally Released Under LGPL - original licence link has changed is not relivant.
10361  *
10362  * Fork - LGPL
10363  * <script type="text/javascript">
10364  */
10365
10366
10367 /**
10368  * @class Roo.data.SortTypes
10369  * @singleton
10370  * Defines the default sorting (casting?) comparison functions used when sorting data.
10371  */
10372 Roo.data.SortTypes = {
10373     /**
10374      * Default sort that does nothing
10375      * @param {Mixed} s The value being converted
10376      * @return {Mixed} The comparison value
10377      */
10378     none : function(s){
10379         return s;
10380     },
10381     
10382     /**
10383      * The regular expression used to strip tags
10384      * @type {RegExp}
10385      * @property
10386      */
10387     stripTagsRE : /<\/?[^>]+>/gi,
10388     
10389     /**
10390      * Strips all HTML tags to sort on text only
10391      * @param {Mixed} s The value being converted
10392      * @return {String} The comparison value
10393      */
10394     asText : function(s){
10395         return String(s).replace(this.stripTagsRE, "");
10396     },
10397     
10398     /**
10399      * Strips all HTML tags to sort on text only - Case insensitive
10400      * @param {Mixed} s The value being converted
10401      * @return {String} The comparison value
10402      */
10403     asUCText : function(s){
10404         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10405     },
10406     
10407     /**
10408      * Case insensitive string
10409      * @param {Mixed} s The value being converted
10410      * @return {String} The comparison value
10411      */
10412     asUCString : function(s) {
10413         return String(s).toUpperCase();
10414     },
10415     
10416     /**
10417      * Date sorting
10418      * @param {Mixed} s The value being converted
10419      * @return {Number} The comparison value
10420      */
10421     asDate : function(s) {
10422         if(!s){
10423             return 0;
10424         }
10425         if(s instanceof Date){
10426             return s.getTime();
10427         }
10428         return Date.parse(String(s));
10429     },
10430     
10431     /**
10432      * Float sorting
10433      * @param {Mixed} s The value being converted
10434      * @return {Float} The comparison value
10435      */
10436     asFloat : function(s) {
10437         var val = parseFloat(String(s).replace(/,/g, ""));
10438         if(isNaN(val)) {
10439             val = 0;
10440         }
10441         return val;
10442     },
10443     
10444     /**
10445      * Integer sorting
10446      * @param {Mixed} s The value being converted
10447      * @return {Number} The comparison value
10448      */
10449     asInt : function(s) {
10450         var val = parseInt(String(s).replace(/,/g, ""));
10451         if(isNaN(val)) {
10452             val = 0;
10453         }
10454         return val;
10455     }
10456 };/*
10457  * Based on:
10458  * Ext JS Library 1.1.1
10459  * Copyright(c) 2006-2007, Ext JS, LLC.
10460  *
10461  * Originally Released Under LGPL - original licence link has changed is not relivant.
10462  *
10463  * Fork - LGPL
10464  * <script type="text/javascript">
10465  */
10466
10467 /**
10468 * @class Roo.data.Record
10469  * Instances of this class encapsulate both record <em>definition</em> information, and record
10470  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10471  * to access Records cached in an {@link Roo.data.Store} object.<br>
10472  * <p>
10473  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10474  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10475  * objects.<br>
10476  * <p>
10477  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10478  * @constructor
10479  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10480  * {@link #create}. The parameters are the same.
10481  * @param {Array} data An associative Array of data values keyed by the field name.
10482  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10483  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10484  * not specified an integer id is generated.
10485  */
10486 Roo.data.Record = function(data, id){
10487     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10488     this.data = data;
10489 };
10490
10491 /**
10492  * Generate a constructor for a specific record layout.
10493  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10494  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10495  * Each field definition object may contain the following properties: <ul>
10496  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
10497  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10498  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10499  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10500  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10501  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10502  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10503  * this may be omitted.</p></li>
10504  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10505  * <ul><li>auto (Default, implies no conversion)</li>
10506  * <li>string</li>
10507  * <li>int</li>
10508  * <li>float</li>
10509  * <li>boolean</li>
10510  * <li>date</li></ul></p></li>
10511  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10512  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10513  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10514  * by the Reader into an object that will be stored in the Record. It is passed the
10515  * following parameters:<ul>
10516  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10517  * </ul></p></li>
10518  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10519  * </ul>
10520  * <br>usage:<br><pre><code>
10521 var TopicRecord = Roo.data.Record.create(
10522     {name: 'title', mapping: 'topic_title'},
10523     {name: 'author', mapping: 'username'},
10524     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10525     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10526     {name: 'lastPoster', mapping: 'user2'},
10527     {name: 'excerpt', mapping: 'post_text'}
10528 );
10529
10530 var myNewRecord = new TopicRecord({
10531     title: 'Do my job please',
10532     author: 'noobie',
10533     totalPosts: 1,
10534     lastPost: new Date(),
10535     lastPoster: 'Animal',
10536     excerpt: 'No way dude!'
10537 });
10538 myStore.add(myNewRecord);
10539 </code></pre>
10540  * @method create
10541  * @static
10542  */
10543 Roo.data.Record.create = function(o){
10544     var f = function(){
10545         f.superclass.constructor.apply(this, arguments);
10546     };
10547     Roo.extend(f, Roo.data.Record);
10548     var p = f.prototype;
10549     p.fields = new Roo.util.MixedCollection(false, function(field){
10550         return field.name;
10551     });
10552     for(var i = 0, len = o.length; i < len; i++){
10553         p.fields.add(new Roo.data.Field(o[i]));
10554     }
10555     f.getField = function(name){
10556         return p.fields.get(name);  
10557     };
10558     return f;
10559 };
10560
10561 Roo.data.Record.AUTO_ID = 1000;
10562 Roo.data.Record.EDIT = 'edit';
10563 Roo.data.Record.REJECT = 'reject';
10564 Roo.data.Record.COMMIT = 'commit';
10565
10566 Roo.data.Record.prototype = {
10567     /**
10568      * Readonly flag - true if this record has been modified.
10569      * @type Boolean
10570      */
10571     dirty : false,
10572     editing : false,
10573     error: null,
10574     modified: null,
10575
10576     // private
10577     join : function(store){
10578         this.store = store;
10579     },
10580
10581     /**
10582      * Set the named field to the specified value.
10583      * @param {String} name The name of the field to set.
10584      * @param {Object} value The value to set the field to.
10585      */
10586     set : function(name, value){
10587         if(this.data[name] == value){
10588             return;
10589         }
10590         this.dirty = true;
10591         if(!this.modified){
10592             this.modified = {};
10593         }
10594         if(typeof this.modified[name] == 'undefined'){
10595             this.modified[name] = this.data[name];
10596         }
10597         this.data[name] = value;
10598         if(!this.editing && this.store){
10599             this.store.afterEdit(this);
10600         }       
10601     },
10602
10603     /**
10604      * Get the value of the named field.
10605      * @param {String} name The name of the field to get the value of.
10606      * @return {Object} The value of the field.
10607      */
10608     get : function(name){
10609         return this.data[name]; 
10610     },
10611
10612     // private
10613     beginEdit : function(){
10614         this.editing = true;
10615         this.modified = {}; 
10616     },
10617
10618     // private
10619     cancelEdit : function(){
10620         this.editing = false;
10621         delete this.modified;
10622     },
10623
10624     // private
10625     endEdit : function(){
10626         this.editing = false;
10627         if(this.dirty && this.store){
10628             this.store.afterEdit(this);
10629         }
10630     },
10631
10632     /**
10633      * Usually called by the {@link Roo.data.Store} which owns the Record.
10634      * Rejects all changes made to the Record since either creation, or the last commit operation.
10635      * Modified fields are reverted to their original values.
10636      * <p>
10637      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10638      * of reject operations.
10639      */
10640     reject : function(){
10641         var m = this.modified;
10642         for(var n in m){
10643             if(typeof m[n] != "function"){
10644                 this.data[n] = m[n];
10645             }
10646         }
10647         this.dirty = false;
10648         delete this.modified;
10649         this.editing = false;
10650         if(this.store){
10651             this.store.afterReject(this);
10652         }
10653     },
10654
10655     /**
10656      * Usually called by the {@link Roo.data.Store} which owns the Record.
10657      * Commits all changes made to the Record since either creation, or the last commit operation.
10658      * <p>
10659      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10660      * of commit operations.
10661      */
10662     commit : function(){
10663         this.dirty = false;
10664         delete this.modified;
10665         this.editing = false;
10666         if(this.store){
10667             this.store.afterCommit(this);
10668         }
10669     },
10670
10671     // private
10672     hasError : function(){
10673         return this.error != null;
10674     },
10675
10676     // private
10677     clearError : function(){
10678         this.error = null;
10679     },
10680
10681     /**
10682      * Creates a copy of this record.
10683      * @param {String} id (optional) A new record id if you don't want to use this record's id
10684      * @return {Record}
10685      */
10686     copy : function(newId) {
10687         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10688     }
10689 };/*
10690  * Based on:
10691  * Ext JS Library 1.1.1
10692  * Copyright(c) 2006-2007, Ext JS, LLC.
10693  *
10694  * Originally Released Under LGPL - original licence link has changed is not relivant.
10695  *
10696  * Fork - LGPL
10697  * <script type="text/javascript">
10698  */
10699
10700
10701
10702 /**
10703  * @class Roo.data.Store
10704  * @extends Roo.util.Observable
10705  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10706  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10707  * <p>
10708  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
10709  * has no knowledge of the format of the data returned by the Proxy.<br>
10710  * <p>
10711  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10712  * instances from the data object. These records are cached and made available through accessor functions.
10713  * @constructor
10714  * Creates a new Store.
10715  * @param {Object} config A config object containing the objects needed for the Store to access data,
10716  * and read the data into Records.
10717  */
10718 Roo.data.Store = function(config){
10719     this.data = new Roo.util.MixedCollection(false);
10720     this.data.getKey = function(o){
10721         return o.id;
10722     };
10723     this.baseParams = {};
10724     // private
10725     this.paramNames = {
10726         "start" : "start",
10727         "limit" : "limit",
10728         "sort" : "sort",
10729         "dir" : "dir",
10730         "multisort" : "_multisort"
10731     };
10732
10733     if(config && config.data){
10734         this.inlineData = config.data;
10735         delete config.data;
10736     }
10737
10738     Roo.apply(this, config);
10739     
10740     if(this.reader){ // reader passed
10741         this.reader = Roo.factory(this.reader, Roo.data);
10742         this.reader.xmodule = this.xmodule || false;
10743         if(!this.recordType){
10744             this.recordType = this.reader.recordType;
10745         }
10746         if(this.reader.onMetaChange){
10747             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10748         }
10749     }
10750
10751     if(this.recordType){
10752         this.fields = this.recordType.prototype.fields;
10753     }
10754     this.modified = [];
10755
10756     this.addEvents({
10757         /**
10758          * @event datachanged
10759          * Fires when the data cache has changed, and a widget which is using this Store
10760          * as a Record cache should refresh its view.
10761          * @param {Store} this
10762          */
10763         datachanged : true,
10764         /**
10765          * @event metachange
10766          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10767          * @param {Store} this
10768          * @param {Object} meta The JSON metadata
10769          */
10770         metachange : true,
10771         /**
10772          * @event add
10773          * Fires when Records have been added to the Store
10774          * @param {Store} this
10775          * @param {Roo.data.Record[]} records The array of Records added
10776          * @param {Number} index The index at which the record(s) were added
10777          */
10778         add : true,
10779         /**
10780          * @event remove
10781          * Fires when a Record has been removed from the Store
10782          * @param {Store} this
10783          * @param {Roo.data.Record} record The Record that was removed
10784          * @param {Number} index The index at which the record was removed
10785          */
10786         remove : true,
10787         /**
10788          * @event update
10789          * Fires when a Record has been updated
10790          * @param {Store} this
10791          * @param {Roo.data.Record} record The Record that was updated
10792          * @param {String} operation The update operation being performed.  Value may be one of:
10793          * <pre><code>
10794  Roo.data.Record.EDIT
10795  Roo.data.Record.REJECT
10796  Roo.data.Record.COMMIT
10797          * </code></pre>
10798          */
10799         update : true,
10800         /**
10801          * @event clear
10802          * Fires when the data cache has been cleared.
10803          * @param {Store} this
10804          */
10805         clear : true,
10806         /**
10807          * @event beforeload
10808          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10809          * the load action will be canceled.
10810          * @param {Store} this
10811          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10812          */
10813         beforeload : true,
10814         /**
10815          * @event beforeloadadd
10816          * Fires after a new set of Records has been loaded.
10817          * @param {Store} this
10818          * @param {Roo.data.Record[]} records The Records that were loaded
10819          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10820          */
10821         beforeloadadd : true,
10822         /**
10823          * @event load
10824          * Fires after a new set of Records has been loaded, before they are added to the store.
10825          * @param {Store} this
10826          * @param {Roo.data.Record[]} records The Records that were loaded
10827          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10828          * @params {Object} return from reader
10829          */
10830         load : true,
10831         /**
10832          * @event loadexception
10833          * Fires if an exception occurs in the Proxy during loading.
10834          * Called with the signature of the Proxy's "loadexception" event.
10835          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10836          * 
10837          * @param {Proxy} 
10838          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10839          * @param {Object} load options 
10840          * @param {Object} jsonData from your request (normally this contains the Exception)
10841          */
10842         loadexception : true
10843     });
10844     
10845     if(this.proxy){
10846         this.proxy = Roo.factory(this.proxy, Roo.data);
10847         this.proxy.xmodule = this.xmodule || false;
10848         this.relayEvents(this.proxy,  ["loadexception"]);
10849     }
10850     this.sortToggle = {};
10851     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10852
10853     Roo.data.Store.superclass.constructor.call(this);
10854
10855     if(this.inlineData){
10856         this.loadData(this.inlineData);
10857         delete this.inlineData;
10858     }
10859 };
10860
10861 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10862      /**
10863     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10864     * without a remote query - used by combo/forms at present.
10865     */
10866     
10867     /**
10868     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10869     */
10870     /**
10871     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10872     */
10873     /**
10874     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10875     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10876     */
10877     /**
10878     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10879     * on any HTTP request
10880     */
10881     /**
10882     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10883     */
10884     /**
10885     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10886     */
10887     multiSort: false,
10888     /**
10889     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10890     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10891     */
10892     remoteSort : false,
10893
10894     /**
10895     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10896      * loaded or when a record is removed. (defaults to false).
10897     */
10898     pruneModifiedRecords : false,
10899
10900     // private
10901     lastOptions : null,
10902
10903     /**
10904      * Add Records to the Store and fires the add event.
10905      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10906      */
10907     add : function(records){
10908         records = [].concat(records);
10909         for(var i = 0, len = records.length; i < len; i++){
10910             records[i].join(this);
10911         }
10912         var index = this.data.length;
10913         this.data.addAll(records);
10914         this.fireEvent("add", this, records, index);
10915     },
10916
10917     /**
10918      * Remove a Record from the Store and fires the remove event.
10919      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10920      */
10921     remove : function(record){
10922         var index = this.data.indexOf(record);
10923         this.data.removeAt(index);
10924         if(this.pruneModifiedRecords){
10925             this.modified.remove(record);
10926         }
10927         this.fireEvent("remove", this, record, index);
10928     },
10929
10930     /**
10931      * Remove all Records from the Store and fires the clear event.
10932      */
10933     removeAll : function(){
10934         this.data.clear();
10935         if(this.pruneModifiedRecords){
10936             this.modified = [];
10937         }
10938         this.fireEvent("clear", this);
10939     },
10940
10941     /**
10942      * Inserts Records to the Store at the given index and fires the add event.
10943      * @param {Number} index The start index at which to insert the passed Records.
10944      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10945      */
10946     insert : function(index, records){
10947         records = [].concat(records);
10948         for(var i = 0, len = records.length; i < len; i++){
10949             this.data.insert(index, records[i]);
10950             records[i].join(this);
10951         }
10952         this.fireEvent("add", this, records, index);
10953     },
10954
10955     /**
10956      * Get the index within the cache of the passed Record.
10957      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10958      * @return {Number} The index of the passed Record. Returns -1 if not found.
10959      */
10960     indexOf : function(record){
10961         return this.data.indexOf(record);
10962     },
10963
10964     /**
10965      * Get the index within the cache of the Record with the passed id.
10966      * @param {String} id The id of the Record to find.
10967      * @return {Number} The index of the Record. Returns -1 if not found.
10968      */
10969     indexOfId : function(id){
10970         return this.data.indexOfKey(id);
10971     },
10972
10973     /**
10974      * Get the Record with the specified id.
10975      * @param {String} id The id of the Record to find.
10976      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10977      */
10978     getById : function(id){
10979         return this.data.key(id);
10980     },
10981
10982     /**
10983      * Get the Record at the specified index.
10984      * @param {Number} index The index of the Record to find.
10985      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10986      */
10987     getAt : function(index){
10988         return this.data.itemAt(index);
10989     },
10990
10991     /**
10992      * Returns a range of Records between specified indices.
10993      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10994      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10995      * @return {Roo.data.Record[]} An array of Records
10996      */
10997     getRange : function(start, end){
10998         return this.data.getRange(start, end);
10999     },
11000
11001     // private
11002     storeOptions : function(o){
11003         o = Roo.apply({}, o);
11004         delete o.callback;
11005         delete o.scope;
11006         this.lastOptions = o;
11007     },
11008
11009     /**
11010      * Loads the Record cache from the configured Proxy using the configured Reader.
11011      * <p>
11012      * If using remote paging, then the first load call must specify the <em>start</em>
11013      * and <em>limit</em> properties in the options.params property to establish the initial
11014      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11015      * <p>
11016      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11017      * and this call will return before the new data has been loaded. Perform any post-processing
11018      * in a callback function, or in a "load" event handler.</strong>
11019      * <p>
11020      * @param {Object} options An object containing properties which control loading options:<ul>
11021      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11022      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11023      * passed the following arguments:<ul>
11024      * <li>r : Roo.data.Record[]</li>
11025      * <li>options: Options object from the load call</li>
11026      * <li>success: Boolean success indicator</li></ul></li>
11027      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11028      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11029      * </ul>
11030      */
11031     load : function(options){
11032         options = options || {};
11033         if(this.fireEvent("beforeload", this, options) !== false){
11034             this.storeOptions(options);
11035             var p = Roo.apply(options.params || {}, this.baseParams);
11036             // if meta was not loaded from remote source.. try requesting it.
11037             if (!this.reader.metaFromRemote) {
11038                 p._requestMeta = 1;
11039             }
11040             if(this.sortInfo && this.remoteSort){
11041                 var pn = this.paramNames;
11042                 p[pn["sort"]] = this.sortInfo.field;
11043                 p[pn["dir"]] = this.sortInfo.direction;
11044             }
11045             if (this.multiSort) {
11046                 var pn = this.paramNames;
11047                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11048             }
11049             
11050             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11051         }
11052     },
11053
11054     /**
11055      * Reloads the Record cache from the configured Proxy using the configured Reader and
11056      * the options from the last load operation performed.
11057      * @param {Object} options (optional) An object containing properties which may override the options
11058      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11059      * the most recently used options are reused).
11060      */
11061     reload : function(options){
11062         this.load(Roo.applyIf(options||{}, this.lastOptions));
11063     },
11064
11065     // private
11066     // Called as a callback by the Reader during a load operation.
11067     loadRecords : function(o, options, success){
11068         if(!o || success === false){
11069             if(success !== false){
11070                 this.fireEvent("load", this, [], options, o);
11071             }
11072             if(options.callback){
11073                 options.callback.call(options.scope || this, [], options, false);
11074             }
11075             return;
11076         }
11077         // if data returned failure - throw an exception.
11078         if (o.success === false) {
11079             // show a message if no listener is registered.
11080             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11081                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11082             }
11083             // loadmask wil be hooked into this..
11084             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11085             return;
11086         }
11087         var r = o.records, t = o.totalRecords || r.length;
11088         
11089         this.fireEvent("beforeloadadd", this, r, options, o);
11090         
11091         if(!options || options.add !== true){
11092             if(this.pruneModifiedRecords){
11093                 this.modified = [];
11094             }
11095             for(var i = 0, len = r.length; i < len; i++){
11096                 r[i].join(this);
11097             }
11098             if(this.snapshot){
11099                 this.data = this.snapshot;
11100                 delete this.snapshot;
11101             }
11102             this.data.clear();
11103             this.data.addAll(r);
11104             this.totalLength = t;
11105             this.applySort();
11106             this.fireEvent("datachanged", this);
11107         }else{
11108             this.totalLength = Math.max(t, this.data.length+r.length);
11109             this.add(r);
11110         }
11111         
11112         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11113                 
11114             var e = new Roo.data.Record({});
11115
11116             e.set(this.parent.displayField, this.parent.emptyTitle);
11117             e.set(this.parent.valueField, '');
11118
11119             this.insert(0, e);
11120         }
11121             
11122         this.fireEvent("load", this, r, options, o);
11123         if(options.callback){
11124             options.callback.call(options.scope || this, r, options, true);
11125         }
11126     },
11127
11128
11129     /**
11130      * Loads data from a passed data block. A Reader which understands the format of the data
11131      * must have been configured in the constructor.
11132      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11133      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11134      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11135      */
11136     loadData : function(o, append){
11137         var r = this.reader.readRecords(o);
11138         this.loadRecords(r, {add: append}, true);
11139     },
11140
11141     /**
11142      * Gets the number of cached records.
11143      * <p>
11144      * <em>If using paging, this may not be the total size of the dataset. If the data object
11145      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11146      * the data set size</em>
11147      */
11148     getCount : function(){
11149         return this.data.length || 0;
11150     },
11151
11152     /**
11153      * Gets the total number of records in the dataset as returned by the server.
11154      * <p>
11155      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11156      * the dataset size</em>
11157      */
11158     getTotalCount : function(){
11159         return this.totalLength || 0;
11160     },
11161
11162     /**
11163      * Returns the sort state of the Store as an object with two properties:
11164      * <pre><code>
11165  field {String} The name of the field by which the Records are sorted
11166  direction {String} The sort order, "ASC" or "DESC"
11167      * </code></pre>
11168      */
11169     getSortState : function(){
11170         return this.sortInfo;
11171     },
11172
11173     // private
11174     applySort : function(){
11175         if(this.sortInfo && !this.remoteSort){
11176             var s = this.sortInfo, f = s.field;
11177             var st = this.fields.get(f).sortType;
11178             var fn = function(r1, r2){
11179                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11180                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11181             };
11182             this.data.sort(s.direction, fn);
11183             if(this.snapshot && this.snapshot != this.data){
11184                 this.snapshot.sort(s.direction, fn);
11185             }
11186         }
11187     },
11188
11189     /**
11190      * Sets the default sort column and order to be used by the next load operation.
11191      * @param {String} fieldName The name of the field to sort by.
11192      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11193      */
11194     setDefaultSort : function(field, dir){
11195         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11196     },
11197
11198     /**
11199      * Sort the Records.
11200      * If remote sorting is used, the sort is performed on the server, and the cache is
11201      * reloaded. If local sorting is used, the cache is sorted internally.
11202      * @param {String} fieldName The name of the field to sort by.
11203      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11204      */
11205     sort : function(fieldName, dir){
11206         var f = this.fields.get(fieldName);
11207         if(!dir){
11208             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11209             
11210             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11211                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11212             }else{
11213                 dir = f.sortDir;
11214             }
11215         }
11216         this.sortToggle[f.name] = dir;
11217         this.sortInfo = {field: f.name, direction: dir};
11218         if(!this.remoteSort){
11219             this.applySort();
11220             this.fireEvent("datachanged", this);
11221         }else{
11222             this.load(this.lastOptions);
11223         }
11224     },
11225
11226     /**
11227      * Calls the specified function for each of the Records in the cache.
11228      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11229      * Returning <em>false</em> aborts and exits the iteration.
11230      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11231      */
11232     each : function(fn, scope){
11233         this.data.each(fn, scope);
11234     },
11235
11236     /**
11237      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11238      * (e.g., during paging).
11239      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11240      */
11241     getModifiedRecords : function(){
11242         return this.modified;
11243     },
11244
11245     // private
11246     createFilterFn : function(property, value, anyMatch){
11247         if(!value.exec){ // not a regex
11248             value = String(value);
11249             if(value.length == 0){
11250                 return false;
11251             }
11252             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11253         }
11254         return function(r){
11255             return value.test(r.data[property]);
11256         };
11257     },
11258
11259     /**
11260      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11261      * @param {String} property A field on your records
11262      * @param {Number} start The record index to start at (defaults to 0)
11263      * @param {Number} end The last record index to include (defaults to length - 1)
11264      * @return {Number} The sum
11265      */
11266     sum : function(property, start, end){
11267         var rs = this.data.items, v = 0;
11268         start = start || 0;
11269         end = (end || end === 0) ? end : rs.length-1;
11270
11271         for(var i = start; i <= end; i++){
11272             v += (rs[i].data[property] || 0);
11273         }
11274         return v;
11275     },
11276
11277     /**
11278      * Filter the records by a specified property.
11279      * @param {String} field A field on your records
11280      * @param {String/RegExp} value Either a string that the field
11281      * should start with or a RegExp to test against the field
11282      * @param {Boolean} anyMatch True to match any part not just the beginning
11283      */
11284     filter : function(property, value, anyMatch){
11285         var fn = this.createFilterFn(property, value, anyMatch);
11286         return fn ? this.filterBy(fn) : this.clearFilter();
11287     },
11288
11289     /**
11290      * Filter by a function. The specified function will be called with each
11291      * record in this data source. If the function returns true the record is included,
11292      * otherwise it is filtered.
11293      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11294      * @param {Object} scope (optional) The scope of the function (defaults to this)
11295      */
11296     filterBy : function(fn, scope){
11297         this.snapshot = this.snapshot || this.data;
11298         this.data = this.queryBy(fn, scope||this);
11299         this.fireEvent("datachanged", this);
11300     },
11301
11302     /**
11303      * Query the records by a specified property.
11304      * @param {String} field A field on your records
11305      * @param {String/RegExp} value Either a string that the field
11306      * should start with or a RegExp to test against the field
11307      * @param {Boolean} anyMatch True to match any part not just the beginning
11308      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11309      */
11310     query : function(property, value, anyMatch){
11311         var fn = this.createFilterFn(property, value, anyMatch);
11312         return fn ? this.queryBy(fn) : this.data.clone();
11313     },
11314
11315     /**
11316      * Query by a function. The specified function will be called with each
11317      * record in this data source. If the function returns true the record is included
11318      * in the results.
11319      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11320      * @param {Object} scope (optional) The scope of the function (defaults to this)
11321       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11322      **/
11323     queryBy : function(fn, scope){
11324         var data = this.snapshot || this.data;
11325         return data.filterBy(fn, scope||this);
11326     },
11327
11328     /**
11329      * Collects unique values for a particular dataIndex from this store.
11330      * @param {String} dataIndex The property to collect
11331      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11332      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11333      * @return {Array} An array of the unique values
11334      **/
11335     collect : function(dataIndex, allowNull, bypassFilter){
11336         var d = (bypassFilter === true && this.snapshot) ?
11337                 this.snapshot.items : this.data.items;
11338         var v, sv, r = [], l = {};
11339         for(var i = 0, len = d.length; i < len; i++){
11340             v = d[i].data[dataIndex];
11341             sv = String(v);
11342             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11343                 l[sv] = true;
11344                 r[r.length] = v;
11345             }
11346         }
11347         return r;
11348     },
11349
11350     /**
11351      * Revert to a view of the Record cache with no filtering applied.
11352      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11353      */
11354     clearFilter : function(suppressEvent){
11355         if(this.snapshot && this.snapshot != this.data){
11356             this.data = this.snapshot;
11357             delete this.snapshot;
11358             if(suppressEvent !== true){
11359                 this.fireEvent("datachanged", this);
11360             }
11361         }
11362     },
11363
11364     // private
11365     afterEdit : function(record){
11366         if(this.modified.indexOf(record) == -1){
11367             this.modified.push(record);
11368         }
11369         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11370     },
11371     
11372     // private
11373     afterReject : function(record){
11374         this.modified.remove(record);
11375         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11376     },
11377
11378     // private
11379     afterCommit : function(record){
11380         this.modified.remove(record);
11381         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11382     },
11383
11384     /**
11385      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11386      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11387      */
11388     commitChanges : function(){
11389         var m = this.modified.slice(0);
11390         this.modified = [];
11391         for(var i = 0, len = m.length; i < len; i++){
11392             m[i].commit();
11393         }
11394     },
11395
11396     /**
11397      * Cancel outstanding changes on all changed records.
11398      */
11399     rejectChanges : function(){
11400         var m = this.modified.slice(0);
11401         this.modified = [];
11402         for(var i = 0, len = m.length; i < len; i++){
11403             m[i].reject();
11404         }
11405     },
11406
11407     onMetaChange : function(meta, rtype, o){
11408         this.recordType = rtype;
11409         this.fields = rtype.prototype.fields;
11410         delete this.snapshot;
11411         this.sortInfo = meta.sortInfo || this.sortInfo;
11412         this.modified = [];
11413         this.fireEvent('metachange', this, this.reader.meta);
11414     },
11415     
11416     moveIndex : function(data, type)
11417     {
11418         var index = this.indexOf(data);
11419         
11420         var newIndex = index + type;
11421         
11422         this.remove(data);
11423         
11424         this.insert(newIndex, data);
11425         
11426     }
11427 });/*
11428  * Based on:
11429  * Ext JS Library 1.1.1
11430  * Copyright(c) 2006-2007, Ext JS, LLC.
11431  *
11432  * Originally Released Under LGPL - original licence link has changed is not relivant.
11433  *
11434  * Fork - LGPL
11435  * <script type="text/javascript">
11436  */
11437
11438 /**
11439  * @class Roo.data.SimpleStore
11440  * @extends Roo.data.Store
11441  * Small helper class to make creating Stores from Array data easier.
11442  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11443  * @cfg {Array} fields An array of field definition objects, or field name strings.
11444  * @cfg {Array} data The multi-dimensional array of data
11445  * @constructor
11446  * @param {Object} config
11447  */
11448 Roo.data.SimpleStore = function(config){
11449     Roo.data.SimpleStore.superclass.constructor.call(this, {
11450         isLocal : true,
11451         reader: new Roo.data.ArrayReader({
11452                 id: config.id
11453             },
11454             Roo.data.Record.create(config.fields)
11455         ),
11456         proxy : new Roo.data.MemoryProxy(config.data)
11457     });
11458     this.load();
11459 };
11460 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11461  * Based on:
11462  * Ext JS Library 1.1.1
11463  * Copyright(c) 2006-2007, Ext JS, LLC.
11464  *
11465  * Originally Released Under LGPL - original licence link has changed is not relivant.
11466  *
11467  * Fork - LGPL
11468  * <script type="text/javascript">
11469  */
11470
11471 /**
11472 /**
11473  * @extends Roo.data.Store
11474  * @class Roo.data.JsonStore
11475  * Small helper class to make creating Stores for JSON data easier. <br/>
11476 <pre><code>
11477 var store = new Roo.data.JsonStore({
11478     url: 'get-images.php',
11479     root: 'images',
11480     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11481 });
11482 </code></pre>
11483  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11484  * JsonReader and HttpProxy (unless inline data is provided).</b>
11485  * @cfg {Array} fields An array of field definition objects, or field name strings.
11486  * @constructor
11487  * @param {Object} config
11488  */
11489 Roo.data.JsonStore = function(c){
11490     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11491         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11492         reader: new Roo.data.JsonReader(c, c.fields)
11493     }));
11494 };
11495 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11496  * Based on:
11497  * Ext JS Library 1.1.1
11498  * Copyright(c) 2006-2007, Ext JS, LLC.
11499  *
11500  * Originally Released Under LGPL - original licence link has changed is not relivant.
11501  *
11502  * Fork - LGPL
11503  * <script type="text/javascript">
11504  */
11505
11506  
11507 Roo.data.Field = function(config){
11508     if(typeof config == "string"){
11509         config = {name: config};
11510     }
11511     Roo.apply(this, config);
11512     
11513     if(!this.type){
11514         this.type = "auto";
11515     }
11516     
11517     var st = Roo.data.SortTypes;
11518     // named sortTypes are supported, here we look them up
11519     if(typeof this.sortType == "string"){
11520         this.sortType = st[this.sortType];
11521     }
11522     
11523     // set default sortType for strings and dates
11524     if(!this.sortType){
11525         switch(this.type){
11526             case "string":
11527                 this.sortType = st.asUCString;
11528                 break;
11529             case "date":
11530                 this.sortType = st.asDate;
11531                 break;
11532             default:
11533                 this.sortType = st.none;
11534         }
11535     }
11536
11537     // define once
11538     var stripRe = /[\$,%]/g;
11539
11540     // prebuilt conversion function for this field, instead of
11541     // switching every time we're reading a value
11542     if(!this.convert){
11543         var cv, dateFormat = this.dateFormat;
11544         switch(this.type){
11545             case "":
11546             case "auto":
11547             case undefined:
11548                 cv = function(v){ return v; };
11549                 break;
11550             case "string":
11551                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11552                 break;
11553             case "int":
11554                 cv = function(v){
11555                     return v !== undefined && v !== null && v !== '' ?
11556                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11557                     };
11558                 break;
11559             case "float":
11560                 cv = function(v){
11561                     return v !== undefined && v !== null && v !== '' ?
11562                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11563                     };
11564                 break;
11565             case "bool":
11566             case "boolean":
11567                 cv = function(v){ return v === true || v === "true" || v == 1; };
11568                 break;
11569             case "date":
11570                 cv = function(v){
11571                     if(!v){
11572                         return '';
11573                     }
11574                     if(v instanceof Date){
11575                         return v;
11576                     }
11577                     if(dateFormat){
11578                         if(dateFormat == "timestamp"){
11579                             return new Date(v*1000);
11580                         }
11581                         return Date.parseDate(v, dateFormat);
11582                     }
11583                     var parsed = Date.parse(v);
11584                     return parsed ? new Date(parsed) : null;
11585                 };
11586              break;
11587             
11588         }
11589         this.convert = cv;
11590     }
11591 };
11592
11593 Roo.data.Field.prototype = {
11594     dateFormat: null,
11595     defaultValue: "",
11596     mapping: null,
11597     sortType : null,
11598     sortDir : "ASC"
11599 };/*
11600  * Based on:
11601  * Ext JS Library 1.1.1
11602  * Copyright(c) 2006-2007, Ext JS, LLC.
11603  *
11604  * Originally Released Under LGPL - original licence link has changed is not relivant.
11605  *
11606  * Fork - LGPL
11607  * <script type="text/javascript">
11608  */
11609  
11610 // Base class for reading structured data from a data source.  This class is intended to be
11611 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11612
11613 /**
11614  * @class Roo.data.DataReader
11615  * Base class for reading structured data from a data source.  This class is intended to be
11616  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11617  */
11618
11619 Roo.data.DataReader = function(meta, recordType){
11620     
11621     this.meta = meta;
11622     
11623     this.recordType = recordType instanceof Array ? 
11624         Roo.data.Record.create(recordType) : recordType;
11625 };
11626
11627 Roo.data.DataReader.prototype = {
11628      /**
11629      * Create an empty record
11630      * @param {Object} data (optional) - overlay some values
11631      * @return {Roo.data.Record} record created.
11632      */
11633     newRow :  function(d) {
11634         var da =  {};
11635         this.recordType.prototype.fields.each(function(c) {
11636             switch( c.type) {
11637                 case 'int' : da[c.name] = 0; break;
11638                 case 'date' : da[c.name] = new Date(); break;
11639                 case 'float' : da[c.name] = 0.0; break;
11640                 case 'boolean' : da[c.name] = false; break;
11641                 default : da[c.name] = ""; break;
11642             }
11643             
11644         });
11645         return new this.recordType(Roo.apply(da, d));
11646     }
11647     
11648 };/*
11649  * Based on:
11650  * Ext JS Library 1.1.1
11651  * Copyright(c) 2006-2007, Ext JS, LLC.
11652  *
11653  * Originally Released Under LGPL - original licence link has changed is not relivant.
11654  *
11655  * Fork - LGPL
11656  * <script type="text/javascript">
11657  */
11658
11659 /**
11660  * @class Roo.data.DataProxy
11661  * @extends Roo.data.Observable
11662  * This class is an abstract base class for implementations which provide retrieval of
11663  * unformatted data objects.<br>
11664  * <p>
11665  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11666  * (of the appropriate type which knows how to parse the data object) to provide a block of
11667  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11668  * <p>
11669  * Custom implementations must implement the load method as described in
11670  * {@link Roo.data.HttpProxy#load}.
11671  */
11672 Roo.data.DataProxy = function(){
11673     this.addEvents({
11674         /**
11675          * @event beforeload
11676          * Fires before a network request is made to retrieve a data object.
11677          * @param {Object} This DataProxy object.
11678          * @param {Object} params The params parameter to the load function.
11679          */
11680         beforeload : true,
11681         /**
11682          * @event load
11683          * Fires before the load method's callback is called.
11684          * @param {Object} This DataProxy object.
11685          * @param {Object} o The data object.
11686          * @param {Object} arg The callback argument object passed to the load function.
11687          */
11688         load : true,
11689         /**
11690          * @event loadexception
11691          * Fires if an Exception occurs during data retrieval.
11692          * @param {Object} This DataProxy object.
11693          * @param {Object} o The data object.
11694          * @param {Object} arg The callback argument object passed to the load function.
11695          * @param {Object} e The Exception.
11696          */
11697         loadexception : true
11698     });
11699     Roo.data.DataProxy.superclass.constructor.call(this);
11700 };
11701
11702 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11703
11704     /**
11705      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11706      */
11707 /*
11708  * Based on:
11709  * Ext JS Library 1.1.1
11710  * Copyright(c) 2006-2007, Ext JS, LLC.
11711  *
11712  * Originally Released Under LGPL - original licence link has changed is not relivant.
11713  *
11714  * Fork - LGPL
11715  * <script type="text/javascript">
11716  */
11717 /**
11718  * @class Roo.data.MemoryProxy
11719  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11720  * to the Reader when its load method is called.
11721  * @constructor
11722  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11723  */
11724 Roo.data.MemoryProxy = function(data){
11725     if (data.data) {
11726         data = data.data;
11727     }
11728     Roo.data.MemoryProxy.superclass.constructor.call(this);
11729     this.data = data;
11730 };
11731
11732 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11733     
11734     /**
11735      * Load data from the requested source (in this case an in-memory
11736      * data object passed to the constructor), read the data object into
11737      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11738      * process that block using the passed callback.
11739      * @param {Object} params This parameter is not used by the MemoryProxy class.
11740      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11741      * object into a block of Roo.data.Records.
11742      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11743      * The function must be passed <ul>
11744      * <li>The Record block object</li>
11745      * <li>The "arg" argument from the load function</li>
11746      * <li>A boolean success indicator</li>
11747      * </ul>
11748      * @param {Object} scope The scope in which to call the callback
11749      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11750      */
11751     load : function(params, reader, callback, scope, arg){
11752         params = params || {};
11753         var result;
11754         try {
11755             result = reader.readRecords(this.data);
11756         }catch(e){
11757             this.fireEvent("loadexception", this, arg, null, e);
11758             callback.call(scope, null, arg, false);
11759             return;
11760         }
11761         callback.call(scope, result, arg, true);
11762     },
11763     
11764     // private
11765     update : function(params, records){
11766         
11767     }
11768 });/*
11769  * Based on:
11770  * Ext JS Library 1.1.1
11771  * Copyright(c) 2006-2007, Ext JS, LLC.
11772  *
11773  * Originally Released Under LGPL - original licence link has changed is not relivant.
11774  *
11775  * Fork - LGPL
11776  * <script type="text/javascript">
11777  */
11778 /**
11779  * @class Roo.data.HttpProxy
11780  * @extends Roo.data.DataProxy
11781  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11782  * configured to reference a certain URL.<br><br>
11783  * <p>
11784  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11785  * from which the running page was served.<br><br>
11786  * <p>
11787  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11788  * <p>
11789  * Be aware that to enable the browser to parse an XML document, the server must set
11790  * the Content-Type header in the HTTP response to "text/xml".
11791  * @constructor
11792  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11793  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11794  * will be used to make the request.
11795  */
11796 Roo.data.HttpProxy = function(conn){
11797     Roo.data.HttpProxy.superclass.constructor.call(this);
11798     // is conn a conn config or a real conn?
11799     this.conn = conn;
11800     this.useAjax = !conn || !conn.events;
11801   
11802 };
11803
11804 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11805     // thse are take from connection...
11806     
11807     /**
11808      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11809      */
11810     /**
11811      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11812      * extra parameters to each request made by this object. (defaults to undefined)
11813      */
11814     /**
11815      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11816      *  to each request made by this object. (defaults to undefined)
11817      */
11818     /**
11819      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11820      */
11821     /**
11822      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11823      */
11824      /**
11825      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11826      * @type Boolean
11827      */
11828   
11829
11830     /**
11831      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11832      * @type Boolean
11833      */
11834     /**
11835      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11836      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11837      * a finer-grained basis than the DataProxy events.
11838      */
11839     getConnection : function(){
11840         return this.useAjax ? Roo.Ajax : this.conn;
11841     },
11842
11843     /**
11844      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11845      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11846      * process that block using the passed callback.
11847      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11848      * for the request to the remote server.
11849      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11850      * object into a block of Roo.data.Records.
11851      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11852      * The function must be passed <ul>
11853      * <li>The Record block object</li>
11854      * <li>The "arg" argument from the load function</li>
11855      * <li>A boolean success indicator</li>
11856      * </ul>
11857      * @param {Object} scope The scope in which to call the callback
11858      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11859      */
11860     load : function(params, reader, callback, scope, arg){
11861         if(this.fireEvent("beforeload", this, params) !== false){
11862             var  o = {
11863                 params : params || {},
11864                 request: {
11865                     callback : callback,
11866                     scope : scope,
11867                     arg : arg
11868                 },
11869                 reader: reader,
11870                 callback : this.loadResponse,
11871                 scope: this
11872             };
11873             if(this.useAjax){
11874                 Roo.applyIf(o, this.conn);
11875                 if(this.activeRequest){
11876                     Roo.Ajax.abort(this.activeRequest);
11877                 }
11878                 this.activeRequest = Roo.Ajax.request(o);
11879             }else{
11880                 this.conn.request(o);
11881             }
11882         }else{
11883             callback.call(scope||this, null, arg, false);
11884         }
11885     },
11886
11887     // private
11888     loadResponse : function(o, success, response){
11889         delete this.activeRequest;
11890         if(!success){
11891             this.fireEvent("loadexception", this, o, response);
11892             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11893             return;
11894         }
11895         var result;
11896         try {
11897             result = o.reader.read(response);
11898         }catch(e){
11899             this.fireEvent("loadexception", this, o, response, e);
11900             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11901             return;
11902         }
11903         
11904         this.fireEvent("load", this, o, o.request.arg);
11905         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11906     },
11907
11908     // private
11909     update : function(dataSet){
11910
11911     },
11912
11913     // private
11914     updateResponse : function(dataSet){
11915
11916     }
11917 });/*
11918  * Based on:
11919  * Ext JS Library 1.1.1
11920  * Copyright(c) 2006-2007, Ext JS, LLC.
11921  *
11922  * Originally Released Under LGPL - original licence link has changed is not relivant.
11923  *
11924  * Fork - LGPL
11925  * <script type="text/javascript">
11926  */
11927
11928 /**
11929  * @class Roo.data.ScriptTagProxy
11930  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11931  * other than the originating domain of the running page.<br><br>
11932  * <p>
11933  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
11934  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11935  * <p>
11936  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11937  * source code that is used as the source inside a &lt;script> tag.<br><br>
11938  * <p>
11939  * In order for the browser to process the returned data, the server must wrap the data object
11940  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11941  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11942  * depending on whether the callback name was passed:
11943  * <p>
11944  * <pre><code>
11945 boolean scriptTag = false;
11946 String cb = request.getParameter("callback");
11947 if (cb != null) {
11948     scriptTag = true;
11949     response.setContentType("text/javascript");
11950 } else {
11951     response.setContentType("application/x-json");
11952 }
11953 Writer out = response.getWriter();
11954 if (scriptTag) {
11955     out.write(cb + "(");
11956 }
11957 out.print(dataBlock.toJsonString());
11958 if (scriptTag) {
11959     out.write(");");
11960 }
11961 </pre></code>
11962  *
11963  * @constructor
11964  * @param {Object} config A configuration object.
11965  */
11966 Roo.data.ScriptTagProxy = function(config){
11967     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11968     Roo.apply(this, config);
11969     this.head = document.getElementsByTagName("head")[0];
11970 };
11971
11972 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11973
11974 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11975     /**
11976      * @cfg {String} url The URL from which to request the data object.
11977      */
11978     /**
11979      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11980      */
11981     timeout : 30000,
11982     /**
11983      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11984      * the server the name of the callback function set up by the load call to process the returned data object.
11985      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11986      * javascript output which calls this named function passing the data object as its only parameter.
11987      */
11988     callbackParam : "callback",
11989     /**
11990      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11991      * name to the request.
11992      */
11993     nocache : true,
11994
11995     /**
11996      * Load data from the configured URL, read the data object into
11997      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11998      * process that block using the passed callback.
11999      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12000      * for the request to the remote server.
12001      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12002      * object into a block of Roo.data.Records.
12003      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12004      * The function must be passed <ul>
12005      * <li>The Record block object</li>
12006      * <li>The "arg" argument from the load function</li>
12007      * <li>A boolean success indicator</li>
12008      * </ul>
12009      * @param {Object} scope The scope in which to call the callback
12010      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12011      */
12012     load : function(params, reader, callback, scope, arg){
12013         if(this.fireEvent("beforeload", this, params) !== false){
12014
12015             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12016
12017             var url = this.url;
12018             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12019             if(this.nocache){
12020                 url += "&_dc=" + (new Date().getTime());
12021             }
12022             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12023             var trans = {
12024                 id : transId,
12025                 cb : "stcCallback"+transId,
12026                 scriptId : "stcScript"+transId,
12027                 params : params,
12028                 arg : arg,
12029                 url : url,
12030                 callback : callback,
12031                 scope : scope,
12032                 reader : reader
12033             };
12034             var conn = this;
12035
12036             window[trans.cb] = function(o){
12037                 conn.handleResponse(o, trans);
12038             };
12039
12040             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12041
12042             if(this.autoAbort !== false){
12043                 this.abort();
12044             }
12045
12046             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12047
12048             var script = document.createElement("script");
12049             script.setAttribute("src", url);
12050             script.setAttribute("type", "text/javascript");
12051             script.setAttribute("id", trans.scriptId);
12052             this.head.appendChild(script);
12053
12054             this.trans = trans;
12055         }else{
12056             callback.call(scope||this, null, arg, false);
12057         }
12058     },
12059
12060     // private
12061     isLoading : function(){
12062         return this.trans ? true : false;
12063     },
12064
12065     /**
12066      * Abort the current server request.
12067      */
12068     abort : function(){
12069         if(this.isLoading()){
12070             this.destroyTrans(this.trans);
12071         }
12072     },
12073
12074     // private
12075     destroyTrans : function(trans, isLoaded){
12076         this.head.removeChild(document.getElementById(trans.scriptId));
12077         clearTimeout(trans.timeoutId);
12078         if(isLoaded){
12079             window[trans.cb] = undefined;
12080             try{
12081                 delete window[trans.cb];
12082             }catch(e){}
12083         }else{
12084             // if hasn't been loaded, wait for load to remove it to prevent script error
12085             window[trans.cb] = function(){
12086                 window[trans.cb] = undefined;
12087                 try{
12088                     delete window[trans.cb];
12089                 }catch(e){}
12090             };
12091         }
12092     },
12093
12094     // private
12095     handleResponse : function(o, trans){
12096         this.trans = false;
12097         this.destroyTrans(trans, true);
12098         var result;
12099         try {
12100             result = trans.reader.readRecords(o);
12101         }catch(e){
12102             this.fireEvent("loadexception", this, o, trans.arg, e);
12103             trans.callback.call(trans.scope||window, null, trans.arg, false);
12104             return;
12105         }
12106         this.fireEvent("load", this, o, trans.arg);
12107         trans.callback.call(trans.scope||window, result, trans.arg, true);
12108     },
12109
12110     // private
12111     handleFailure : function(trans){
12112         this.trans = false;
12113         this.destroyTrans(trans, false);
12114         this.fireEvent("loadexception", this, null, trans.arg);
12115         trans.callback.call(trans.scope||window, null, trans.arg, false);
12116     }
12117 });/*
12118  * Based on:
12119  * Ext JS Library 1.1.1
12120  * Copyright(c) 2006-2007, Ext JS, LLC.
12121  *
12122  * Originally Released Under LGPL - original licence link has changed is not relivant.
12123  *
12124  * Fork - LGPL
12125  * <script type="text/javascript">
12126  */
12127
12128 /**
12129  * @class Roo.data.JsonReader
12130  * @extends Roo.data.DataReader
12131  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12132  * based on mappings in a provided Roo.data.Record constructor.
12133  * 
12134  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12135  * in the reply previously. 
12136  * 
12137  * <p>
12138  * Example code:
12139  * <pre><code>
12140 var RecordDef = Roo.data.Record.create([
12141     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12142     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12143 ]);
12144 var myReader = new Roo.data.JsonReader({
12145     totalProperty: "results",    // The property which contains the total dataset size (optional)
12146     root: "rows",                // The property which contains an Array of row objects
12147     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12148 }, RecordDef);
12149 </code></pre>
12150  * <p>
12151  * This would consume a JSON file like this:
12152  * <pre><code>
12153 { 'results': 2, 'rows': [
12154     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12155     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12156 }
12157 </code></pre>
12158  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12159  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12160  * paged from the remote server.
12161  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12162  * @cfg {String} root name of the property which contains the Array of row objects.
12163  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12164  * @cfg {Array} fields Array of field definition objects
12165  * @constructor
12166  * Create a new JsonReader
12167  * @param {Object} meta Metadata configuration options
12168  * @param {Object} recordType Either an Array of field definition objects,
12169  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12170  */
12171 Roo.data.JsonReader = function(meta, recordType){
12172     
12173     meta = meta || {};
12174     // set some defaults:
12175     Roo.applyIf(meta, {
12176         totalProperty: 'total',
12177         successProperty : 'success',
12178         root : 'data',
12179         id : 'id'
12180     });
12181     
12182     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12183 };
12184 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12185     
12186     /**
12187      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12188      * Used by Store query builder to append _requestMeta to params.
12189      * 
12190      */
12191     metaFromRemote : false,
12192     /**
12193      * This method is only used by a DataProxy which has retrieved data from a remote server.
12194      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12195      * @return {Object} data A data block which is used by an Roo.data.Store object as
12196      * a cache of Roo.data.Records.
12197      */
12198     read : function(response){
12199         var json = response.responseText;
12200        
12201         var o = /* eval:var:o */ eval("("+json+")");
12202         if(!o) {
12203             throw {message: "JsonReader.read: Json object not found"};
12204         }
12205         
12206         if(o.metaData){
12207             
12208             delete this.ef;
12209             this.metaFromRemote = true;
12210             this.meta = o.metaData;
12211             this.recordType = Roo.data.Record.create(o.metaData.fields);
12212             this.onMetaChange(this.meta, this.recordType, o);
12213         }
12214         return this.readRecords(o);
12215     },
12216
12217     // private function a store will implement
12218     onMetaChange : function(meta, recordType, o){
12219
12220     },
12221
12222     /**
12223          * @ignore
12224          */
12225     simpleAccess: function(obj, subsc) {
12226         return obj[subsc];
12227     },
12228
12229         /**
12230          * @ignore
12231          */
12232     getJsonAccessor: function(){
12233         var re = /[\[\.]/;
12234         return function(expr) {
12235             try {
12236                 return(re.test(expr))
12237                     ? new Function("obj", "return obj." + expr)
12238                     : function(obj){
12239                         return obj[expr];
12240                     };
12241             } catch(e){}
12242             return Roo.emptyFn;
12243         };
12244     }(),
12245
12246     /**
12247      * Create a data block containing Roo.data.Records from an XML document.
12248      * @param {Object} o An object which contains an Array of row objects in the property specified
12249      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12250      * which contains the total size of the dataset.
12251      * @return {Object} data A data block which is used by an Roo.data.Store object as
12252      * a cache of Roo.data.Records.
12253      */
12254     readRecords : function(o){
12255         /**
12256          * After any data loads, the raw JSON data is available for further custom processing.
12257          * @type Object
12258          */
12259         this.o = o;
12260         var s = this.meta, Record = this.recordType,
12261             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12262
12263 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12264         if (!this.ef) {
12265             if(s.totalProperty) {
12266                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12267                 }
12268                 if(s.successProperty) {
12269                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12270                 }
12271                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12272                 if (s.id) {
12273                         var g = this.getJsonAccessor(s.id);
12274                         this.getId = function(rec) {
12275                                 var r = g(rec);  
12276                                 return (r === undefined || r === "") ? null : r;
12277                         };
12278                 } else {
12279                         this.getId = function(){return null;};
12280                 }
12281             this.ef = [];
12282             for(var jj = 0; jj < fl; jj++){
12283                 f = fi[jj];
12284                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12285                 this.ef[jj] = this.getJsonAccessor(map);
12286             }
12287         }
12288
12289         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12290         if(s.totalProperty){
12291             var vt = parseInt(this.getTotal(o), 10);
12292             if(!isNaN(vt)){
12293                 totalRecords = vt;
12294             }
12295         }
12296         if(s.successProperty){
12297             var vs = this.getSuccess(o);
12298             if(vs === false || vs === 'false'){
12299                 success = false;
12300             }
12301         }
12302         var records = [];
12303         for(var i = 0; i < c; i++){
12304                 var n = root[i];
12305             var values = {};
12306             var id = this.getId(n);
12307             for(var j = 0; j < fl; j++){
12308                 f = fi[j];
12309             var v = this.ef[j](n);
12310             if (!f.convert) {
12311                 Roo.log('missing convert for ' + f.name);
12312                 Roo.log(f);
12313                 continue;
12314             }
12315             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12316             }
12317             var record = new Record(values, id);
12318             record.json = n;
12319             records[i] = record;
12320         }
12321         return {
12322             raw : o,
12323             success : success,
12324             records : records,
12325             totalRecords : totalRecords
12326         };
12327     }
12328 });/*
12329  * Based on:
12330  * Ext JS Library 1.1.1
12331  * Copyright(c) 2006-2007, Ext JS, LLC.
12332  *
12333  * Originally Released Under LGPL - original licence link has changed is not relivant.
12334  *
12335  * Fork - LGPL
12336  * <script type="text/javascript">
12337  */
12338
12339 /**
12340  * @class Roo.data.ArrayReader
12341  * @extends Roo.data.DataReader
12342  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12343  * Each element of that Array represents a row of data fields. The
12344  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12345  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12346  * <p>
12347  * Example code:.
12348  * <pre><code>
12349 var RecordDef = Roo.data.Record.create([
12350     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12351     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12352 ]);
12353 var myReader = new Roo.data.ArrayReader({
12354     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12355 }, RecordDef);
12356 </code></pre>
12357  * <p>
12358  * This would consume an Array like this:
12359  * <pre><code>
12360 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12361   </code></pre>
12362  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12363  * @constructor
12364  * Create a new JsonReader
12365  * @param {Object} meta Metadata configuration options.
12366  * @param {Object} recordType Either an Array of field definition objects
12367  * as specified to {@link Roo.data.Record#create},
12368  * or an {@link Roo.data.Record} object
12369  * created using {@link Roo.data.Record#create}.
12370  */
12371 Roo.data.ArrayReader = function(meta, recordType){
12372     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12373 };
12374
12375 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12376     /**
12377      * Create a data block containing Roo.data.Records from an XML document.
12378      * @param {Object} o An Array of row objects which represents the dataset.
12379      * @return {Object} data A data block which is used by an Roo.data.Store object as
12380      * a cache of Roo.data.Records.
12381      */
12382     readRecords : function(o){
12383         var sid = this.meta ? this.meta.id : null;
12384         var recordType = this.recordType, fields = recordType.prototype.fields;
12385         var records = [];
12386         var root = o;
12387             for(var i = 0; i < root.length; i++){
12388                     var n = root[i];
12389                 var values = {};
12390                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12391                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12392                 var f = fields.items[j];
12393                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12394                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12395                 v = f.convert(v);
12396                 values[f.name] = v;
12397             }
12398                 var record = new recordType(values, id);
12399                 record.json = n;
12400                 records[records.length] = record;
12401             }
12402             return {
12403                 records : records,
12404                 totalRecords : records.length
12405             };
12406     }
12407 });/*
12408  * - LGPL
12409  * * 
12410  */
12411
12412 /**
12413  * @class Roo.bootstrap.ComboBox
12414  * @extends Roo.bootstrap.TriggerField
12415  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12416  * @cfg {Boolean} append (true|false) default false
12417  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12418  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12419  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12420  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12421  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12422  * @cfg {Boolean} animate default true
12423  * @cfg {Boolean} emptyResultText only for touch device
12424  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12425  * @cfg {String} emptyTitle default ''
12426  * @constructor
12427  * Create a new ComboBox.
12428  * @param {Object} config Configuration options
12429  */
12430 Roo.bootstrap.ComboBox = function(config){
12431     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12432     this.addEvents({
12433         /**
12434          * @event expand
12435          * Fires when the dropdown list is expanded
12436         * @param {Roo.bootstrap.ComboBox} combo This combo box
12437         */
12438         'expand' : true,
12439         /**
12440          * @event collapse
12441          * Fires when the dropdown list is collapsed
12442         * @param {Roo.bootstrap.ComboBox} combo This combo box
12443         */
12444         'collapse' : true,
12445         /**
12446          * @event beforeselect
12447          * Fires before a list item is selected. Return false to cancel the selection.
12448         * @param {Roo.bootstrap.ComboBox} combo This combo box
12449         * @param {Roo.data.Record} record The data record returned from the underlying store
12450         * @param {Number} index The index of the selected item in the dropdown list
12451         */
12452         'beforeselect' : true,
12453         /**
12454          * @event select
12455          * Fires when a list item is selected
12456         * @param {Roo.bootstrap.ComboBox} combo This combo box
12457         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12458         * @param {Number} index The index of the selected item in the dropdown list
12459         */
12460         'select' : true,
12461         /**
12462          * @event beforequery
12463          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12464          * The event object passed has these properties:
12465         * @param {Roo.bootstrap.ComboBox} combo This combo box
12466         * @param {String} query The query
12467         * @param {Boolean} forceAll true to force "all" query
12468         * @param {Boolean} cancel true to cancel the query
12469         * @param {Object} e The query event object
12470         */
12471         'beforequery': true,
12472          /**
12473          * @event add
12474          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12475         * @param {Roo.bootstrap.ComboBox} combo This combo box
12476         */
12477         'add' : true,
12478         /**
12479          * @event edit
12480          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12481         * @param {Roo.bootstrap.ComboBox} combo This combo box
12482         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12483         */
12484         'edit' : true,
12485         /**
12486          * @event remove
12487          * Fires when the remove value from the combobox array
12488         * @param {Roo.bootstrap.ComboBox} combo This combo box
12489         */
12490         'remove' : true,
12491         /**
12492          * @event afterremove
12493          * Fires when the remove value from the combobox array
12494         * @param {Roo.bootstrap.ComboBox} combo This combo box
12495         */
12496         'afterremove' : true,
12497         /**
12498          * @event specialfilter
12499          * Fires when specialfilter
12500             * @param {Roo.bootstrap.ComboBox} combo This combo box
12501             */
12502         'specialfilter' : true,
12503         /**
12504          * @event tick
12505          * Fires when tick the element
12506             * @param {Roo.bootstrap.ComboBox} combo This combo box
12507             */
12508         'tick' : true,
12509         /**
12510          * @event touchviewdisplay
12511          * Fires when touch view require special display (default is using displayField)
12512             * @param {Roo.bootstrap.ComboBox} combo This combo box
12513             * @param {Object} cfg set html .
12514             */
12515         'touchviewdisplay' : true
12516         
12517     });
12518     
12519     this.item = [];
12520     this.tickItems = [];
12521     
12522     this.selectedIndex = -1;
12523     if(this.mode == 'local'){
12524         if(config.queryDelay === undefined){
12525             this.queryDelay = 10;
12526         }
12527         if(config.minChars === undefined){
12528             this.minChars = 0;
12529         }
12530     }
12531 };
12532
12533 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12534      
12535     /**
12536      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12537      * rendering into an Roo.Editor, defaults to false)
12538      */
12539     /**
12540      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12541      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12542      */
12543     /**
12544      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12545      */
12546     /**
12547      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12548      * the dropdown list (defaults to undefined, with no header element)
12549      */
12550
12551      /**
12552      * @cfg {String/Roo.Template} tpl The template to use to render the output
12553      */
12554      
12555      /**
12556      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12557      */
12558     listWidth: undefined,
12559     /**
12560      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12561      * mode = 'remote' or 'text' if mode = 'local')
12562      */
12563     displayField: undefined,
12564     
12565     /**
12566      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12567      * mode = 'remote' or 'value' if mode = 'local'). 
12568      * Note: use of a valueField requires the user make a selection
12569      * in order for a value to be mapped.
12570      */
12571     valueField: undefined,
12572     /**
12573      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12574      */
12575     modalTitle : '',
12576     
12577     /**
12578      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12579      * field's data value (defaults to the underlying DOM element's name)
12580      */
12581     hiddenName: undefined,
12582     /**
12583      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12584      */
12585     listClass: '',
12586     /**
12587      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12588      */
12589     selectedClass: 'active',
12590     
12591     /**
12592      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12593      */
12594     shadow:'sides',
12595     /**
12596      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12597      * anchor positions (defaults to 'tl-bl')
12598      */
12599     listAlign: 'tl-bl?',
12600     /**
12601      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12602      */
12603     maxHeight: 300,
12604     /**
12605      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12606      * query specified by the allQuery config option (defaults to 'query')
12607      */
12608     triggerAction: 'query',
12609     /**
12610      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12611      * (defaults to 4, does not apply if editable = false)
12612      */
12613     minChars : 4,
12614     /**
12615      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12616      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12617      */
12618     typeAhead: false,
12619     /**
12620      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12621      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12622      */
12623     queryDelay: 500,
12624     /**
12625      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12626      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12627      */
12628     pageSize: 0,
12629     /**
12630      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12631      * when editable = true (defaults to false)
12632      */
12633     selectOnFocus:false,
12634     /**
12635      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12636      */
12637     queryParam: 'query',
12638     /**
12639      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12640      * when mode = 'remote' (defaults to 'Loading...')
12641      */
12642     loadingText: 'Loading...',
12643     /**
12644      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12645      */
12646     resizable: false,
12647     /**
12648      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12649      */
12650     handleHeight : 8,
12651     /**
12652      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12653      * traditional select (defaults to true)
12654      */
12655     editable: true,
12656     /**
12657      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12658      */
12659     allQuery: '',
12660     /**
12661      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12662      */
12663     mode: 'remote',
12664     /**
12665      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12666      * listWidth has a higher value)
12667      */
12668     minListWidth : 70,
12669     /**
12670      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12671      * allow the user to set arbitrary text into the field (defaults to false)
12672      */
12673     forceSelection:false,
12674     /**
12675      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12676      * if typeAhead = true (defaults to 250)
12677      */
12678     typeAheadDelay : 250,
12679     /**
12680      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12681      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12682      */
12683     valueNotFoundText : undefined,
12684     /**
12685      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12686      */
12687     blockFocus : false,
12688     
12689     /**
12690      * @cfg {Boolean} disableClear Disable showing of clear button.
12691      */
12692     disableClear : false,
12693     /**
12694      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12695      */
12696     alwaysQuery : false,
12697     
12698     /**
12699      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12700      */
12701     multiple : false,
12702     
12703     /**
12704      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12705      */
12706     invalidClass : "has-warning",
12707     
12708     /**
12709      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12710      */
12711     validClass : "has-success",
12712     
12713     /**
12714      * @cfg {Boolean} specialFilter (true|false) special filter default false
12715      */
12716     specialFilter : false,
12717     
12718     /**
12719      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12720      */
12721     mobileTouchView : true,
12722     
12723     /**
12724      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12725      */
12726     useNativeIOS : false,
12727     
12728     ios_options : false,
12729     
12730     //private
12731     addicon : false,
12732     editicon: false,
12733     
12734     page: 0,
12735     hasQuery: false,
12736     append: false,
12737     loadNext: false,
12738     autoFocus : true,
12739     tickable : false,
12740     btnPosition : 'right',
12741     triggerList : true,
12742     showToggleBtn : true,
12743     animate : true,
12744     emptyResultText: 'Empty',
12745     triggerText : 'Select',
12746     emptyTitle : '',
12747     
12748     // element that contains real text value.. (when hidden is used..)
12749     
12750     getAutoCreate : function()
12751     {   
12752         var cfg = false;
12753         //render
12754         /*
12755          * Render classic select for iso
12756          */
12757         
12758         if(Roo.isIOS && this.useNativeIOS){
12759             cfg = this.getAutoCreateNativeIOS();
12760             return cfg;
12761         }
12762         
12763         /*
12764          * Touch Devices
12765          */
12766         
12767         if(Roo.isTouch && this.mobileTouchView){
12768             cfg = this.getAutoCreateTouchView();
12769             return cfg;;
12770         }
12771         
12772         /*
12773          *  Normal ComboBox
12774          */
12775         if(!this.tickable){
12776             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12777             return cfg;
12778         }
12779         
12780         /*
12781          *  ComboBox with tickable selections
12782          */
12783              
12784         var align = this.labelAlign || this.parentLabelAlign();
12785         
12786         cfg = {
12787             cls : 'form-group roo-combobox-tickable' //input-group
12788         };
12789         
12790         var btn_text_select = '';
12791         var btn_text_done = '';
12792         var btn_text_cancel = '';
12793         
12794         if (this.btn_text_show) {
12795             btn_text_select = 'Select';
12796             btn_text_done = 'Done';
12797             btn_text_cancel = 'Cancel'; 
12798         }
12799         
12800         var buttons = {
12801             tag : 'div',
12802             cls : 'tickable-buttons',
12803             cn : [
12804                 {
12805                     tag : 'button',
12806                     type : 'button',
12807                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12808                     //html : this.triggerText
12809                     html: btn_text_select
12810                 },
12811                 {
12812                     tag : 'button',
12813                     type : 'button',
12814                     name : 'ok',
12815                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12816                     //html : 'Done'
12817                     html: btn_text_done
12818                 },
12819                 {
12820                     tag : 'button',
12821                     type : 'button',
12822                     name : 'cancel',
12823                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12824                     //html : 'Cancel'
12825                     html: btn_text_cancel
12826                 }
12827             ]
12828         };
12829         
12830         if(this.editable){
12831             buttons.cn.unshift({
12832                 tag: 'input',
12833                 cls: 'roo-select2-search-field-input'
12834             });
12835         }
12836         
12837         var _this = this;
12838         
12839         Roo.each(buttons.cn, function(c){
12840             if (_this.size) {
12841                 c.cls += ' btn-' + _this.size;
12842             }
12843
12844             if (_this.disabled) {
12845                 c.disabled = true;
12846             }
12847         });
12848         
12849         var box = {
12850             tag: 'div',
12851             cn: [
12852                 {
12853                     tag: 'input',
12854                     type : 'hidden',
12855                     cls: 'form-hidden-field'
12856                 },
12857                 {
12858                     tag: 'ul',
12859                     cls: 'roo-select2-choices',
12860                     cn:[
12861                         {
12862                             tag: 'li',
12863                             cls: 'roo-select2-search-field',
12864                             cn: [
12865                                 buttons
12866                             ]
12867                         }
12868                     ]
12869                 }
12870             ]
12871         };
12872         
12873         var combobox = {
12874             cls: 'roo-select2-container input-group roo-select2-container-multi',
12875             cn: [
12876                 box
12877 //                {
12878 //                    tag: 'ul',
12879 //                    cls: 'typeahead typeahead-long dropdown-menu',
12880 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12881 //                }
12882             ]
12883         };
12884         
12885         if(this.hasFeedback && !this.allowBlank){
12886             
12887             var feedback = {
12888                 tag: 'span',
12889                 cls: 'glyphicon form-control-feedback'
12890             };
12891
12892             combobox.cn.push(feedback);
12893         }
12894         
12895         
12896         if (align ==='left' && this.fieldLabel.length) {
12897             
12898             cfg.cls += ' roo-form-group-label-left';
12899             
12900             cfg.cn = [
12901                 {
12902                     tag : 'i',
12903                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12904                     tooltip : 'This field is required'
12905                 },
12906                 {
12907                     tag: 'label',
12908                     'for' :  id,
12909                     cls : 'control-label',
12910                     html : this.fieldLabel
12911
12912                 },
12913                 {
12914                     cls : "", 
12915                     cn: [
12916                         combobox
12917                     ]
12918                 }
12919
12920             ];
12921             
12922             var labelCfg = cfg.cn[1];
12923             var contentCfg = cfg.cn[2];
12924             
12925
12926             if(this.indicatorpos == 'right'){
12927                 
12928                 cfg.cn = [
12929                     {
12930                         tag: 'label',
12931                         'for' :  id,
12932                         cls : 'control-label',
12933                         cn : [
12934                             {
12935                                 tag : 'span',
12936                                 html : this.fieldLabel
12937                             },
12938                             {
12939                                 tag : 'i',
12940                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12941                                 tooltip : 'This field is required'
12942                             }
12943                         ]
12944                     },
12945                     {
12946                         cls : "",
12947                         cn: [
12948                             combobox
12949                         ]
12950                     }
12951
12952                 ];
12953                 
12954                 
12955                 
12956                 labelCfg = cfg.cn[0];
12957                 contentCfg = cfg.cn[1];
12958             
12959             }
12960             
12961             if(this.labelWidth > 12){
12962                 labelCfg.style = "width: " + this.labelWidth + 'px';
12963             }
12964             
12965             if(this.labelWidth < 13 && this.labelmd == 0){
12966                 this.labelmd = this.labelWidth;
12967             }
12968             
12969             if(this.labellg > 0){
12970                 labelCfg.cls += ' col-lg-' + this.labellg;
12971                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12972             }
12973             
12974             if(this.labelmd > 0){
12975                 labelCfg.cls += ' col-md-' + this.labelmd;
12976                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12977             }
12978             
12979             if(this.labelsm > 0){
12980                 labelCfg.cls += ' col-sm-' + this.labelsm;
12981                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12982             }
12983             
12984             if(this.labelxs > 0){
12985                 labelCfg.cls += ' col-xs-' + this.labelxs;
12986                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12987             }
12988                 
12989                 
12990         } else if ( this.fieldLabel.length) {
12991 //                Roo.log(" label");
12992                  cfg.cn = [
12993                     {
12994                         tag : 'i',
12995                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12996                         tooltip : 'This field is required'
12997                     },
12998                     {
12999                         tag: 'label',
13000                         //cls : 'input-group-addon',
13001                         html : this.fieldLabel
13002                     },
13003                     combobox
13004                 ];
13005                 
13006                 if(this.indicatorpos == 'right'){
13007                     cfg.cn = [
13008                         {
13009                             tag: 'label',
13010                             //cls : 'input-group-addon',
13011                             html : this.fieldLabel
13012                         },
13013                         {
13014                             tag : 'i',
13015                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13016                             tooltip : 'This field is required'
13017                         },
13018                         combobox
13019                     ];
13020                     
13021                 }
13022
13023         } else {
13024             
13025 //                Roo.log(" no label && no align");
13026                 cfg = combobox
13027                      
13028                 
13029         }
13030          
13031         var settings=this;
13032         ['xs','sm','md','lg'].map(function(size){
13033             if (settings[size]) {
13034                 cfg.cls += ' col-' + size + '-' + settings[size];
13035             }
13036         });
13037         
13038         return cfg;
13039         
13040     },
13041     
13042     _initEventsCalled : false,
13043     
13044     // private
13045     initEvents: function()
13046     {   
13047         if (this._initEventsCalled) { // as we call render... prevent looping...
13048             return;
13049         }
13050         this._initEventsCalled = true;
13051         
13052         if (!this.store) {
13053             throw "can not find store for combo";
13054         }
13055         
13056         this.indicator = this.indicatorEl();
13057         
13058         this.store = Roo.factory(this.store, Roo.data);
13059         this.store.parent = this;
13060         
13061         // if we are building from html. then this element is so complex, that we can not really
13062         // use the rendered HTML.
13063         // so we have to trash and replace the previous code.
13064         if (Roo.XComponent.build_from_html) {
13065             // remove this element....
13066             var e = this.el.dom, k=0;
13067             while (e ) { e = e.previousSibling;  ++k;}
13068
13069             this.el.remove();
13070             
13071             this.el=false;
13072             this.rendered = false;
13073             
13074             this.render(this.parent().getChildContainer(true), k);
13075         }
13076         
13077         if(Roo.isIOS && this.useNativeIOS){
13078             this.initIOSView();
13079             return;
13080         }
13081         
13082         /*
13083          * Touch Devices
13084          */
13085         
13086         if(Roo.isTouch && this.mobileTouchView){
13087             this.initTouchView();
13088             return;
13089         }
13090         
13091         if(this.tickable){
13092             this.initTickableEvents();
13093             return;
13094         }
13095         
13096         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13097         
13098         if(this.hiddenName){
13099             
13100             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13101             
13102             this.hiddenField.dom.value =
13103                 this.hiddenValue !== undefined ? this.hiddenValue :
13104                 this.value !== undefined ? this.value : '';
13105
13106             // prevent input submission
13107             this.el.dom.removeAttribute('name');
13108             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13109              
13110              
13111         }
13112         //if(Roo.isGecko){
13113         //    this.el.dom.setAttribute('autocomplete', 'off');
13114         //}
13115         
13116         var cls = 'x-combo-list';
13117         
13118         //this.list = new Roo.Layer({
13119         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13120         //});
13121         
13122         var _this = this;
13123         
13124         (function(){
13125             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13126             _this.list.setWidth(lw);
13127         }).defer(100);
13128         
13129         this.list.on('mouseover', this.onViewOver, this);
13130         this.list.on('mousemove', this.onViewMove, this);
13131         this.list.on('scroll', this.onViewScroll, this);
13132         
13133         /*
13134         this.list.swallowEvent('mousewheel');
13135         this.assetHeight = 0;
13136
13137         if(this.title){
13138             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13139             this.assetHeight += this.header.getHeight();
13140         }
13141
13142         this.innerList = this.list.createChild({cls:cls+'-inner'});
13143         this.innerList.on('mouseover', this.onViewOver, this);
13144         this.innerList.on('mousemove', this.onViewMove, this);
13145         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13146         
13147         if(this.allowBlank && !this.pageSize && !this.disableClear){
13148             this.footer = this.list.createChild({cls:cls+'-ft'});
13149             this.pageTb = new Roo.Toolbar(this.footer);
13150            
13151         }
13152         if(this.pageSize){
13153             this.footer = this.list.createChild({cls:cls+'-ft'});
13154             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13155                     {pageSize: this.pageSize});
13156             
13157         }
13158         
13159         if (this.pageTb && this.allowBlank && !this.disableClear) {
13160             var _this = this;
13161             this.pageTb.add(new Roo.Toolbar.Fill(), {
13162                 cls: 'x-btn-icon x-btn-clear',
13163                 text: '&#160;',
13164                 handler: function()
13165                 {
13166                     _this.collapse();
13167                     _this.clearValue();
13168                     _this.onSelect(false, -1);
13169                 }
13170             });
13171         }
13172         if (this.footer) {
13173             this.assetHeight += this.footer.getHeight();
13174         }
13175         */
13176             
13177         if(!this.tpl){
13178             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13179         }
13180
13181         this.view = new Roo.View(this.list, this.tpl, {
13182             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13183         });
13184         //this.view.wrapEl.setDisplayed(false);
13185         this.view.on('click', this.onViewClick, this);
13186         
13187         
13188         this.store.on('beforeload', this.onBeforeLoad, this);
13189         this.store.on('load', this.onLoad, this);
13190         this.store.on('loadexception', this.onLoadException, this);
13191         /*
13192         if(this.resizable){
13193             this.resizer = new Roo.Resizable(this.list,  {
13194                pinned:true, handles:'se'
13195             });
13196             this.resizer.on('resize', function(r, w, h){
13197                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13198                 this.listWidth = w;
13199                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13200                 this.restrictHeight();
13201             }, this);
13202             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13203         }
13204         */
13205         if(!this.editable){
13206             this.editable = true;
13207             this.setEditable(false);
13208         }
13209         
13210         /*
13211         
13212         if (typeof(this.events.add.listeners) != 'undefined') {
13213             
13214             this.addicon = this.wrap.createChild(
13215                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13216        
13217             this.addicon.on('click', function(e) {
13218                 this.fireEvent('add', this);
13219             }, this);
13220         }
13221         if (typeof(this.events.edit.listeners) != 'undefined') {
13222             
13223             this.editicon = this.wrap.createChild(
13224                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13225             if (this.addicon) {
13226                 this.editicon.setStyle('margin-left', '40px');
13227             }
13228             this.editicon.on('click', function(e) {
13229                 
13230                 // we fire even  if inothing is selected..
13231                 this.fireEvent('edit', this, this.lastData );
13232                 
13233             }, this);
13234         }
13235         */
13236         
13237         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13238             "up" : function(e){
13239                 this.inKeyMode = true;
13240                 this.selectPrev();
13241             },
13242
13243             "down" : function(e){
13244                 if(!this.isExpanded()){
13245                     this.onTriggerClick();
13246                 }else{
13247                     this.inKeyMode = true;
13248                     this.selectNext();
13249                 }
13250             },
13251
13252             "enter" : function(e){
13253 //                this.onViewClick();
13254                 //return true;
13255                 this.collapse();
13256                 
13257                 if(this.fireEvent("specialkey", this, e)){
13258                     this.onViewClick(false);
13259                 }
13260                 
13261                 return true;
13262             },
13263
13264             "esc" : function(e){
13265                 this.collapse();
13266             },
13267
13268             "tab" : function(e){
13269                 this.collapse();
13270                 
13271                 if(this.fireEvent("specialkey", this, e)){
13272                     this.onViewClick(false);
13273                 }
13274                 
13275                 return true;
13276             },
13277
13278             scope : this,
13279
13280             doRelay : function(foo, bar, hname){
13281                 if(hname == 'down' || this.scope.isExpanded()){
13282                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13283                 }
13284                 return true;
13285             },
13286
13287             forceKeyDown: true
13288         });
13289         
13290         
13291         this.queryDelay = Math.max(this.queryDelay || 10,
13292                 this.mode == 'local' ? 10 : 250);
13293         
13294         
13295         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13296         
13297         if(this.typeAhead){
13298             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13299         }
13300         if(this.editable !== false){
13301             this.inputEl().on("keyup", this.onKeyUp, this);
13302         }
13303         if(this.forceSelection){
13304             this.inputEl().on('blur', this.doForce, this);
13305         }
13306         
13307         if(this.multiple){
13308             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13309             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13310         }
13311     },
13312     
13313     initTickableEvents: function()
13314     {   
13315         this.createList();
13316         
13317         if(this.hiddenName){
13318             
13319             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13320             
13321             this.hiddenField.dom.value =
13322                 this.hiddenValue !== undefined ? this.hiddenValue :
13323                 this.value !== undefined ? this.value : '';
13324
13325             // prevent input submission
13326             this.el.dom.removeAttribute('name');
13327             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13328              
13329              
13330         }
13331         
13332 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13333         
13334         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13335         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13336         if(this.triggerList){
13337             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13338         }
13339          
13340         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13341         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13342         
13343         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13344         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13345         
13346         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13347         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13348         
13349         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13350         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13351         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13352         
13353         this.okBtn.hide();
13354         this.cancelBtn.hide();
13355         
13356         var _this = this;
13357         
13358         (function(){
13359             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13360             _this.list.setWidth(lw);
13361         }).defer(100);
13362         
13363         this.list.on('mouseover', this.onViewOver, this);
13364         this.list.on('mousemove', this.onViewMove, this);
13365         
13366         this.list.on('scroll', this.onViewScroll, this);
13367         
13368         if(!this.tpl){
13369             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}" type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
13370         }
13371
13372         this.view = new Roo.View(this.list, this.tpl, {
13373             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13374         });
13375         
13376         //this.view.wrapEl.setDisplayed(false);
13377         this.view.on('click', this.onViewClick, this);
13378         
13379         
13380         
13381         this.store.on('beforeload', this.onBeforeLoad, this);
13382         this.store.on('load', this.onLoad, this);
13383         this.store.on('loadexception', this.onLoadException, this);
13384         
13385         if(this.editable){
13386             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13387                 "up" : function(e){
13388                     this.inKeyMode = true;
13389                     this.selectPrev();
13390                 },
13391
13392                 "down" : function(e){
13393                     this.inKeyMode = true;
13394                     this.selectNext();
13395                 },
13396
13397                 "enter" : function(e){
13398                     if(this.fireEvent("specialkey", this, e)){
13399                         this.onViewClick(false);
13400                     }
13401                     
13402                     return true;
13403                 },
13404
13405                 "esc" : function(e){
13406                     this.onTickableFooterButtonClick(e, false, false);
13407                 },
13408
13409                 "tab" : function(e){
13410                     this.fireEvent("specialkey", this, e);
13411                     
13412                     this.onTickableFooterButtonClick(e, false, false);
13413                     
13414                     return true;
13415                 },
13416
13417                 scope : this,
13418
13419                 doRelay : function(e, fn, key){
13420                     if(this.scope.isExpanded()){
13421                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13422                     }
13423                     return true;
13424                 },
13425
13426                 forceKeyDown: true
13427             });
13428         }
13429         
13430         this.queryDelay = Math.max(this.queryDelay || 10,
13431                 this.mode == 'local' ? 10 : 250);
13432         
13433         
13434         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13435         
13436         if(this.typeAhead){
13437             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13438         }
13439         
13440         if(this.editable !== false){
13441             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13442         }
13443         
13444         this.indicator = this.indicatorEl();
13445         
13446         if(this.indicator){
13447             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13448             this.indicator.hide();
13449         }
13450         
13451     },
13452
13453     onDestroy : function(){
13454         if(this.view){
13455             this.view.setStore(null);
13456             this.view.el.removeAllListeners();
13457             this.view.el.remove();
13458             this.view.purgeListeners();
13459         }
13460         if(this.list){
13461             this.list.dom.innerHTML  = '';
13462         }
13463         
13464         if(this.store){
13465             this.store.un('beforeload', this.onBeforeLoad, this);
13466             this.store.un('load', this.onLoad, this);
13467             this.store.un('loadexception', this.onLoadException, this);
13468         }
13469         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13470     },
13471
13472     // private
13473     fireKey : function(e){
13474         if(e.isNavKeyPress() && !this.list.isVisible()){
13475             this.fireEvent("specialkey", this, e);
13476         }
13477     },
13478
13479     // private
13480     onResize: function(w, h){
13481 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13482 //        
13483 //        if(typeof w != 'number'){
13484 //            // we do not handle it!?!?
13485 //            return;
13486 //        }
13487 //        var tw = this.trigger.getWidth();
13488 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13489 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13490 //        var x = w - tw;
13491 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13492 //            
13493 //        //this.trigger.setStyle('left', x+'px');
13494 //        
13495 //        if(this.list && this.listWidth === undefined){
13496 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13497 //            this.list.setWidth(lw);
13498 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13499 //        }
13500         
13501     
13502         
13503     },
13504
13505     /**
13506      * Allow or prevent the user from directly editing the field text.  If false is passed,
13507      * the user will only be able to select from the items defined in the dropdown list.  This method
13508      * is the runtime equivalent of setting the 'editable' config option at config time.
13509      * @param {Boolean} value True to allow the user to directly edit the field text
13510      */
13511     setEditable : function(value){
13512         if(value == this.editable){
13513             return;
13514         }
13515         this.editable = value;
13516         if(!value){
13517             this.inputEl().dom.setAttribute('readOnly', true);
13518             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13519             this.inputEl().addClass('x-combo-noedit');
13520         }else{
13521             this.inputEl().dom.setAttribute('readOnly', false);
13522             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13523             this.inputEl().removeClass('x-combo-noedit');
13524         }
13525     },
13526
13527     // private
13528     
13529     onBeforeLoad : function(combo,opts){
13530         if(!this.hasFocus){
13531             return;
13532         }
13533          if (!opts.add) {
13534             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13535          }
13536         this.restrictHeight();
13537         this.selectedIndex = -1;
13538     },
13539
13540     // private
13541     onLoad : function(){
13542         
13543         this.hasQuery = false;
13544         
13545         if(!this.hasFocus){
13546             return;
13547         }
13548         
13549         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13550             this.loading.hide();
13551         }
13552         
13553         if(this.store.getCount() > 0){
13554             
13555             this.expand();
13556             this.restrictHeight();
13557             if(this.lastQuery == this.allQuery){
13558                 if(this.editable && !this.tickable){
13559                     this.inputEl().dom.select();
13560                 }
13561                 
13562                 if(
13563                     !this.selectByValue(this.value, true) &&
13564                     this.autoFocus && 
13565                     (
13566                         !this.store.lastOptions ||
13567                         typeof(this.store.lastOptions.add) == 'undefined' || 
13568                         this.store.lastOptions.add != true
13569                     )
13570                 ){
13571                     this.select(0, true);
13572                 }
13573             }else{
13574                 if(this.autoFocus){
13575                     this.selectNext();
13576                 }
13577                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13578                     this.taTask.delay(this.typeAheadDelay);
13579                 }
13580             }
13581         }else{
13582             this.onEmptyResults();
13583         }
13584         
13585         //this.el.focus();
13586     },
13587     // private
13588     onLoadException : function()
13589     {
13590         this.hasQuery = false;
13591         
13592         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13593             this.loading.hide();
13594         }
13595         
13596         if(this.tickable && this.editable){
13597             return;
13598         }
13599         
13600         this.collapse();
13601         // only causes errors at present
13602         //Roo.log(this.store.reader.jsonData);
13603         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13604             // fixme
13605             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13606         //}
13607         
13608         
13609     },
13610     // private
13611     onTypeAhead : function(){
13612         if(this.store.getCount() > 0){
13613             var r = this.store.getAt(0);
13614             var newValue = r.data[this.displayField];
13615             var len = newValue.length;
13616             var selStart = this.getRawValue().length;
13617             
13618             if(selStart != len){
13619                 this.setRawValue(newValue);
13620                 this.selectText(selStart, newValue.length);
13621             }
13622         }
13623     },
13624
13625     // private
13626     onSelect : function(record, index){
13627         
13628         if(this.fireEvent('beforeselect', this, record, index) !== false){
13629         
13630             this.setFromData(index > -1 ? record.data : false);
13631             
13632             this.collapse();
13633             this.fireEvent('select', this, record, index);
13634         }
13635     },
13636
13637     /**
13638      * Returns the currently selected field value or empty string if no value is set.
13639      * @return {String} value The selected value
13640      */
13641     getValue : function()
13642     {
13643         if(Roo.isIOS && this.useNativeIOS){
13644             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13645         }
13646         
13647         if(this.multiple){
13648             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13649         }
13650         
13651         if(this.valueField){
13652             return typeof this.value != 'undefined' ? this.value : '';
13653         }else{
13654             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13655         }
13656     },
13657     
13658     getRawValue : function()
13659     {
13660         if(Roo.isIOS && this.useNativeIOS){
13661             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13662         }
13663         
13664         var v = this.inputEl().getValue();
13665         
13666         return v;
13667     },
13668
13669     /**
13670      * Clears any text/value currently set in the field
13671      */
13672     clearValue : function(){
13673         
13674         if(this.hiddenField){
13675             this.hiddenField.dom.value = '';
13676         }
13677         this.value = '';
13678         this.setRawValue('');
13679         this.lastSelectionText = '';
13680         this.lastData = false;
13681         
13682         var close = this.closeTriggerEl();
13683         
13684         if(close){
13685             close.hide();
13686         }
13687         
13688         this.validate();
13689         
13690     },
13691
13692     /**
13693      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13694      * will be displayed in the field.  If the value does not match the data value of an existing item,
13695      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13696      * Otherwise the field will be blank (although the value will still be set).
13697      * @param {String} value The value to match
13698      */
13699     setValue : function(v)
13700     {
13701         if(Roo.isIOS && this.useNativeIOS){
13702             this.setIOSValue(v);
13703             return;
13704         }
13705         
13706         if(this.multiple){
13707             this.syncValue();
13708             return;
13709         }
13710         
13711         var text = v;
13712         if(this.valueField){
13713             var r = this.findRecord(this.valueField, v);
13714             if(r){
13715                 text = r.data[this.displayField];
13716             }else if(this.valueNotFoundText !== undefined){
13717                 text = this.valueNotFoundText;
13718             }
13719         }
13720         this.lastSelectionText = text;
13721         if(this.hiddenField){
13722             this.hiddenField.dom.value = v;
13723         }
13724         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13725         this.value = v;
13726         
13727         var close = this.closeTriggerEl();
13728         
13729         if(close){
13730             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13731         }
13732         
13733         this.validate();
13734     },
13735     /**
13736      * @property {Object} the last set data for the element
13737      */
13738     
13739     lastData : false,
13740     /**
13741      * Sets the value of the field based on a object which is related to the record format for the store.
13742      * @param {Object} value the value to set as. or false on reset?
13743      */
13744     setFromData : function(o){
13745         
13746         if(this.multiple){
13747             this.addItem(o);
13748             return;
13749         }
13750             
13751         var dv = ''; // display value
13752         var vv = ''; // value value..
13753         this.lastData = o;
13754         if (this.displayField) {
13755             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13756         } else {
13757             // this is an error condition!!!
13758             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13759         }
13760         
13761         if(this.valueField){
13762             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13763         }
13764         
13765         var close = this.closeTriggerEl();
13766         
13767         if(close){
13768             if(dv.length || vv * 1 > 0){
13769                 close.show() ;
13770                 this.blockFocus=true;
13771             } else {
13772                 close.hide();
13773             }             
13774         }
13775         
13776         if(this.hiddenField){
13777             this.hiddenField.dom.value = vv;
13778             
13779             this.lastSelectionText = dv;
13780             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13781             this.value = vv;
13782             return;
13783         }
13784         // no hidden field.. - we store the value in 'value', but still display
13785         // display field!!!!
13786         this.lastSelectionText = dv;
13787         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13788         this.value = vv;
13789         
13790         
13791         
13792     },
13793     // private
13794     reset : function(){
13795         // overridden so that last data is reset..
13796         
13797         if(this.multiple){
13798             this.clearItem();
13799             return;
13800         }
13801         
13802         this.setValue(this.originalValue);
13803         //this.clearInvalid();
13804         this.lastData = false;
13805         if (this.view) {
13806             this.view.clearSelections();
13807         }
13808         
13809         this.validate();
13810     },
13811     // private
13812     findRecord : function(prop, value){
13813         var record;
13814         if(this.store.getCount() > 0){
13815             this.store.each(function(r){
13816                 if(r.data[prop] == value){
13817                     record = r;
13818                     return false;
13819                 }
13820                 return true;
13821             });
13822         }
13823         return record;
13824     },
13825     
13826     getName: function()
13827     {
13828         // returns hidden if it's set..
13829         if (!this.rendered) {return ''};
13830         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13831         
13832     },
13833     // private
13834     onViewMove : function(e, t){
13835         this.inKeyMode = false;
13836     },
13837
13838     // private
13839     onViewOver : function(e, t){
13840         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13841             return;
13842         }
13843         var item = this.view.findItemFromChild(t);
13844         
13845         if(item){
13846             var index = this.view.indexOf(item);
13847             this.select(index, false);
13848         }
13849     },
13850
13851     // private
13852     onViewClick : function(view, doFocus, el, e)
13853     {
13854         var index = this.view.getSelectedIndexes()[0];
13855         
13856         var r = this.store.getAt(index);
13857         
13858         if(this.tickable){
13859             
13860             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13861                 return;
13862             }
13863             
13864             var rm = false;
13865             var _this = this;
13866             
13867             Roo.each(this.tickItems, function(v,k){
13868                 
13869                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13870                     Roo.log(v);
13871                     _this.tickItems.splice(k, 1);
13872                     
13873                     if(typeof(e) == 'undefined' && view == false){
13874                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13875                     }
13876                     
13877                     rm = true;
13878                     return;
13879                 }
13880             });
13881             
13882             if(rm){
13883                 return;
13884             }
13885             
13886             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13887                 this.tickItems.push(r.data);
13888             }
13889             
13890             if(typeof(e) == 'undefined' && view == false){
13891                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13892             }
13893                     
13894             return;
13895         }
13896         
13897         if(r){
13898             this.onSelect(r, index);
13899         }
13900         if(doFocus !== false && !this.blockFocus){
13901             this.inputEl().focus();
13902         }
13903     },
13904
13905     // private
13906     restrictHeight : function(){
13907         //this.innerList.dom.style.height = '';
13908         //var inner = this.innerList.dom;
13909         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13910         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13911         //this.list.beginUpdate();
13912         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13913         this.list.alignTo(this.inputEl(), this.listAlign);
13914         this.list.alignTo(this.inputEl(), this.listAlign);
13915         //this.list.endUpdate();
13916     },
13917
13918     // private
13919     onEmptyResults : function(){
13920         
13921         if(this.tickable && this.editable){
13922             this.restrictHeight();
13923             return;
13924         }
13925         
13926         this.collapse();
13927     },
13928
13929     /**
13930      * Returns true if the dropdown list is expanded, else false.
13931      */
13932     isExpanded : function(){
13933         return this.list.isVisible();
13934     },
13935
13936     /**
13937      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13938      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13939      * @param {String} value The data value of the item to select
13940      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13941      * selected item if it is not currently in view (defaults to true)
13942      * @return {Boolean} True if the value matched an item in the list, else false
13943      */
13944     selectByValue : function(v, scrollIntoView){
13945         if(v !== undefined && v !== null){
13946             var r = this.findRecord(this.valueField || this.displayField, v);
13947             if(r){
13948                 this.select(this.store.indexOf(r), scrollIntoView);
13949                 return true;
13950             }
13951         }
13952         return false;
13953     },
13954
13955     /**
13956      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13957      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13958      * @param {Number} index The zero-based index of the list item to select
13959      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13960      * selected item if it is not currently in view (defaults to true)
13961      */
13962     select : function(index, scrollIntoView){
13963         this.selectedIndex = index;
13964         this.view.select(index);
13965         if(scrollIntoView !== false){
13966             var el = this.view.getNode(index);
13967             /*
13968              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13969              */
13970             if(el){
13971                 this.list.scrollChildIntoView(el, false);
13972             }
13973         }
13974     },
13975
13976     // private
13977     selectNext : function(){
13978         var ct = this.store.getCount();
13979         if(ct > 0){
13980             if(this.selectedIndex == -1){
13981                 this.select(0);
13982             }else if(this.selectedIndex < ct-1){
13983                 this.select(this.selectedIndex+1);
13984             }
13985         }
13986     },
13987
13988     // private
13989     selectPrev : function(){
13990         var ct = this.store.getCount();
13991         if(ct > 0){
13992             if(this.selectedIndex == -1){
13993                 this.select(0);
13994             }else if(this.selectedIndex != 0){
13995                 this.select(this.selectedIndex-1);
13996             }
13997         }
13998     },
13999
14000     // private
14001     onKeyUp : function(e){
14002         if(this.editable !== false && !e.isSpecialKey()){
14003             this.lastKey = e.getKey();
14004             this.dqTask.delay(this.queryDelay);
14005         }
14006     },
14007
14008     // private
14009     validateBlur : function(){
14010         return !this.list || !this.list.isVisible();   
14011     },
14012
14013     // private
14014     initQuery : function(){
14015         
14016         var v = this.getRawValue();
14017         
14018         if(this.tickable && this.editable){
14019             v = this.tickableInputEl().getValue();
14020         }
14021         
14022         this.doQuery(v);
14023     },
14024
14025     // private
14026     doForce : function(){
14027         if(this.inputEl().dom.value.length > 0){
14028             this.inputEl().dom.value =
14029                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14030              
14031         }
14032     },
14033
14034     /**
14035      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14036      * query allowing the query action to be canceled if needed.
14037      * @param {String} query The SQL query to execute
14038      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14039      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14040      * saved in the current store (defaults to false)
14041      */
14042     doQuery : function(q, forceAll){
14043         
14044         if(q === undefined || q === null){
14045             q = '';
14046         }
14047         var qe = {
14048             query: q,
14049             forceAll: forceAll,
14050             combo: this,
14051             cancel:false
14052         };
14053         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14054             return false;
14055         }
14056         q = qe.query;
14057         
14058         forceAll = qe.forceAll;
14059         if(forceAll === true || (q.length >= this.minChars)){
14060             
14061             this.hasQuery = true;
14062             
14063             if(this.lastQuery != q || this.alwaysQuery){
14064                 this.lastQuery = q;
14065                 if(this.mode == 'local'){
14066                     this.selectedIndex = -1;
14067                     if(forceAll){
14068                         this.store.clearFilter();
14069                     }else{
14070                         
14071                         if(this.specialFilter){
14072                             this.fireEvent('specialfilter', this);
14073                             this.onLoad();
14074                             return;
14075                         }
14076                         
14077                         this.store.filter(this.displayField, q);
14078                     }
14079                     
14080                     this.store.fireEvent("datachanged", this.store);
14081                     
14082                     this.onLoad();
14083                     
14084                     Roo.log('onload???');
14085                     
14086                 }else{
14087                     
14088                     this.store.baseParams[this.queryParam] = q;
14089                     
14090                     var options = {params : this.getParams(q)};
14091                     
14092                     if(this.loadNext){
14093                         options.add = true;
14094                         options.params.start = this.page * this.pageSize;
14095                     }
14096                     
14097                     this.store.load(options);
14098                     
14099                     /*
14100                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14101                      *  we should expand the list on onLoad
14102                      *  so command out it
14103                      */
14104 //                    this.expand();
14105                 }
14106             }else{
14107                 this.selectedIndex = -1;
14108                 this.onLoad();   
14109             }
14110         }
14111         
14112         this.loadNext = false;
14113     },
14114     
14115     // private
14116     getParams : function(q){
14117         var p = {};
14118         //p[this.queryParam] = q;
14119         
14120         if(this.pageSize){
14121             p.start = 0;
14122             p.limit = this.pageSize;
14123         }
14124         return p;
14125     },
14126
14127     /**
14128      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14129      */
14130     collapse : function(){
14131         if(!this.isExpanded()){
14132             return;
14133         }
14134         
14135         this.list.hide();
14136         
14137         this.hasFocus = false;
14138         
14139         if(this.tickable){
14140             this.okBtn.hide();
14141             this.cancelBtn.hide();
14142             this.trigger.show();
14143             
14144             if(this.editable){
14145                 this.tickableInputEl().dom.value = '';
14146                 this.tickableInputEl().blur();
14147             }
14148             
14149         }
14150         
14151         Roo.get(document).un('mousedown', this.collapseIf, this);
14152         Roo.get(document).un('mousewheel', this.collapseIf, this);
14153         if (!this.editable) {
14154             Roo.get(document).un('keydown', this.listKeyPress, this);
14155         }
14156         this.fireEvent('collapse', this);
14157         
14158         this.validate();
14159     },
14160
14161     // private
14162     collapseIf : function(e){
14163         var in_combo  = e.within(this.el);
14164         var in_list =  e.within(this.list);
14165         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14166         
14167         if (in_combo || in_list || is_list) {
14168             //e.stopPropagation();
14169             return;
14170         }
14171         
14172         if(this.tickable){
14173             this.onTickableFooterButtonClick(e, false, false);
14174         }
14175
14176         this.collapse();
14177         
14178     },
14179
14180     /**
14181      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14182      */
14183     expand : function(){
14184        
14185         if(this.isExpanded() || !this.hasFocus){
14186             return;
14187         }
14188         
14189         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14190         this.list.setWidth(lw);
14191         
14192         Roo.log('expand');
14193         
14194         this.list.show();
14195         
14196         this.restrictHeight();
14197         
14198         if(this.tickable){
14199             
14200             this.tickItems = Roo.apply([], this.item);
14201             
14202             this.okBtn.show();
14203             this.cancelBtn.show();
14204             this.trigger.hide();
14205             
14206             if(this.editable){
14207                 this.tickableInputEl().focus();
14208             }
14209             
14210         }
14211         
14212         Roo.get(document).on('mousedown', this.collapseIf, this);
14213         Roo.get(document).on('mousewheel', this.collapseIf, this);
14214         if (!this.editable) {
14215             Roo.get(document).on('keydown', this.listKeyPress, this);
14216         }
14217         
14218         this.fireEvent('expand', this);
14219     },
14220
14221     // private
14222     // Implements the default empty TriggerField.onTriggerClick function
14223     onTriggerClick : function(e)
14224     {
14225         Roo.log('trigger click');
14226         
14227         if(this.disabled || !this.triggerList){
14228             return;
14229         }
14230         
14231         this.page = 0;
14232         this.loadNext = false;
14233         
14234         if(this.isExpanded()){
14235             this.collapse();
14236             if (!this.blockFocus) {
14237                 this.inputEl().focus();
14238             }
14239             
14240         }else {
14241             this.hasFocus = true;
14242             if(this.triggerAction == 'all') {
14243                 this.doQuery(this.allQuery, true);
14244             } else {
14245                 this.doQuery(this.getRawValue());
14246             }
14247             if (!this.blockFocus) {
14248                 this.inputEl().focus();
14249             }
14250         }
14251     },
14252     
14253     onTickableTriggerClick : function(e)
14254     {
14255         if(this.disabled){
14256             return;
14257         }
14258         
14259         this.page = 0;
14260         this.loadNext = false;
14261         this.hasFocus = true;
14262         
14263         if(this.triggerAction == 'all') {
14264             this.doQuery(this.allQuery, true);
14265         } else {
14266             this.doQuery(this.getRawValue());
14267         }
14268     },
14269     
14270     onSearchFieldClick : function(e)
14271     {
14272         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14273             this.onTickableFooterButtonClick(e, false, false);
14274             return;
14275         }
14276         
14277         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14278             return;
14279         }
14280         
14281         this.page = 0;
14282         this.loadNext = false;
14283         this.hasFocus = true;
14284         
14285         if(this.triggerAction == 'all') {
14286             this.doQuery(this.allQuery, true);
14287         } else {
14288             this.doQuery(this.getRawValue());
14289         }
14290     },
14291     
14292     listKeyPress : function(e)
14293     {
14294         //Roo.log('listkeypress');
14295         // scroll to first matching element based on key pres..
14296         if (e.isSpecialKey()) {
14297             return false;
14298         }
14299         var k = String.fromCharCode(e.getKey()).toUpperCase();
14300         //Roo.log(k);
14301         var match  = false;
14302         var csel = this.view.getSelectedNodes();
14303         var cselitem = false;
14304         if (csel.length) {
14305             var ix = this.view.indexOf(csel[0]);
14306             cselitem  = this.store.getAt(ix);
14307             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14308                 cselitem = false;
14309             }
14310             
14311         }
14312         
14313         this.store.each(function(v) { 
14314             if (cselitem) {
14315                 // start at existing selection.
14316                 if (cselitem.id == v.id) {
14317                     cselitem = false;
14318                 }
14319                 return true;
14320             }
14321                 
14322             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14323                 match = this.store.indexOf(v);
14324                 return false;
14325             }
14326             return true;
14327         }, this);
14328         
14329         if (match === false) {
14330             return true; // no more action?
14331         }
14332         // scroll to?
14333         this.view.select(match);
14334         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14335         sn.scrollIntoView(sn.dom.parentNode, false);
14336     },
14337     
14338     onViewScroll : function(e, t){
14339         
14340         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
14341             return;
14342         }
14343         
14344         this.hasQuery = true;
14345         
14346         this.loading = this.list.select('.loading', true).first();
14347         
14348         if(this.loading === null){
14349             this.list.createChild({
14350                 tag: 'div',
14351                 cls: 'loading roo-select2-more-results roo-select2-active',
14352                 html: 'Loading more results...'
14353             });
14354             
14355             this.loading = this.list.select('.loading', true).first();
14356             
14357             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14358             
14359             this.loading.hide();
14360         }
14361         
14362         this.loading.show();
14363         
14364         var _combo = this;
14365         
14366         this.page++;
14367         this.loadNext = true;
14368         
14369         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14370         
14371         return;
14372     },
14373     
14374     addItem : function(o)
14375     {   
14376         var dv = ''; // display value
14377         
14378         if (this.displayField) {
14379             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14380         } else {
14381             // this is an error condition!!!
14382             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14383         }
14384         
14385         if(!dv.length){
14386             return;
14387         }
14388         
14389         var choice = this.choices.createChild({
14390             tag: 'li',
14391             cls: 'roo-select2-search-choice',
14392             cn: [
14393                 {
14394                     tag: 'div',
14395                     html: dv
14396                 },
14397                 {
14398                     tag: 'a',
14399                     href: '#',
14400                     cls: 'roo-select2-search-choice-close fa fa-times',
14401                     tabindex: '-1'
14402                 }
14403             ]
14404             
14405         }, this.searchField);
14406         
14407         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14408         
14409         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14410         
14411         this.item.push(o);
14412         
14413         this.lastData = o;
14414         
14415         this.syncValue();
14416         
14417         this.inputEl().dom.value = '';
14418         
14419         this.validate();
14420     },
14421     
14422     onRemoveItem : function(e, _self, o)
14423     {
14424         e.preventDefault();
14425         
14426         this.lastItem = Roo.apply([], this.item);
14427         
14428         var index = this.item.indexOf(o.data) * 1;
14429         
14430         if( index < 0){
14431             Roo.log('not this item?!');
14432             return;
14433         }
14434         
14435         this.item.splice(index, 1);
14436         o.item.remove();
14437         
14438         this.syncValue();
14439         
14440         this.fireEvent('remove', this, e);
14441         
14442         this.validate();
14443         
14444     },
14445     
14446     syncValue : function()
14447     {
14448         if(!this.item.length){
14449             this.clearValue();
14450             return;
14451         }
14452             
14453         var value = [];
14454         var _this = this;
14455         Roo.each(this.item, function(i){
14456             if(_this.valueField){
14457                 value.push(i[_this.valueField]);
14458                 return;
14459             }
14460
14461             value.push(i);
14462         });
14463
14464         this.value = value.join(',');
14465
14466         if(this.hiddenField){
14467             this.hiddenField.dom.value = this.value;
14468         }
14469         
14470         this.store.fireEvent("datachanged", this.store);
14471         
14472         this.validate();
14473     },
14474     
14475     clearItem : function()
14476     {
14477         if(!this.multiple){
14478             return;
14479         }
14480         
14481         this.item = [];
14482         
14483         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14484            c.remove();
14485         });
14486         
14487         this.syncValue();
14488         
14489         this.validate();
14490         
14491         if(this.tickable && !Roo.isTouch){
14492             this.view.refresh();
14493         }
14494     },
14495     
14496     inputEl: function ()
14497     {
14498         if(Roo.isIOS && this.useNativeIOS){
14499             return this.el.select('select.roo-ios-select', true).first();
14500         }
14501         
14502         if(Roo.isTouch && this.mobileTouchView){
14503             return this.el.select('input.form-control',true).first();
14504         }
14505         
14506         if(this.tickable){
14507             return this.searchField;
14508         }
14509         
14510         return this.el.select('input.form-control',true).first();
14511     },
14512     
14513     onTickableFooterButtonClick : function(e, btn, el)
14514     {
14515         e.preventDefault();
14516         
14517         this.lastItem = Roo.apply([], this.item);
14518         
14519         if(btn && btn.name == 'cancel'){
14520             this.tickItems = Roo.apply([], this.item);
14521             this.collapse();
14522             return;
14523         }
14524         
14525         this.clearItem();
14526         
14527         var _this = this;
14528         
14529         Roo.each(this.tickItems, function(o){
14530             _this.addItem(o);
14531         });
14532         
14533         this.collapse();
14534         
14535     },
14536     
14537     validate : function()
14538     {
14539         var v = this.getRawValue();
14540         
14541         if(this.multiple){
14542             v = this.getValue();
14543         }
14544         
14545         if(this.disabled || this.allowBlank || v.length){
14546             this.markValid();
14547             return true;
14548         }
14549         
14550         this.markInvalid();
14551         return false;
14552     },
14553     
14554     tickableInputEl : function()
14555     {
14556         if(!this.tickable || !this.editable){
14557             return this.inputEl();
14558         }
14559         
14560         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14561     },
14562     
14563     
14564     getAutoCreateTouchView : function()
14565     {
14566         var id = Roo.id();
14567         
14568         var cfg = {
14569             cls: 'form-group' //input-group
14570         };
14571         
14572         var input =  {
14573             tag: 'input',
14574             id : id,
14575             type : this.inputType,
14576             cls : 'form-control x-combo-noedit',
14577             autocomplete: 'new-password',
14578             placeholder : this.placeholder || '',
14579             readonly : true
14580         };
14581         
14582         if (this.name) {
14583             input.name = this.name;
14584         }
14585         
14586         if (this.size) {
14587             input.cls += ' input-' + this.size;
14588         }
14589         
14590         if (this.disabled) {
14591             input.disabled = true;
14592         }
14593         
14594         var inputblock = {
14595             cls : '',
14596             cn : [
14597                 input
14598             ]
14599         };
14600         
14601         if(this.before){
14602             inputblock.cls += ' input-group';
14603             
14604             inputblock.cn.unshift({
14605                 tag :'span',
14606                 cls : 'input-group-addon',
14607                 html : this.before
14608             });
14609         }
14610         
14611         if(this.removable && !this.multiple){
14612             inputblock.cls += ' roo-removable';
14613             
14614             inputblock.cn.push({
14615                 tag: 'button',
14616                 html : 'x',
14617                 cls : 'roo-combo-removable-btn close'
14618             });
14619         }
14620
14621         if(this.hasFeedback && !this.allowBlank){
14622             
14623             inputblock.cls += ' has-feedback';
14624             
14625             inputblock.cn.push({
14626                 tag: 'span',
14627                 cls: 'glyphicon form-control-feedback'
14628             });
14629             
14630         }
14631         
14632         if (this.after) {
14633             
14634             inputblock.cls += (this.before) ? '' : ' input-group';
14635             
14636             inputblock.cn.push({
14637                 tag :'span',
14638                 cls : 'input-group-addon',
14639                 html : this.after
14640             });
14641         }
14642
14643         var box = {
14644             tag: 'div',
14645             cn: [
14646                 {
14647                     tag: 'input',
14648                     type : 'hidden',
14649                     cls: 'form-hidden-field'
14650                 },
14651                 inputblock
14652             ]
14653             
14654         };
14655         
14656         if(this.multiple){
14657             box = {
14658                 tag: 'div',
14659                 cn: [
14660                     {
14661                         tag: 'input',
14662                         type : 'hidden',
14663                         cls: 'form-hidden-field'
14664                     },
14665                     {
14666                         tag: 'ul',
14667                         cls: 'roo-select2-choices',
14668                         cn:[
14669                             {
14670                                 tag: 'li',
14671                                 cls: 'roo-select2-search-field',
14672                                 cn: [
14673
14674                                     inputblock
14675                                 ]
14676                             }
14677                         ]
14678                     }
14679                 ]
14680             }
14681         };
14682         
14683         var combobox = {
14684             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14685             cn: [
14686                 box
14687             ]
14688         };
14689         
14690         if(!this.multiple && this.showToggleBtn){
14691             
14692             var caret = {
14693                         tag: 'span',
14694                         cls: 'caret'
14695             };
14696             
14697             if (this.caret != false) {
14698                 caret = {
14699                      tag: 'i',
14700                      cls: 'fa fa-' + this.caret
14701                 };
14702                 
14703             }
14704             
14705             combobox.cn.push({
14706                 tag :'span',
14707                 cls : 'input-group-addon btn dropdown-toggle',
14708                 cn : [
14709                     caret,
14710                     {
14711                         tag: 'span',
14712                         cls: 'combobox-clear',
14713                         cn  : [
14714                             {
14715                                 tag : 'i',
14716                                 cls: 'icon-remove'
14717                             }
14718                         ]
14719                     }
14720                 ]
14721
14722             })
14723         }
14724         
14725         if(this.multiple){
14726             combobox.cls += ' roo-select2-container-multi';
14727         }
14728         
14729         var align = this.labelAlign || this.parentLabelAlign();
14730         
14731         if (align ==='left' && this.fieldLabel.length) {
14732
14733             cfg.cn = [
14734                 {
14735                    tag : 'i',
14736                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14737                    tooltip : 'This field is required'
14738                 },
14739                 {
14740                     tag: 'label',
14741                     cls : 'control-label',
14742                     html : this.fieldLabel
14743
14744                 },
14745                 {
14746                     cls : '', 
14747                     cn: [
14748                         combobox
14749                     ]
14750                 }
14751             ];
14752             
14753             var labelCfg = cfg.cn[1];
14754             var contentCfg = cfg.cn[2];
14755             
14756
14757             if(this.indicatorpos == 'right'){
14758                 cfg.cn = [
14759                     {
14760                         tag: 'label',
14761                         'for' :  id,
14762                         cls : 'control-label',
14763                         cn : [
14764                             {
14765                                 tag : 'span',
14766                                 html : this.fieldLabel
14767                             },
14768                             {
14769                                 tag : 'i',
14770                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14771                                 tooltip : 'This field is required'
14772                             }
14773                         ]
14774                     },
14775                     {
14776                         cls : "",
14777                         cn: [
14778                             combobox
14779                         ]
14780                     }
14781
14782                 ];
14783                 
14784                 labelCfg = cfg.cn[0];
14785                 contentCfg = cfg.cn[1];
14786             }
14787             
14788            
14789             
14790             if(this.labelWidth > 12){
14791                 labelCfg.style = "width: " + this.labelWidth + 'px';
14792             }
14793             
14794             if(this.labelWidth < 13 && this.labelmd == 0){
14795                 this.labelmd = this.labelWidth;
14796             }
14797             
14798             if(this.labellg > 0){
14799                 labelCfg.cls += ' col-lg-' + this.labellg;
14800                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14801             }
14802             
14803             if(this.labelmd > 0){
14804                 labelCfg.cls += ' col-md-' + this.labelmd;
14805                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14806             }
14807             
14808             if(this.labelsm > 0){
14809                 labelCfg.cls += ' col-sm-' + this.labelsm;
14810                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14811             }
14812             
14813             if(this.labelxs > 0){
14814                 labelCfg.cls += ' col-xs-' + this.labelxs;
14815                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14816             }
14817                 
14818                 
14819         } else if ( this.fieldLabel.length) {
14820             cfg.cn = [
14821                 {
14822                    tag : 'i',
14823                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14824                    tooltip : 'This field is required'
14825                 },
14826                 {
14827                     tag: 'label',
14828                     cls : 'control-label',
14829                     html : this.fieldLabel
14830
14831                 },
14832                 {
14833                     cls : '', 
14834                     cn: [
14835                         combobox
14836                     ]
14837                 }
14838             ];
14839             
14840             if(this.indicatorpos == 'right'){
14841                 cfg.cn = [
14842                     {
14843                         tag: 'label',
14844                         cls : 'control-label',
14845                         html : this.fieldLabel,
14846                         cn : [
14847                             {
14848                                tag : 'i',
14849                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14850                                tooltip : 'This field is required'
14851                             }
14852                         ]
14853                     },
14854                     {
14855                         cls : '', 
14856                         cn: [
14857                             combobox
14858                         ]
14859                     }
14860                 ];
14861             }
14862         } else {
14863             cfg.cn = combobox;    
14864         }
14865         
14866         
14867         var settings = this;
14868         
14869         ['xs','sm','md','lg'].map(function(size){
14870             if (settings[size]) {
14871                 cfg.cls += ' col-' + size + '-' + settings[size];
14872             }
14873         });
14874         
14875         return cfg;
14876     },
14877     
14878     initTouchView : function()
14879     {
14880         this.renderTouchView();
14881         
14882         this.touchViewEl.on('scroll', function(){
14883             this.el.dom.scrollTop = 0;
14884         }, this);
14885         
14886         this.originalValue = this.getValue();
14887         
14888         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14889         
14890         this.inputEl().on("click", this.showTouchView, this);
14891         if (this.triggerEl) {
14892             this.triggerEl.on("click", this.showTouchView, this);
14893         }
14894         
14895         
14896         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14897         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14898         
14899         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14900         
14901         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14902         this.store.on('load', this.onTouchViewLoad, this);
14903         this.store.on('loadexception', this.onTouchViewLoadException, this);
14904         
14905         if(this.hiddenName){
14906             
14907             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14908             
14909             this.hiddenField.dom.value =
14910                 this.hiddenValue !== undefined ? this.hiddenValue :
14911                 this.value !== undefined ? this.value : '';
14912         
14913             this.el.dom.removeAttribute('name');
14914             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14915         }
14916         
14917         if(this.multiple){
14918             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14919             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14920         }
14921         
14922         if(this.removable && !this.multiple){
14923             var close = this.closeTriggerEl();
14924             if(close){
14925                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14926                 close.on('click', this.removeBtnClick, this, close);
14927             }
14928         }
14929         /*
14930          * fix the bug in Safari iOS8
14931          */
14932         this.inputEl().on("focus", function(e){
14933             document.activeElement.blur();
14934         }, this);
14935         
14936         return;
14937         
14938         
14939     },
14940     
14941     renderTouchView : function()
14942     {
14943         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14944         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14945         
14946         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14947         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14948         
14949         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14950         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14951         this.touchViewBodyEl.setStyle('overflow', 'auto');
14952         
14953         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14954         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14955         
14956         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14957         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14958         
14959     },
14960     
14961     showTouchView : function()
14962     {
14963         if(this.disabled){
14964             return;
14965         }
14966         
14967         this.touchViewHeaderEl.hide();
14968
14969         if(this.modalTitle.length){
14970             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14971             this.touchViewHeaderEl.show();
14972         }
14973
14974         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14975         this.touchViewEl.show();
14976
14977         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14978         
14979         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14980         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14981
14982         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14983
14984         if(this.modalTitle.length){
14985             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14986         }
14987         
14988         this.touchViewBodyEl.setHeight(bodyHeight);
14989
14990         if(this.animate){
14991             var _this = this;
14992             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14993         }else{
14994             this.touchViewEl.addClass('in');
14995         }
14996
14997         this.doTouchViewQuery();
14998         
14999     },
15000     
15001     hideTouchView : function()
15002     {
15003         this.touchViewEl.removeClass('in');
15004
15005         if(this.animate){
15006             var _this = this;
15007             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15008         }else{
15009             this.touchViewEl.setStyle('display', 'none');
15010         }
15011         
15012     },
15013     
15014     setTouchViewValue : function()
15015     {
15016         if(this.multiple){
15017             this.clearItem();
15018         
15019             var _this = this;
15020
15021             Roo.each(this.tickItems, function(o){
15022                 this.addItem(o);
15023             }, this);
15024         }
15025         
15026         this.hideTouchView();
15027     },
15028     
15029     doTouchViewQuery : function()
15030     {
15031         var qe = {
15032             query: '',
15033             forceAll: true,
15034             combo: this,
15035             cancel:false
15036         };
15037         
15038         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15039             return false;
15040         }
15041         
15042         if(!this.alwaysQuery || this.mode == 'local'){
15043             this.onTouchViewLoad();
15044             return;
15045         }
15046         
15047         this.store.load();
15048     },
15049     
15050     onTouchViewBeforeLoad : function(combo,opts)
15051     {
15052         return;
15053     },
15054
15055     // private
15056     onTouchViewLoad : function()
15057     {
15058         if(this.store.getCount() < 1){
15059             this.onTouchViewEmptyResults();
15060             return;
15061         }
15062         
15063         this.clearTouchView();
15064         
15065         var rawValue = this.getRawValue();
15066         
15067         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15068         
15069         this.tickItems = [];
15070         
15071         this.store.data.each(function(d, rowIndex){
15072             var row = this.touchViewListGroup.createChild(template);
15073             
15074             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15075                 row.addClass(d.data.cls);
15076             }
15077             
15078             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15079                 var cfg = {
15080                     data : d.data,
15081                     html : d.data[this.displayField]
15082                 };
15083                 
15084                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15085                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15086                 }
15087             }
15088             row.removeClass('selected');
15089             if(!this.multiple && this.valueField &&
15090                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15091             {
15092                 // radio buttons..
15093                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15094                 row.addClass('selected');
15095             }
15096             
15097             if(this.multiple && this.valueField &&
15098                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15099             {
15100                 
15101                 // checkboxes...
15102                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15103                 this.tickItems.push(d.data);
15104             }
15105             
15106             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15107             
15108         }, this);
15109         
15110         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15111         
15112         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15113
15114         if(this.modalTitle.length){
15115             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15116         }
15117
15118         var listHeight = this.touchViewListGroup.getHeight();
15119         
15120         var _this = this;
15121         
15122         if(firstChecked && listHeight > bodyHeight){
15123             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15124         }
15125         
15126     },
15127     
15128     onTouchViewLoadException : function()
15129     {
15130         this.hideTouchView();
15131     },
15132     
15133     onTouchViewEmptyResults : function()
15134     {
15135         this.clearTouchView();
15136         
15137         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15138         
15139         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15140         
15141     },
15142     
15143     clearTouchView : function()
15144     {
15145         this.touchViewListGroup.dom.innerHTML = '';
15146     },
15147     
15148     onTouchViewClick : function(e, el, o)
15149     {
15150         e.preventDefault();
15151         
15152         var row = o.row;
15153         var rowIndex = o.rowIndex;
15154         
15155         var r = this.store.getAt(rowIndex);
15156         
15157         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15158             
15159             if(!this.multiple){
15160                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15161                     c.dom.removeAttribute('checked');
15162                 }, this);
15163
15164                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15165
15166                 this.setFromData(r.data);
15167
15168                 var close = this.closeTriggerEl();
15169
15170                 if(close){
15171                     close.show();
15172                 }
15173
15174                 this.hideTouchView();
15175
15176                 this.fireEvent('select', this, r, rowIndex);
15177
15178                 return;
15179             }
15180
15181             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15182                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15183                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15184                 return;
15185             }
15186
15187             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15188             this.addItem(r.data);
15189             this.tickItems.push(r.data);
15190         }
15191     },
15192     
15193     getAutoCreateNativeIOS : function()
15194     {
15195         var cfg = {
15196             cls: 'form-group' //input-group,
15197         };
15198         
15199         var combobox =  {
15200             tag: 'select',
15201             cls : 'roo-ios-select'
15202         };
15203         
15204         if (this.name) {
15205             combobox.name = this.name;
15206         }
15207         
15208         if (this.disabled) {
15209             combobox.disabled = true;
15210         }
15211         
15212         var settings = this;
15213         
15214         ['xs','sm','md','lg'].map(function(size){
15215             if (settings[size]) {
15216                 cfg.cls += ' col-' + size + '-' + settings[size];
15217             }
15218         });
15219         
15220         cfg.cn = combobox;
15221         
15222         return cfg;
15223         
15224     },
15225     
15226     initIOSView : function()
15227     {
15228         this.store.on('load', this.onIOSViewLoad, this);
15229         
15230         return;
15231     },
15232     
15233     onIOSViewLoad : function()
15234     {
15235         if(this.store.getCount() < 1){
15236             return;
15237         }
15238         
15239         this.clearIOSView();
15240         
15241         if(this.allowBlank) {
15242             
15243             var default_text = '-- SELECT --';
15244             
15245             if(this.placeholder.length){
15246                 default_text = this.placeholder;
15247             }
15248             
15249             if(this.emptyTitle.length){
15250                 default_text += ' - ' + this.emptyTitle + ' -';
15251             }
15252             
15253             var opt = this.inputEl().createChild({
15254                 tag: 'option',
15255                 value : 0,
15256                 html : default_text
15257             });
15258             
15259             var o = {};
15260             o[this.valueField] = 0;
15261             o[this.displayField] = default_text;
15262             
15263             this.ios_options.push({
15264                 data : o,
15265                 el : opt
15266             });
15267             
15268         }
15269         
15270         this.store.data.each(function(d, rowIndex){
15271             
15272             var html = '';
15273             
15274             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15275                 html = d.data[this.displayField];
15276             }
15277             
15278             var value = '';
15279             
15280             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15281                 value = d.data[this.valueField];
15282             }
15283             
15284             var option = {
15285                 tag: 'option',
15286                 value : value,
15287                 html : html
15288             };
15289             
15290             if(this.value == d.data[this.valueField]){
15291                 option['selected'] = true;
15292             }
15293             
15294             var opt = this.inputEl().createChild(option);
15295             
15296             this.ios_options.push({
15297                 data : d.data,
15298                 el : opt
15299             });
15300             
15301         }, this);
15302         
15303         this.inputEl().on('change', function(){
15304            this.fireEvent('select', this);
15305         }, this);
15306         
15307     },
15308     
15309     clearIOSView: function()
15310     {
15311         this.inputEl().dom.innerHTML = '';
15312         
15313         this.ios_options = [];
15314     },
15315     
15316     setIOSValue: function(v)
15317     {
15318         this.value = v;
15319         
15320         if(!this.ios_options){
15321             return;
15322         }
15323         
15324         Roo.each(this.ios_options, function(opts){
15325            
15326            opts.el.dom.removeAttribute('selected');
15327            
15328            if(opts.data[this.valueField] != v){
15329                return;
15330            }
15331            
15332            opts.el.dom.setAttribute('selected', true);
15333            
15334         }, this);
15335     }
15336
15337     /** 
15338     * @cfg {Boolean} grow 
15339     * @hide 
15340     */
15341     /** 
15342     * @cfg {Number} growMin 
15343     * @hide 
15344     */
15345     /** 
15346     * @cfg {Number} growMax 
15347     * @hide 
15348     */
15349     /**
15350      * @hide
15351      * @method autoSize
15352      */
15353 });
15354
15355 Roo.apply(Roo.bootstrap.ComboBox,  {
15356     
15357     header : {
15358         tag: 'div',
15359         cls: 'modal-header',
15360         cn: [
15361             {
15362                 tag: 'h4',
15363                 cls: 'modal-title'
15364             }
15365         ]
15366     },
15367     
15368     body : {
15369         tag: 'div',
15370         cls: 'modal-body',
15371         cn: [
15372             {
15373                 tag: 'ul',
15374                 cls: 'list-group'
15375             }
15376         ]
15377     },
15378     
15379     listItemRadio : {
15380         tag: 'li',
15381         cls: 'list-group-item',
15382         cn: [
15383             {
15384                 tag: 'span',
15385                 cls: 'roo-combobox-list-group-item-value'
15386             },
15387             {
15388                 tag: 'div',
15389                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15390                 cn: [
15391                     {
15392                         tag: 'input',
15393                         type: 'radio'
15394                     },
15395                     {
15396                         tag: 'label'
15397                     }
15398                 ]
15399             }
15400         ]
15401     },
15402     
15403     listItemCheckbox : {
15404         tag: 'li',
15405         cls: 'list-group-item',
15406         cn: [
15407             {
15408                 tag: 'span',
15409                 cls: 'roo-combobox-list-group-item-value'
15410             },
15411             {
15412                 tag: 'div',
15413                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15414                 cn: [
15415                     {
15416                         tag: 'input',
15417                         type: 'checkbox'
15418                     },
15419                     {
15420                         tag: 'label'
15421                     }
15422                 ]
15423             }
15424         ]
15425     },
15426     
15427     emptyResult : {
15428         tag: 'div',
15429         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15430     },
15431     
15432     footer : {
15433         tag: 'div',
15434         cls: 'modal-footer',
15435         cn: [
15436             {
15437                 tag: 'div',
15438                 cls: 'row',
15439                 cn: [
15440                     {
15441                         tag: 'div',
15442                         cls: 'col-xs-6 text-left',
15443                         cn: {
15444                             tag: 'button',
15445                             cls: 'btn btn-danger roo-touch-view-cancel',
15446                             html: 'Cancel'
15447                         }
15448                     },
15449                     {
15450                         tag: 'div',
15451                         cls: 'col-xs-6 text-right',
15452                         cn: {
15453                             tag: 'button',
15454                             cls: 'btn btn-success roo-touch-view-ok',
15455                             html: 'OK'
15456                         }
15457                     }
15458                 ]
15459             }
15460         ]
15461         
15462     }
15463 });
15464
15465 Roo.apply(Roo.bootstrap.ComboBox,  {
15466     
15467     touchViewTemplate : {
15468         tag: 'div',
15469         cls: 'modal fade roo-combobox-touch-view',
15470         cn: [
15471             {
15472                 tag: 'div',
15473                 cls: 'modal-dialog',
15474                 style : 'position:fixed', // we have to fix position....
15475                 cn: [
15476                     {
15477                         tag: 'div',
15478                         cls: 'modal-content',
15479                         cn: [
15480                             Roo.bootstrap.ComboBox.header,
15481                             Roo.bootstrap.ComboBox.body,
15482                             Roo.bootstrap.ComboBox.footer
15483                         ]
15484                     }
15485                 ]
15486             }
15487         ]
15488     }
15489 });/*
15490  * Based on:
15491  * Ext JS Library 1.1.1
15492  * Copyright(c) 2006-2007, Ext JS, LLC.
15493  *
15494  * Originally Released Under LGPL - original licence link has changed is not relivant.
15495  *
15496  * Fork - LGPL
15497  * <script type="text/javascript">
15498  */
15499
15500 /**
15501  * @class Roo.View
15502  * @extends Roo.util.Observable
15503  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15504  * This class also supports single and multi selection modes. <br>
15505  * Create a data model bound view:
15506  <pre><code>
15507  var store = new Roo.data.Store(...);
15508
15509  var view = new Roo.View({
15510     el : "my-element",
15511     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15512  
15513     singleSelect: true,
15514     selectedClass: "ydataview-selected",
15515     store: store
15516  });
15517
15518  // listen for node click?
15519  view.on("click", function(vw, index, node, e){
15520  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15521  });
15522
15523  // load XML data
15524  dataModel.load("foobar.xml");
15525  </code></pre>
15526  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15527  * <br><br>
15528  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15529  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15530  * 
15531  * Note: old style constructor is still suported (container, template, config)
15532  * 
15533  * @constructor
15534  * Create a new View
15535  * @param {Object} config The config object
15536  * 
15537  */
15538 Roo.View = function(config, depreciated_tpl, depreciated_config){
15539     
15540     this.parent = false;
15541     
15542     if (typeof(depreciated_tpl) == 'undefined') {
15543         // new way.. - universal constructor.
15544         Roo.apply(this, config);
15545         this.el  = Roo.get(this.el);
15546     } else {
15547         // old format..
15548         this.el  = Roo.get(config);
15549         this.tpl = depreciated_tpl;
15550         Roo.apply(this, depreciated_config);
15551     }
15552     this.wrapEl  = this.el.wrap().wrap();
15553     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15554     
15555     
15556     if(typeof(this.tpl) == "string"){
15557         this.tpl = new Roo.Template(this.tpl);
15558     } else {
15559         // support xtype ctors..
15560         this.tpl = new Roo.factory(this.tpl, Roo);
15561     }
15562     
15563     
15564     this.tpl.compile();
15565     
15566     /** @private */
15567     this.addEvents({
15568         /**
15569          * @event beforeclick
15570          * Fires before a click is processed. Returns false to cancel the default action.
15571          * @param {Roo.View} this
15572          * @param {Number} index The index of the target node
15573          * @param {HTMLElement} node The target node
15574          * @param {Roo.EventObject} e The raw event object
15575          */
15576             "beforeclick" : true,
15577         /**
15578          * @event click
15579          * Fires when a template node is clicked.
15580          * @param {Roo.View} this
15581          * @param {Number} index The index of the target node
15582          * @param {HTMLElement} node The target node
15583          * @param {Roo.EventObject} e The raw event object
15584          */
15585             "click" : true,
15586         /**
15587          * @event dblclick
15588          * Fires when a template node is double clicked.
15589          * @param {Roo.View} this
15590          * @param {Number} index The index of the target node
15591          * @param {HTMLElement} node The target node
15592          * @param {Roo.EventObject} e The raw event object
15593          */
15594             "dblclick" : true,
15595         /**
15596          * @event contextmenu
15597          * Fires when a template node is right clicked.
15598          * @param {Roo.View} this
15599          * @param {Number} index The index of the target node
15600          * @param {HTMLElement} node The target node
15601          * @param {Roo.EventObject} e The raw event object
15602          */
15603             "contextmenu" : true,
15604         /**
15605          * @event selectionchange
15606          * Fires when the selected nodes change.
15607          * @param {Roo.View} this
15608          * @param {Array} selections Array of the selected nodes
15609          */
15610             "selectionchange" : true,
15611     
15612         /**
15613          * @event beforeselect
15614          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15615          * @param {Roo.View} this
15616          * @param {HTMLElement} node The node to be selected
15617          * @param {Array} selections Array of currently selected nodes
15618          */
15619             "beforeselect" : true,
15620         /**
15621          * @event preparedata
15622          * Fires on every row to render, to allow you to change the data.
15623          * @param {Roo.View} this
15624          * @param {Object} data to be rendered (change this)
15625          */
15626           "preparedata" : true
15627           
15628           
15629         });
15630
15631
15632
15633     this.el.on({
15634         "click": this.onClick,
15635         "dblclick": this.onDblClick,
15636         "contextmenu": this.onContextMenu,
15637         scope:this
15638     });
15639
15640     this.selections = [];
15641     this.nodes = [];
15642     this.cmp = new Roo.CompositeElementLite([]);
15643     if(this.store){
15644         this.store = Roo.factory(this.store, Roo.data);
15645         this.setStore(this.store, true);
15646     }
15647     
15648     if ( this.footer && this.footer.xtype) {
15649            
15650          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15651         
15652         this.footer.dataSource = this.store;
15653         this.footer.container = fctr;
15654         this.footer = Roo.factory(this.footer, Roo);
15655         fctr.insertFirst(this.el);
15656         
15657         // this is a bit insane - as the paging toolbar seems to detach the el..
15658 //        dom.parentNode.parentNode.parentNode
15659          // they get detached?
15660     }
15661     
15662     
15663     Roo.View.superclass.constructor.call(this);
15664     
15665     
15666 };
15667
15668 Roo.extend(Roo.View, Roo.util.Observable, {
15669     
15670      /**
15671      * @cfg {Roo.data.Store} store Data store to load data from.
15672      */
15673     store : false,
15674     
15675     /**
15676      * @cfg {String|Roo.Element} el The container element.
15677      */
15678     el : '',
15679     
15680     /**
15681      * @cfg {String|Roo.Template} tpl The template used by this View 
15682      */
15683     tpl : false,
15684     /**
15685      * @cfg {String} dataName the named area of the template to use as the data area
15686      *                          Works with domtemplates roo-name="name"
15687      */
15688     dataName: false,
15689     /**
15690      * @cfg {String} selectedClass The css class to add to selected nodes
15691      */
15692     selectedClass : "x-view-selected",
15693      /**
15694      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15695      */
15696     emptyText : "",
15697     
15698     /**
15699      * @cfg {String} text to display on mask (default Loading)
15700      */
15701     mask : false,
15702     /**
15703      * @cfg {Boolean} multiSelect Allow multiple selection
15704      */
15705     multiSelect : false,
15706     /**
15707      * @cfg {Boolean} singleSelect Allow single selection
15708      */
15709     singleSelect:  false,
15710     
15711     /**
15712      * @cfg {Boolean} toggleSelect - selecting 
15713      */
15714     toggleSelect : false,
15715     
15716     /**
15717      * @cfg {Boolean} tickable - selecting 
15718      */
15719     tickable : false,
15720     
15721     /**
15722      * Returns the element this view is bound to.
15723      * @return {Roo.Element}
15724      */
15725     getEl : function(){
15726         return this.wrapEl;
15727     },
15728     
15729     
15730
15731     /**
15732      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15733      */
15734     refresh : function(){
15735         //Roo.log('refresh');
15736         var t = this.tpl;
15737         
15738         // if we are using something like 'domtemplate', then
15739         // the what gets used is:
15740         // t.applySubtemplate(NAME, data, wrapping data..)
15741         // the outer template then get' applied with
15742         //     the store 'extra data'
15743         // and the body get's added to the
15744         //      roo-name="data" node?
15745         //      <span class='roo-tpl-{name}'></span> ?????
15746         
15747         
15748         
15749         this.clearSelections();
15750         this.el.update("");
15751         var html = [];
15752         var records = this.store.getRange();
15753         if(records.length < 1) {
15754             
15755             // is this valid??  = should it render a template??
15756             
15757             this.el.update(this.emptyText);
15758             return;
15759         }
15760         var el = this.el;
15761         if (this.dataName) {
15762             this.el.update(t.apply(this.store.meta)); //????
15763             el = this.el.child('.roo-tpl-' + this.dataName);
15764         }
15765         
15766         for(var i = 0, len = records.length; i < len; i++){
15767             var data = this.prepareData(records[i].data, i, records[i]);
15768             this.fireEvent("preparedata", this, data, i, records[i]);
15769             
15770             var d = Roo.apply({}, data);
15771             
15772             if(this.tickable){
15773                 Roo.apply(d, {'roo-id' : Roo.id()});
15774                 
15775                 var _this = this;
15776             
15777                 Roo.each(this.parent.item, function(item){
15778                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15779                         return;
15780                     }
15781                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15782                 });
15783             }
15784             
15785             html[html.length] = Roo.util.Format.trim(
15786                 this.dataName ?
15787                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15788                     t.apply(d)
15789             );
15790         }
15791         
15792         
15793         
15794         el.update(html.join(""));
15795         this.nodes = el.dom.childNodes;
15796         this.updateIndexes(0);
15797     },
15798     
15799
15800     /**
15801      * Function to override to reformat the data that is sent to
15802      * the template for each node.
15803      * DEPRICATED - use the preparedata event handler.
15804      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15805      * a JSON object for an UpdateManager bound view).
15806      */
15807     prepareData : function(data, index, record)
15808     {
15809         this.fireEvent("preparedata", this, data, index, record);
15810         return data;
15811     },
15812
15813     onUpdate : function(ds, record){
15814         // Roo.log('on update');   
15815         this.clearSelections();
15816         var index = this.store.indexOf(record);
15817         var n = this.nodes[index];
15818         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15819         n.parentNode.removeChild(n);
15820         this.updateIndexes(index, index);
15821     },
15822
15823     
15824     
15825 // --------- FIXME     
15826     onAdd : function(ds, records, index)
15827     {
15828         //Roo.log(['on Add', ds, records, index] );        
15829         this.clearSelections();
15830         if(this.nodes.length == 0){
15831             this.refresh();
15832             return;
15833         }
15834         var n = this.nodes[index];
15835         for(var i = 0, len = records.length; i < len; i++){
15836             var d = this.prepareData(records[i].data, i, records[i]);
15837             if(n){
15838                 this.tpl.insertBefore(n, d);
15839             }else{
15840                 
15841                 this.tpl.append(this.el, d);
15842             }
15843         }
15844         this.updateIndexes(index);
15845     },
15846
15847     onRemove : function(ds, record, index){
15848        // Roo.log('onRemove');
15849         this.clearSelections();
15850         var el = this.dataName  ?
15851             this.el.child('.roo-tpl-' + this.dataName) :
15852             this.el; 
15853         
15854         el.dom.removeChild(this.nodes[index]);
15855         this.updateIndexes(index);
15856     },
15857
15858     /**
15859      * Refresh an individual node.
15860      * @param {Number} index
15861      */
15862     refreshNode : function(index){
15863         this.onUpdate(this.store, this.store.getAt(index));
15864     },
15865
15866     updateIndexes : function(startIndex, endIndex){
15867         var ns = this.nodes;
15868         startIndex = startIndex || 0;
15869         endIndex = endIndex || ns.length - 1;
15870         for(var i = startIndex; i <= endIndex; i++){
15871             ns[i].nodeIndex = i;
15872         }
15873     },
15874
15875     /**
15876      * Changes the data store this view uses and refresh the view.
15877      * @param {Store} store
15878      */
15879     setStore : function(store, initial){
15880         if(!initial && this.store){
15881             this.store.un("datachanged", this.refresh);
15882             this.store.un("add", this.onAdd);
15883             this.store.un("remove", this.onRemove);
15884             this.store.un("update", this.onUpdate);
15885             this.store.un("clear", this.refresh);
15886             this.store.un("beforeload", this.onBeforeLoad);
15887             this.store.un("load", this.onLoad);
15888             this.store.un("loadexception", this.onLoad);
15889         }
15890         if(store){
15891           
15892             store.on("datachanged", this.refresh, this);
15893             store.on("add", this.onAdd, this);
15894             store.on("remove", this.onRemove, this);
15895             store.on("update", this.onUpdate, this);
15896             store.on("clear", this.refresh, this);
15897             store.on("beforeload", this.onBeforeLoad, this);
15898             store.on("load", this.onLoad, this);
15899             store.on("loadexception", this.onLoad, this);
15900         }
15901         
15902         if(store){
15903             this.refresh();
15904         }
15905     },
15906     /**
15907      * onbeforeLoad - masks the loading area.
15908      *
15909      */
15910     onBeforeLoad : function(store,opts)
15911     {
15912          //Roo.log('onBeforeLoad');   
15913         if (!opts.add) {
15914             this.el.update("");
15915         }
15916         this.el.mask(this.mask ? this.mask : "Loading" ); 
15917     },
15918     onLoad : function ()
15919     {
15920         this.el.unmask();
15921     },
15922     
15923
15924     /**
15925      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15926      * @param {HTMLElement} node
15927      * @return {HTMLElement} The template node
15928      */
15929     findItemFromChild : function(node){
15930         var el = this.dataName  ?
15931             this.el.child('.roo-tpl-' + this.dataName,true) :
15932             this.el.dom; 
15933         
15934         if(!node || node.parentNode == el){
15935                     return node;
15936             }
15937             var p = node.parentNode;
15938             while(p && p != el){
15939             if(p.parentNode == el){
15940                 return p;
15941             }
15942             p = p.parentNode;
15943         }
15944             return null;
15945     },
15946
15947     /** @ignore */
15948     onClick : function(e){
15949         var item = this.findItemFromChild(e.getTarget());
15950         if(item){
15951             var index = this.indexOf(item);
15952             if(this.onItemClick(item, index, e) !== false){
15953                 this.fireEvent("click", this, index, item, e);
15954             }
15955         }else{
15956             this.clearSelections();
15957         }
15958     },
15959
15960     /** @ignore */
15961     onContextMenu : function(e){
15962         var item = this.findItemFromChild(e.getTarget());
15963         if(item){
15964             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15965         }
15966     },
15967
15968     /** @ignore */
15969     onDblClick : function(e){
15970         var item = this.findItemFromChild(e.getTarget());
15971         if(item){
15972             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15973         }
15974     },
15975
15976     onItemClick : function(item, index, e)
15977     {
15978         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15979             return false;
15980         }
15981         if (this.toggleSelect) {
15982             var m = this.isSelected(item) ? 'unselect' : 'select';
15983             //Roo.log(m);
15984             var _t = this;
15985             _t[m](item, true, false);
15986             return true;
15987         }
15988         if(this.multiSelect || this.singleSelect){
15989             if(this.multiSelect && e.shiftKey && this.lastSelection){
15990                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15991             }else{
15992                 this.select(item, this.multiSelect && e.ctrlKey);
15993                 this.lastSelection = item;
15994             }
15995             
15996             if(!this.tickable){
15997                 e.preventDefault();
15998             }
15999             
16000         }
16001         return true;
16002     },
16003
16004     /**
16005      * Get the number of selected nodes.
16006      * @return {Number}
16007      */
16008     getSelectionCount : function(){
16009         return this.selections.length;
16010     },
16011
16012     /**
16013      * Get the currently selected nodes.
16014      * @return {Array} An array of HTMLElements
16015      */
16016     getSelectedNodes : function(){
16017         return this.selections;
16018     },
16019
16020     /**
16021      * Get the indexes of the selected nodes.
16022      * @return {Array}
16023      */
16024     getSelectedIndexes : function(){
16025         var indexes = [], s = this.selections;
16026         for(var i = 0, len = s.length; i < len; i++){
16027             indexes.push(s[i].nodeIndex);
16028         }
16029         return indexes;
16030     },
16031
16032     /**
16033      * Clear all selections
16034      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16035      */
16036     clearSelections : function(suppressEvent){
16037         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16038             this.cmp.elements = this.selections;
16039             this.cmp.removeClass(this.selectedClass);
16040             this.selections = [];
16041             if(!suppressEvent){
16042                 this.fireEvent("selectionchange", this, this.selections);
16043             }
16044         }
16045     },
16046
16047     /**
16048      * Returns true if the passed node is selected
16049      * @param {HTMLElement/Number} node The node or node index
16050      * @return {Boolean}
16051      */
16052     isSelected : function(node){
16053         var s = this.selections;
16054         if(s.length < 1){
16055             return false;
16056         }
16057         node = this.getNode(node);
16058         return s.indexOf(node) !== -1;
16059     },
16060
16061     /**
16062      * Selects nodes.
16063      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
16064      * @param {Boolean} keepExisting (optional) true to keep existing selections
16065      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16066      */
16067     select : function(nodeInfo, keepExisting, suppressEvent){
16068         if(nodeInfo instanceof Array){
16069             if(!keepExisting){
16070                 this.clearSelections(true);
16071             }
16072             for(var i = 0, len = nodeInfo.length; i < len; i++){
16073                 this.select(nodeInfo[i], true, true);
16074             }
16075             return;
16076         } 
16077         var node = this.getNode(nodeInfo);
16078         if(!node || this.isSelected(node)){
16079             return; // already selected.
16080         }
16081         if(!keepExisting){
16082             this.clearSelections(true);
16083         }
16084         
16085         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16086             Roo.fly(node).addClass(this.selectedClass);
16087             this.selections.push(node);
16088             if(!suppressEvent){
16089                 this.fireEvent("selectionchange", this, this.selections);
16090             }
16091         }
16092         
16093         
16094     },
16095       /**
16096      * Unselects nodes.
16097      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
16098      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16099      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16100      */
16101     unselect : function(nodeInfo, keepExisting, suppressEvent)
16102     {
16103         if(nodeInfo instanceof Array){
16104             Roo.each(this.selections, function(s) {
16105                 this.unselect(s, nodeInfo);
16106             }, this);
16107             return;
16108         }
16109         var node = this.getNode(nodeInfo);
16110         if(!node || !this.isSelected(node)){
16111             //Roo.log("not selected");
16112             return; // not selected.
16113         }
16114         // fireevent???
16115         var ns = [];
16116         Roo.each(this.selections, function(s) {
16117             if (s == node ) {
16118                 Roo.fly(node).removeClass(this.selectedClass);
16119
16120                 return;
16121             }
16122             ns.push(s);
16123         },this);
16124         
16125         this.selections= ns;
16126         this.fireEvent("selectionchange", this, this.selections);
16127     },
16128
16129     /**
16130      * Gets a template node.
16131      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16132      * @return {HTMLElement} The node or null if it wasn't found
16133      */
16134     getNode : function(nodeInfo){
16135         if(typeof nodeInfo == "string"){
16136             return document.getElementById(nodeInfo);
16137         }else if(typeof nodeInfo == "number"){
16138             return this.nodes[nodeInfo];
16139         }
16140         return nodeInfo;
16141     },
16142
16143     /**
16144      * Gets a range template nodes.
16145      * @param {Number} startIndex
16146      * @param {Number} endIndex
16147      * @return {Array} An array of nodes
16148      */
16149     getNodes : function(start, end){
16150         var ns = this.nodes;
16151         start = start || 0;
16152         end = typeof end == "undefined" ? ns.length - 1 : end;
16153         var nodes = [];
16154         if(start <= end){
16155             for(var i = start; i <= end; i++){
16156                 nodes.push(ns[i]);
16157             }
16158         } else{
16159             for(var i = start; i >= end; i--){
16160                 nodes.push(ns[i]);
16161             }
16162         }
16163         return nodes;
16164     },
16165
16166     /**
16167      * Finds the index of the passed node
16168      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16169      * @return {Number} The index of the node or -1
16170      */
16171     indexOf : function(node){
16172         node = this.getNode(node);
16173         if(typeof node.nodeIndex == "number"){
16174             return node.nodeIndex;
16175         }
16176         var ns = this.nodes;
16177         for(var i = 0, len = ns.length; i < len; i++){
16178             if(ns[i] == node){
16179                 return i;
16180             }
16181         }
16182         return -1;
16183     }
16184 });
16185 /*
16186  * - LGPL
16187  *
16188  * based on jquery fullcalendar
16189  * 
16190  */
16191
16192 Roo.bootstrap = Roo.bootstrap || {};
16193 /**
16194  * @class Roo.bootstrap.Calendar
16195  * @extends Roo.bootstrap.Component
16196  * Bootstrap Calendar class
16197  * @cfg {Boolean} loadMask (true|false) default false
16198  * @cfg {Object} header generate the user specific header of the calendar, default false
16199
16200  * @constructor
16201  * Create a new Container
16202  * @param {Object} config The config object
16203  */
16204
16205
16206
16207 Roo.bootstrap.Calendar = function(config){
16208     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16209      this.addEvents({
16210         /**
16211              * @event select
16212              * Fires when a date is selected
16213              * @param {DatePicker} this
16214              * @param {Date} date The selected date
16215              */
16216         'select': true,
16217         /**
16218              * @event monthchange
16219              * Fires when the displayed month changes 
16220              * @param {DatePicker} this
16221              * @param {Date} date The selected month
16222              */
16223         'monthchange': true,
16224         /**
16225              * @event evententer
16226              * Fires when mouse over an event
16227              * @param {Calendar} this
16228              * @param {event} Event
16229              */
16230         'evententer': true,
16231         /**
16232              * @event eventleave
16233              * Fires when the mouse leaves an
16234              * @param {Calendar} this
16235              * @param {event}
16236              */
16237         'eventleave': true,
16238         /**
16239              * @event eventclick
16240              * Fires when the mouse click an
16241              * @param {Calendar} this
16242              * @param {event}
16243              */
16244         'eventclick': true
16245         
16246     });
16247
16248 };
16249
16250 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16251     
16252      /**
16253      * @cfg {Number} startDay
16254      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16255      */
16256     startDay : 0,
16257     
16258     loadMask : false,
16259     
16260     header : false,
16261       
16262     getAutoCreate : function(){
16263         
16264         
16265         var fc_button = function(name, corner, style, content ) {
16266             return Roo.apply({},{
16267                 tag : 'span',
16268                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16269                          (corner.length ?
16270                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16271                             ''
16272                         ),
16273                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16274                 unselectable: 'on'
16275             });
16276         };
16277         
16278         var header = {};
16279         
16280         if(!this.header){
16281             header = {
16282                 tag : 'table',
16283                 cls : 'fc-header',
16284                 style : 'width:100%',
16285                 cn : [
16286                     {
16287                         tag: 'tr',
16288                         cn : [
16289                             {
16290                                 tag : 'td',
16291                                 cls : 'fc-header-left',
16292                                 cn : [
16293                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16294                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16295                                     { tag: 'span', cls: 'fc-header-space' },
16296                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16297
16298
16299                                 ]
16300                             },
16301
16302                             {
16303                                 tag : 'td',
16304                                 cls : 'fc-header-center',
16305                                 cn : [
16306                                     {
16307                                         tag: 'span',
16308                                         cls: 'fc-header-title',
16309                                         cn : {
16310                                             tag: 'H2',
16311                                             html : 'month / year'
16312                                         }
16313                                     }
16314
16315                                 ]
16316                             },
16317                             {
16318                                 tag : 'td',
16319                                 cls : 'fc-header-right',
16320                                 cn : [
16321                               /*      fc_button('month', 'left', '', 'month' ),
16322                                     fc_button('week', '', '', 'week' ),
16323                                     fc_button('day', 'right', '', 'day' )
16324                                 */    
16325
16326                                 ]
16327                             }
16328
16329                         ]
16330                     }
16331                 ]
16332             };
16333         }
16334         
16335         header = this.header;
16336         
16337        
16338         var cal_heads = function() {
16339             var ret = [];
16340             // fixme - handle this.
16341             
16342             for (var i =0; i < Date.dayNames.length; i++) {
16343                 var d = Date.dayNames[i];
16344                 ret.push({
16345                     tag: 'th',
16346                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16347                     html : d.substring(0,3)
16348                 });
16349                 
16350             }
16351             ret[0].cls += ' fc-first';
16352             ret[6].cls += ' fc-last';
16353             return ret;
16354         };
16355         var cal_cell = function(n) {
16356             return  {
16357                 tag: 'td',
16358                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16359                 cn : [
16360                     {
16361                         cn : [
16362                             {
16363                                 cls: 'fc-day-number',
16364                                 html: 'D'
16365                             },
16366                             {
16367                                 cls: 'fc-day-content',
16368                              
16369                                 cn : [
16370                                      {
16371                                         style: 'position: relative;' // height: 17px;
16372                                     }
16373                                 ]
16374                             }
16375                             
16376                             
16377                         ]
16378                     }
16379                 ]
16380                 
16381             }
16382         };
16383         var cal_rows = function() {
16384             
16385             var ret = [];
16386             for (var r = 0; r < 6; r++) {
16387                 var row= {
16388                     tag : 'tr',
16389                     cls : 'fc-week',
16390                     cn : []
16391                 };
16392                 
16393                 for (var i =0; i < Date.dayNames.length; i++) {
16394                     var d = Date.dayNames[i];
16395                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16396
16397                 }
16398                 row.cn[0].cls+=' fc-first';
16399                 row.cn[0].cn[0].style = 'min-height:90px';
16400                 row.cn[6].cls+=' fc-last';
16401                 ret.push(row);
16402                 
16403             }
16404             ret[0].cls += ' fc-first';
16405             ret[4].cls += ' fc-prev-last';
16406             ret[5].cls += ' fc-last';
16407             return ret;
16408             
16409         };
16410         
16411         var cal_table = {
16412             tag: 'table',
16413             cls: 'fc-border-separate',
16414             style : 'width:100%',
16415             cellspacing  : 0,
16416             cn : [
16417                 { 
16418                     tag: 'thead',
16419                     cn : [
16420                         { 
16421                             tag: 'tr',
16422                             cls : 'fc-first fc-last',
16423                             cn : cal_heads()
16424                         }
16425                     ]
16426                 },
16427                 { 
16428                     tag: 'tbody',
16429                     cn : cal_rows()
16430                 }
16431                   
16432             ]
16433         };
16434          
16435          var cfg = {
16436             cls : 'fc fc-ltr',
16437             cn : [
16438                 header,
16439                 {
16440                     cls : 'fc-content',
16441                     style : "position: relative;",
16442                     cn : [
16443                         {
16444                             cls : 'fc-view fc-view-month fc-grid',
16445                             style : 'position: relative',
16446                             unselectable : 'on',
16447                             cn : [
16448                                 {
16449                                     cls : 'fc-event-container',
16450                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16451                                 },
16452                                 cal_table
16453                             ]
16454                         }
16455                     ]
16456     
16457                 }
16458            ] 
16459             
16460         };
16461         
16462          
16463         
16464         return cfg;
16465     },
16466     
16467     
16468     initEvents : function()
16469     {
16470         if(!this.store){
16471             throw "can not find store for calendar";
16472         }
16473         
16474         var mark = {
16475             tag: "div",
16476             cls:"x-dlg-mask",
16477             style: "text-align:center",
16478             cn: [
16479                 {
16480                     tag: "div",
16481                     style: "background-color:white;width:50%;margin:250 auto",
16482                     cn: [
16483                         {
16484                             tag: "img",
16485                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16486                         },
16487                         {
16488                             tag: "span",
16489                             html: "Loading"
16490                         }
16491                         
16492                     ]
16493                 }
16494             ]
16495         };
16496         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16497         
16498         var size = this.el.select('.fc-content', true).first().getSize();
16499         this.maskEl.setSize(size.width, size.height);
16500         this.maskEl.enableDisplayMode("block");
16501         if(!this.loadMask){
16502             this.maskEl.hide();
16503         }
16504         
16505         this.store = Roo.factory(this.store, Roo.data);
16506         this.store.on('load', this.onLoad, this);
16507         this.store.on('beforeload', this.onBeforeLoad, this);
16508         
16509         this.resize();
16510         
16511         this.cells = this.el.select('.fc-day',true);
16512         //Roo.log(this.cells);
16513         this.textNodes = this.el.query('.fc-day-number');
16514         this.cells.addClassOnOver('fc-state-hover');
16515         
16516         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16517         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16518         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16519         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16520         
16521         this.on('monthchange', this.onMonthChange, this);
16522         
16523         this.update(new Date().clearTime());
16524     },
16525     
16526     resize : function() {
16527         var sz  = this.el.getSize();
16528         
16529         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16530         this.el.select('.fc-day-content div',true).setHeight(34);
16531     },
16532     
16533     
16534     // private
16535     showPrevMonth : function(e){
16536         this.update(this.activeDate.add("mo", -1));
16537     },
16538     showToday : function(e){
16539         this.update(new Date().clearTime());
16540     },
16541     // private
16542     showNextMonth : function(e){
16543         this.update(this.activeDate.add("mo", 1));
16544     },
16545
16546     // private
16547     showPrevYear : function(){
16548         this.update(this.activeDate.add("y", -1));
16549     },
16550
16551     // private
16552     showNextYear : function(){
16553         this.update(this.activeDate.add("y", 1));
16554     },
16555
16556     
16557    // private
16558     update : function(date)
16559     {
16560         var vd = this.activeDate;
16561         this.activeDate = date;
16562 //        if(vd && this.el){
16563 //            var t = date.getTime();
16564 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16565 //                Roo.log('using add remove');
16566 //                
16567 //                this.fireEvent('monthchange', this, date);
16568 //                
16569 //                this.cells.removeClass("fc-state-highlight");
16570 //                this.cells.each(function(c){
16571 //                   if(c.dateValue == t){
16572 //                       c.addClass("fc-state-highlight");
16573 //                       setTimeout(function(){
16574 //                            try{c.dom.firstChild.focus();}catch(e){}
16575 //                       }, 50);
16576 //                       return false;
16577 //                   }
16578 //                   return true;
16579 //                });
16580 //                return;
16581 //            }
16582 //        }
16583         
16584         var days = date.getDaysInMonth();
16585         
16586         var firstOfMonth = date.getFirstDateOfMonth();
16587         var startingPos = firstOfMonth.getDay()-this.startDay;
16588         
16589         if(startingPos < this.startDay){
16590             startingPos += 7;
16591         }
16592         
16593         var pm = date.add(Date.MONTH, -1);
16594         var prevStart = pm.getDaysInMonth()-startingPos;
16595 //        
16596         this.cells = this.el.select('.fc-day',true);
16597         this.textNodes = this.el.query('.fc-day-number');
16598         this.cells.addClassOnOver('fc-state-hover');
16599         
16600         var cells = this.cells.elements;
16601         var textEls = this.textNodes;
16602         
16603         Roo.each(cells, function(cell){
16604             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16605         });
16606         
16607         days += startingPos;
16608
16609         // convert everything to numbers so it's fast
16610         var day = 86400000;
16611         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16612         //Roo.log(d);
16613         //Roo.log(pm);
16614         //Roo.log(prevStart);
16615         
16616         var today = new Date().clearTime().getTime();
16617         var sel = date.clearTime().getTime();
16618         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16619         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16620         var ddMatch = this.disabledDatesRE;
16621         var ddText = this.disabledDatesText;
16622         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16623         var ddaysText = this.disabledDaysText;
16624         var format = this.format;
16625         
16626         var setCellClass = function(cal, cell){
16627             cell.row = 0;
16628             cell.events = [];
16629             cell.more = [];
16630             //Roo.log('set Cell Class');
16631             cell.title = "";
16632             var t = d.getTime();
16633             
16634             //Roo.log(d);
16635             
16636             cell.dateValue = t;
16637             if(t == today){
16638                 cell.className += " fc-today";
16639                 cell.className += " fc-state-highlight";
16640                 cell.title = cal.todayText;
16641             }
16642             if(t == sel){
16643                 // disable highlight in other month..
16644                 //cell.className += " fc-state-highlight";
16645                 
16646             }
16647             // disabling
16648             if(t < min) {
16649                 cell.className = " fc-state-disabled";
16650                 cell.title = cal.minText;
16651                 return;
16652             }
16653             if(t > max) {
16654                 cell.className = " fc-state-disabled";
16655                 cell.title = cal.maxText;
16656                 return;
16657             }
16658             if(ddays){
16659                 if(ddays.indexOf(d.getDay()) != -1){
16660                     cell.title = ddaysText;
16661                     cell.className = " fc-state-disabled";
16662                 }
16663             }
16664             if(ddMatch && format){
16665                 var fvalue = d.dateFormat(format);
16666                 if(ddMatch.test(fvalue)){
16667                     cell.title = ddText.replace("%0", fvalue);
16668                     cell.className = " fc-state-disabled";
16669                 }
16670             }
16671             
16672             if (!cell.initialClassName) {
16673                 cell.initialClassName = cell.dom.className;
16674             }
16675             
16676             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16677         };
16678
16679         var i = 0;
16680         
16681         for(; i < startingPos; i++) {
16682             textEls[i].innerHTML = (++prevStart);
16683             d.setDate(d.getDate()+1);
16684             
16685             cells[i].className = "fc-past fc-other-month";
16686             setCellClass(this, cells[i]);
16687         }
16688         
16689         var intDay = 0;
16690         
16691         for(; i < days; i++){
16692             intDay = i - startingPos + 1;
16693             textEls[i].innerHTML = (intDay);
16694             d.setDate(d.getDate()+1);
16695             
16696             cells[i].className = ''; // "x-date-active";
16697             setCellClass(this, cells[i]);
16698         }
16699         var extraDays = 0;
16700         
16701         for(; i < 42; i++) {
16702             textEls[i].innerHTML = (++extraDays);
16703             d.setDate(d.getDate()+1);
16704             
16705             cells[i].className = "fc-future fc-other-month";
16706             setCellClass(this, cells[i]);
16707         }
16708         
16709         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16710         
16711         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16712         
16713         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16714         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16715         
16716         if(totalRows != 6){
16717             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16718             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16719         }
16720         
16721         this.fireEvent('monthchange', this, date);
16722         
16723         
16724         /*
16725         if(!this.internalRender){
16726             var main = this.el.dom.firstChild;
16727             var w = main.offsetWidth;
16728             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16729             Roo.fly(main).setWidth(w);
16730             this.internalRender = true;
16731             // opera does not respect the auto grow header center column
16732             // then, after it gets a width opera refuses to recalculate
16733             // without a second pass
16734             if(Roo.isOpera && !this.secondPass){
16735                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16736                 this.secondPass = true;
16737                 this.update.defer(10, this, [date]);
16738             }
16739         }
16740         */
16741         
16742     },
16743     
16744     findCell : function(dt) {
16745         dt = dt.clearTime().getTime();
16746         var ret = false;
16747         this.cells.each(function(c){
16748             //Roo.log("check " +c.dateValue + '?=' + dt);
16749             if(c.dateValue == dt){
16750                 ret = c;
16751                 return false;
16752             }
16753             return true;
16754         });
16755         
16756         return ret;
16757     },
16758     
16759     findCells : function(ev) {
16760         var s = ev.start.clone().clearTime().getTime();
16761        // Roo.log(s);
16762         var e= ev.end.clone().clearTime().getTime();
16763        // Roo.log(e);
16764         var ret = [];
16765         this.cells.each(function(c){
16766              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16767             
16768             if(c.dateValue > e){
16769                 return ;
16770             }
16771             if(c.dateValue < s){
16772                 return ;
16773             }
16774             ret.push(c);
16775         });
16776         
16777         return ret;    
16778     },
16779     
16780 //    findBestRow: function(cells)
16781 //    {
16782 //        var ret = 0;
16783 //        
16784 //        for (var i =0 ; i < cells.length;i++) {
16785 //            ret  = Math.max(cells[i].rows || 0,ret);
16786 //        }
16787 //        return ret;
16788 //        
16789 //    },
16790     
16791     
16792     addItem : function(ev)
16793     {
16794         // look for vertical location slot in
16795         var cells = this.findCells(ev);
16796         
16797 //        ev.row = this.findBestRow(cells);
16798         
16799         // work out the location.
16800         
16801         var crow = false;
16802         var rows = [];
16803         for(var i =0; i < cells.length; i++) {
16804             
16805             cells[i].row = cells[0].row;
16806             
16807             if(i == 0){
16808                 cells[i].row = cells[i].row + 1;
16809             }
16810             
16811             if (!crow) {
16812                 crow = {
16813                     start : cells[i],
16814                     end :  cells[i]
16815                 };
16816                 continue;
16817             }
16818             if (crow.start.getY() == cells[i].getY()) {
16819                 // on same row.
16820                 crow.end = cells[i];
16821                 continue;
16822             }
16823             // different row.
16824             rows.push(crow);
16825             crow = {
16826                 start: cells[i],
16827                 end : cells[i]
16828             };
16829             
16830         }
16831         
16832         rows.push(crow);
16833         ev.els = [];
16834         ev.rows = rows;
16835         ev.cells = cells;
16836         
16837         cells[0].events.push(ev);
16838         
16839         this.calevents.push(ev);
16840     },
16841     
16842     clearEvents: function() {
16843         
16844         if(!this.calevents){
16845             return;
16846         }
16847         
16848         Roo.each(this.cells.elements, function(c){
16849             c.row = 0;
16850             c.events = [];
16851             c.more = [];
16852         });
16853         
16854         Roo.each(this.calevents, function(e) {
16855             Roo.each(e.els, function(el) {
16856                 el.un('mouseenter' ,this.onEventEnter, this);
16857                 el.un('mouseleave' ,this.onEventLeave, this);
16858                 el.remove();
16859             },this);
16860         },this);
16861         
16862         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16863             e.remove();
16864         });
16865         
16866     },
16867     
16868     renderEvents: function()
16869     {   
16870         var _this = this;
16871         
16872         this.cells.each(function(c) {
16873             
16874             if(c.row < 5){
16875                 return;
16876             }
16877             
16878             var ev = c.events;
16879             
16880             var r = 4;
16881             if(c.row != c.events.length){
16882                 r = 4 - (4 - (c.row - c.events.length));
16883             }
16884             
16885             c.events = ev.slice(0, r);
16886             c.more = ev.slice(r);
16887             
16888             if(c.more.length && c.more.length == 1){
16889                 c.events.push(c.more.pop());
16890             }
16891             
16892             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16893             
16894         });
16895             
16896         this.cells.each(function(c) {
16897             
16898             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16899             
16900             
16901             for (var e = 0; e < c.events.length; e++){
16902                 var ev = c.events[e];
16903                 var rows = ev.rows;
16904                 
16905                 for(var i = 0; i < rows.length; i++) {
16906                 
16907                     // how many rows should it span..
16908
16909                     var  cfg = {
16910                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16911                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16912
16913                         unselectable : "on",
16914                         cn : [
16915                             {
16916                                 cls: 'fc-event-inner',
16917                                 cn : [
16918     //                                {
16919     //                                  tag:'span',
16920     //                                  cls: 'fc-event-time',
16921     //                                  html : cells.length > 1 ? '' : ev.time
16922     //                                },
16923                                     {
16924                                       tag:'span',
16925                                       cls: 'fc-event-title',
16926                                       html : String.format('{0}', ev.title)
16927                                     }
16928
16929
16930                                 ]
16931                             },
16932                             {
16933                                 cls: 'ui-resizable-handle ui-resizable-e',
16934                                 html : '&nbsp;&nbsp;&nbsp'
16935                             }
16936
16937                         ]
16938                     };
16939
16940                     if (i == 0) {
16941                         cfg.cls += ' fc-event-start';
16942                     }
16943                     if ((i+1) == rows.length) {
16944                         cfg.cls += ' fc-event-end';
16945                     }
16946
16947                     var ctr = _this.el.select('.fc-event-container',true).first();
16948                     var cg = ctr.createChild(cfg);
16949
16950                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16951                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16952
16953                     var r = (c.more.length) ? 1 : 0;
16954                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16955                     cg.setWidth(ebox.right - sbox.x -2);
16956
16957                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16958                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16959                     cg.on('click', _this.onEventClick, _this, ev);
16960
16961                     ev.els.push(cg);
16962                     
16963                 }
16964                 
16965             }
16966             
16967             
16968             if(c.more.length){
16969                 var  cfg = {
16970                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16971                     style : 'position: absolute',
16972                     unselectable : "on",
16973                     cn : [
16974                         {
16975                             cls: 'fc-event-inner',
16976                             cn : [
16977                                 {
16978                                   tag:'span',
16979                                   cls: 'fc-event-title',
16980                                   html : 'More'
16981                                 }
16982
16983
16984                             ]
16985                         },
16986                         {
16987                             cls: 'ui-resizable-handle ui-resizable-e',
16988                             html : '&nbsp;&nbsp;&nbsp'
16989                         }
16990
16991                     ]
16992                 };
16993
16994                 var ctr = _this.el.select('.fc-event-container',true).first();
16995                 var cg = ctr.createChild(cfg);
16996
16997                 var sbox = c.select('.fc-day-content',true).first().getBox();
16998                 var ebox = c.select('.fc-day-content',true).first().getBox();
16999                 //Roo.log(cg);
17000                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17001                 cg.setWidth(ebox.right - sbox.x -2);
17002
17003                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17004                 
17005             }
17006             
17007         });
17008         
17009         
17010         
17011     },
17012     
17013     onEventEnter: function (e, el,event,d) {
17014         this.fireEvent('evententer', this, el, event);
17015     },
17016     
17017     onEventLeave: function (e, el,event,d) {
17018         this.fireEvent('eventleave', this, el, event);
17019     },
17020     
17021     onEventClick: function (e, el,event,d) {
17022         this.fireEvent('eventclick', this, el, event);
17023     },
17024     
17025     onMonthChange: function () {
17026         this.store.load();
17027     },
17028     
17029     onMoreEventClick: function(e, el, more)
17030     {
17031         var _this = this;
17032         
17033         this.calpopover.placement = 'right';
17034         this.calpopover.setTitle('More');
17035         
17036         this.calpopover.setContent('');
17037         
17038         var ctr = this.calpopover.el.select('.popover-content', true).first();
17039         
17040         Roo.each(more, function(m){
17041             var cfg = {
17042                 cls : 'fc-event-hori fc-event-draggable',
17043                 html : m.title
17044             };
17045             var cg = ctr.createChild(cfg);
17046             
17047             cg.on('click', _this.onEventClick, _this, m);
17048         });
17049         
17050         this.calpopover.show(el);
17051         
17052         
17053     },
17054     
17055     onLoad: function () 
17056     {   
17057         this.calevents = [];
17058         var cal = this;
17059         
17060         if(this.store.getCount() > 0){
17061             this.store.data.each(function(d){
17062                cal.addItem({
17063                     id : d.data.id,
17064                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17065                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17066                     time : d.data.start_time,
17067                     title : d.data.title,
17068                     description : d.data.description,
17069                     venue : d.data.venue
17070                 });
17071             });
17072         }
17073         
17074         this.renderEvents();
17075         
17076         if(this.calevents.length && this.loadMask){
17077             this.maskEl.hide();
17078         }
17079     },
17080     
17081     onBeforeLoad: function()
17082     {
17083         this.clearEvents();
17084         if(this.loadMask){
17085             this.maskEl.show();
17086         }
17087     }
17088 });
17089
17090  
17091  /*
17092  * - LGPL
17093  *
17094  * element
17095  * 
17096  */
17097
17098 /**
17099  * @class Roo.bootstrap.Popover
17100  * @extends Roo.bootstrap.Component
17101  * Bootstrap Popover class
17102  * @cfg {String} html contents of the popover   (or false to use children..)
17103  * @cfg {String} title of popover (or false to hide)
17104  * @cfg {String} placement how it is placed
17105  * @cfg {String} trigger click || hover (or false to trigger manually)
17106  * @cfg {String} over what (parent or false to trigger manually.)
17107  * @cfg {Number} delay - delay before showing
17108  
17109  * @constructor
17110  * Create a new Popover
17111  * @param {Object} config The config object
17112  */
17113
17114 Roo.bootstrap.Popover = function(config){
17115     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17116     
17117     this.addEvents({
17118         // raw events
17119          /**
17120          * @event show
17121          * After the popover show
17122          * 
17123          * @param {Roo.bootstrap.Popover} this
17124          */
17125         "show" : true,
17126         /**
17127          * @event hide
17128          * After the popover hide
17129          * 
17130          * @param {Roo.bootstrap.Popover} this
17131          */
17132         "hide" : true
17133     });
17134 };
17135
17136 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17137     
17138     title: 'Fill in a title',
17139     html: false,
17140     
17141     placement : 'right',
17142     trigger : 'hover', // hover
17143     
17144     delay : 0,
17145     
17146     over: 'parent',
17147     
17148     can_build_overlaid : false,
17149     
17150     getChildContainer : function()
17151     {
17152         return this.el.select('.popover-content',true).first();
17153     },
17154     
17155     getAutoCreate : function(){
17156          
17157         var cfg = {
17158            cls : 'popover roo-dynamic',
17159            style: 'display:block',
17160            cn : [
17161                 {
17162                     cls : 'arrow'
17163                 },
17164                 {
17165                     cls : 'popover-inner',
17166                     cn : [
17167                         {
17168                             tag: 'h3',
17169                             cls: 'popover-title',
17170                             html : this.title
17171                         },
17172                         {
17173                             cls : 'popover-content',
17174                             html : this.html
17175                         }
17176                     ]
17177                     
17178                 }
17179            ]
17180         };
17181         
17182         return cfg;
17183     },
17184     setTitle: function(str)
17185     {
17186         this.title = str;
17187         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17188     },
17189     setContent: function(str)
17190     {
17191         this.html = str;
17192         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17193     },
17194     // as it get's added to the bottom of the page.
17195     onRender : function(ct, position)
17196     {
17197         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17198         if(!this.el){
17199             var cfg = Roo.apply({},  this.getAutoCreate());
17200             cfg.id = Roo.id();
17201             
17202             if (this.cls) {
17203                 cfg.cls += ' ' + this.cls;
17204             }
17205             if (this.style) {
17206                 cfg.style = this.style;
17207             }
17208             //Roo.log("adding to ");
17209             this.el = Roo.get(document.body).createChild(cfg, position);
17210 //            Roo.log(this.el);
17211         }
17212         this.initEvents();
17213     },
17214     
17215     initEvents : function()
17216     {
17217         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17218         this.el.enableDisplayMode('block');
17219         this.el.hide();
17220         if (this.over === false) {
17221             return; 
17222         }
17223         if (this.triggers === false) {
17224             return;
17225         }
17226         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17227         var triggers = this.trigger ? this.trigger.split(' ') : [];
17228         Roo.each(triggers, function(trigger) {
17229         
17230             if (trigger == 'click') {
17231                 on_el.on('click', this.toggle, this);
17232             } else if (trigger != 'manual') {
17233                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17234                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17235       
17236                 on_el.on(eventIn  ,this.enter, this);
17237                 on_el.on(eventOut, this.leave, this);
17238             }
17239         }, this);
17240         
17241     },
17242     
17243     
17244     // private
17245     timeout : null,
17246     hoverState : null,
17247     
17248     toggle : function () {
17249         this.hoverState == 'in' ? this.leave() : this.enter();
17250     },
17251     
17252     enter : function () {
17253         
17254         clearTimeout(this.timeout);
17255     
17256         this.hoverState = 'in';
17257     
17258         if (!this.delay || !this.delay.show) {
17259             this.show();
17260             return;
17261         }
17262         var _t = this;
17263         this.timeout = setTimeout(function () {
17264             if (_t.hoverState == 'in') {
17265                 _t.show();
17266             }
17267         }, this.delay.show)
17268     },
17269     
17270     leave : function() {
17271         clearTimeout(this.timeout);
17272     
17273         this.hoverState = 'out';
17274     
17275         if (!this.delay || !this.delay.hide) {
17276             this.hide();
17277             return;
17278         }
17279         var _t = this;
17280         this.timeout = setTimeout(function () {
17281             if (_t.hoverState == 'out') {
17282                 _t.hide();
17283             }
17284         }, this.delay.hide)
17285     },
17286     
17287     show : function (on_el)
17288     {
17289         if (!on_el) {
17290             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17291         }
17292         
17293         // set content.
17294         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17295         if (this.html !== false) {
17296             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17297         }
17298         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17299         if (!this.title.length) {
17300             this.el.select('.popover-title',true).hide();
17301         }
17302         
17303         var placement = typeof this.placement == 'function' ?
17304             this.placement.call(this, this.el, on_el) :
17305             this.placement;
17306             
17307         var autoToken = /\s?auto?\s?/i;
17308         var autoPlace = autoToken.test(placement);
17309         if (autoPlace) {
17310             placement = placement.replace(autoToken, '') || 'top';
17311         }
17312         
17313         //this.el.detach()
17314         //this.el.setXY([0,0]);
17315         this.el.show();
17316         this.el.dom.style.display='block';
17317         this.el.addClass(placement);
17318         
17319         //this.el.appendTo(on_el);
17320         
17321         var p = this.getPosition();
17322         var box = this.el.getBox();
17323         
17324         if (autoPlace) {
17325             // fixme..
17326         }
17327         var align = Roo.bootstrap.Popover.alignment[placement];
17328         this.el.alignTo(on_el, align[0],align[1]);
17329         //var arrow = this.el.select('.arrow',true).first();
17330         //arrow.set(align[2], 
17331         
17332         this.el.addClass('in');
17333         
17334         
17335         if (this.el.hasClass('fade')) {
17336             // fade it?
17337         }
17338         
17339         this.hoverState = 'in';
17340         
17341         this.fireEvent('show', this);
17342         
17343     },
17344     hide : function()
17345     {
17346         this.el.setXY([0,0]);
17347         this.el.removeClass('in');
17348         this.el.hide();
17349         this.hoverState = null;
17350         
17351         this.fireEvent('hide', this);
17352     }
17353     
17354 });
17355
17356 Roo.bootstrap.Popover.alignment = {
17357     'left' : ['r-l', [-10,0], 'right'],
17358     'right' : ['l-r', [10,0], 'left'],
17359     'bottom' : ['t-b', [0,10], 'top'],
17360     'top' : [ 'b-t', [0,-10], 'bottom']
17361 };
17362
17363  /*
17364  * - LGPL
17365  *
17366  * Progress
17367  * 
17368  */
17369
17370 /**
17371  * @class Roo.bootstrap.Progress
17372  * @extends Roo.bootstrap.Component
17373  * Bootstrap Progress class
17374  * @cfg {Boolean} striped striped of the progress bar
17375  * @cfg {Boolean} active animated of the progress bar
17376  * 
17377  * 
17378  * @constructor
17379  * Create a new Progress
17380  * @param {Object} config The config object
17381  */
17382
17383 Roo.bootstrap.Progress = function(config){
17384     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17385 };
17386
17387 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17388     
17389     striped : false,
17390     active: false,
17391     
17392     getAutoCreate : function(){
17393         var cfg = {
17394             tag: 'div',
17395             cls: 'progress'
17396         };
17397         
17398         
17399         if(this.striped){
17400             cfg.cls += ' progress-striped';
17401         }
17402       
17403         if(this.active){
17404             cfg.cls += ' active';
17405         }
17406         
17407         
17408         return cfg;
17409     }
17410    
17411 });
17412
17413  
17414
17415  /*
17416  * - LGPL
17417  *
17418  * ProgressBar
17419  * 
17420  */
17421
17422 /**
17423  * @class Roo.bootstrap.ProgressBar
17424  * @extends Roo.bootstrap.Component
17425  * Bootstrap ProgressBar class
17426  * @cfg {Number} aria_valuenow aria-value now
17427  * @cfg {Number} aria_valuemin aria-value min
17428  * @cfg {Number} aria_valuemax aria-value max
17429  * @cfg {String} label label for the progress bar
17430  * @cfg {String} panel (success | info | warning | danger )
17431  * @cfg {String} role role of the progress bar
17432  * @cfg {String} sr_only text
17433  * 
17434  * 
17435  * @constructor
17436  * Create a new ProgressBar
17437  * @param {Object} config The config object
17438  */
17439
17440 Roo.bootstrap.ProgressBar = function(config){
17441     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17442 };
17443
17444 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17445     
17446     aria_valuenow : 0,
17447     aria_valuemin : 0,
17448     aria_valuemax : 100,
17449     label : false,
17450     panel : false,
17451     role : false,
17452     sr_only: false,
17453     
17454     getAutoCreate : function()
17455     {
17456         
17457         var cfg = {
17458             tag: 'div',
17459             cls: 'progress-bar',
17460             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17461         };
17462         
17463         if(this.sr_only){
17464             cfg.cn = {
17465                 tag: 'span',
17466                 cls: 'sr-only',
17467                 html: this.sr_only
17468             }
17469         }
17470         
17471         if(this.role){
17472             cfg.role = this.role;
17473         }
17474         
17475         if(this.aria_valuenow){
17476             cfg['aria-valuenow'] = this.aria_valuenow;
17477         }
17478         
17479         if(this.aria_valuemin){
17480             cfg['aria-valuemin'] = this.aria_valuemin;
17481         }
17482         
17483         if(this.aria_valuemax){
17484             cfg['aria-valuemax'] = this.aria_valuemax;
17485         }
17486         
17487         if(this.label && !this.sr_only){
17488             cfg.html = this.label;
17489         }
17490         
17491         if(this.panel){
17492             cfg.cls += ' progress-bar-' + this.panel;
17493         }
17494         
17495         return cfg;
17496     },
17497     
17498     update : function(aria_valuenow)
17499     {
17500         this.aria_valuenow = aria_valuenow;
17501         
17502         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17503     }
17504    
17505 });
17506
17507  
17508
17509  /*
17510  * - LGPL
17511  *
17512  * column
17513  * 
17514  */
17515
17516 /**
17517  * @class Roo.bootstrap.TabGroup
17518  * @extends Roo.bootstrap.Column
17519  * Bootstrap Column class
17520  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17521  * @cfg {Boolean} carousel true to make the group behave like a carousel
17522  * @cfg {Boolean} bullets show bullets for the panels
17523  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17524  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17525  * @cfg {Boolean} showarrow (true|false) show arrow default true
17526  * 
17527  * @constructor
17528  * Create a new TabGroup
17529  * @param {Object} config The config object
17530  */
17531
17532 Roo.bootstrap.TabGroup = function(config){
17533     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17534     if (!this.navId) {
17535         this.navId = Roo.id();
17536     }
17537     this.tabs = [];
17538     Roo.bootstrap.TabGroup.register(this);
17539     
17540 };
17541
17542 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17543     
17544     carousel : false,
17545     transition : false,
17546     bullets : 0,
17547     timer : 0,
17548     autoslide : false,
17549     slideFn : false,
17550     slideOnTouch : false,
17551     showarrow : true,
17552     
17553     getAutoCreate : function()
17554     {
17555         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17556         
17557         cfg.cls += ' tab-content';
17558         
17559         if (this.carousel) {
17560             cfg.cls += ' carousel slide';
17561             
17562             cfg.cn = [{
17563                cls : 'carousel-inner',
17564                cn : []
17565             }];
17566         
17567             if(this.bullets  && !Roo.isTouch){
17568                 
17569                 var bullets = {
17570                     cls : 'carousel-bullets',
17571                     cn : []
17572                 };
17573                
17574                 if(this.bullets_cls){
17575                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17576                 }
17577                 
17578                 bullets.cn.push({
17579                     cls : 'clear'
17580                 });
17581                 
17582                 cfg.cn[0].cn.push(bullets);
17583             }
17584             
17585             if(this.showarrow){
17586                 cfg.cn[0].cn.push({
17587                     tag : 'div',
17588                     class : 'carousel-arrow',
17589                     cn : [
17590                         {
17591                             tag : 'div',
17592                             class : 'carousel-prev',
17593                             cn : [
17594                                 {
17595                                     tag : 'i',
17596                                     class : 'fa fa-chevron-left'
17597                                 }
17598                             ]
17599                         },
17600                         {
17601                             tag : 'div',
17602                             class : 'carousel-next',
17603                             cn : [
17604                                 {
17605                                     tag : 'i',
17606                                     class : 'fa fa-chevron-right'
17607                                 }
17608                             ]
17609                         }
17610                     ]
17611                 });
17612             }
17613             
17614         }
17615         
17616         return cfg;
17617     },
17618     
17619     initEvents:  function()
17620     {
17621 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17622 //            this.el.on("touchstart", this.onTouchStart, this);
17623 //        }
17624         
17625         if(this.autoslide){
17626             var _this = this;
17627             
17628             this.slideFn = window.setInterval(function() {
17629                 _this.showPanelNext();
17630             }, this.timer);
17631         }
17632         
17633         if(this.showarrow){
17634             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17635             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17636         }
17637         
17638         
17639     },
17640     
17641 //    onTouchStart : function(e, el, o)
17642 //    {
17643 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17644 //            return;
17645 //        }
17646 //        
17647 //        this.showPanelNext();
17648 //    },
17649     
17650     
17651     getChildContainer : function()
17652     {
17653         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17654     },
17655     
17656     /**
17657     * register a Navigation item
17658     * @param {Roo.bootstrap.NavItem} the navitem to add
17659     */
17660     register : function(item)
17661     {
17662         this.tabs.push( item);
17663         item.navId = this.navId; // not really needed..
17664         this.addBullet();
17665     
17666     },
17667     
17668     getActivePanel : function()
17669     {
17670         var r = false;
17671         Roo.each(this.tabs, function(t) {
17672             if (t.active) {
17673                 r = t;
17674                 return false;
17675             }
17676             return null;
17677         });
17678         return r;
17679         
17680     },
17681     getPanelByName : function(n)
17682     {
17683         var r = false;
17684         Roo.each(this.tabs, function(t) {
17685             if (t.tabId == n) {
17686                 r = t;
17687                 return false;
17688             }
17689             return null;
17690         });
17691         return r;
17692     },
17693     indexOfPanel : function(p)
17694     {
17695         var r = false;
17696         Roo.each(this.tabs, function(t,i) {
17697             if (t.tabId == p.tabId) {
17698                 r = i;
17699                 return false;
17700             }
17701             return null;
17702         });
17703         return r;
17704     },
17705     /**
17706      * show a specific panel
17707      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17708      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17709      */
17710     showPanel : function (pan)
17711     {
17712         if(this.transition || typeof(pan) == 'undefined'){
17713             Roo.log("waiting for the transitionend");
17714             return;
17715         }
17716         
17717         if (typeof(pan) == 'number') {
17718             pan = this.tabs[pan];
17719         }
17720         
17721         if (typeof(pan) == 'string') {
17722             pan = this.getPanelByName(pan);
17723         }
17724         
17725         var cur = this.getActivePanel();
17726         
17727         if(!pan || !cur){
17728             Roo.log('pan or acitve pan is undefined');
17729             return false;
17730         }
17731         
17732         if (pan.tabId == this.getActivePanel().tabId) {
17733             return true;
17734         }
17735         
17736         if (false === cur.fireEvent('beforedeactivate')) {
17737             return false;
17738         }
17739         
17740         if(this.bullets > 0 && !Roo.isTouch){
17741             this.setActiveBullet(this.indexOfPanel(pan));
17742         }
17743         
17744         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17745             
17746             this.transition = true;
17747             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17748             var lr = dir == 'next' ? 'left' : 'right';
17749             pan.el.addClass(dir); // or prev
17750             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17751             cur.el.addClass(lr); // or right
17752             pan.el.addClass(lr);
17753             
17754             var _this = this;
17755             cur.el.on('transitionend', function() {
17756                 Roo.log("trans end?");
17757                 
17758                 pan.el.removeClass([lr,dir]);
17759                 pan.setActive(true);
17760                 
17761                 cur.el.removeClass([lr]);
17762                 cur.setActive(false);
17763                 
17764                 _this.transition = false;
17765                 
17766             }, this, { single:  true } );
17767             
17768             return true;
17769         }
17770         
17771         cur.setActive(false);
17772         pan.setActive(true);
17773         
17774         return true;
17775         
17776     },
17777     showPanelNext : function()
17778     {
17779         var i = this.indexOfPanel(this.getActivePanel());
17780         
17781         if (i >= this.tabs.length - 1 && !this.autoslide) {
17782             return;
17783         }
17784         
17785         if (i >= this.tabs.length - 1 && this.autoslide) {
17786             i = -1;
17787         }
17788         
17789         this.showPanel(this.tabs[i+1]);
17790     },
17791     
17792     showPanelPrev : function()
17793     {
17794         var i = this.indexOfPanel(this.getActivePanel());
17795         
17796         if (i  < 1 && !this.autoslide) {
17797             return;
17798         }
17799         
17800         if (i < 1 && this.autoslide) {
17801             i = this.tabs.length;
17802         }
17803         
17804         this.showPanel(this.tabs[i-1]);
17805     },
17806     
17807     
17808     addBullet: function()
17809     {
17810         if(!this.bullets || Roo.isTouch){
17811             return;
17812         }
17813         var ctr = this.el.select('.carousel-bullets',true).first();
17814         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17815         var bullet = ctr.createChild({
17816             cls : 'bullet bullet-' + i
17817         },ctr.dom.lastChild);
17818         
17819         
17820         var _this = this;
17821         
17822         bullet.on('click', (function(e, el, o, ii, t){
17823
17824             e.preventDefault();
17825
17826             this.showPanel(ii);
17827
17828             if(this.autoslide && this.slideFn){
17829                 clearInterval(this.slideFn);
17830                 this.slideFn = window.setInterval(function() {
17831                     _this.showPanelNext();
17832                 }, this.timer);
17833             }
17834
17835         }).createDelegate(this, [i, bullet], true));
17836                 
17837         
17838     },
17839      
17840     setActiveBullet : function(i)
17841     {
17842         if(Roo.isTouch){
17843             return;
17844         }
17845         
17846         Roo.each(this.el.select('.bullet', true).elements, function(el){
17847             el.removeClass('selected');
17848         });
17849
17850         var bullet = this.el.select('.bullet-' + i, true).first();
17851         
17852         if(!bullet){
17853             return;
17854         }
17855         
17856         bullet.addClass('selected');
17857     }
17858     
17859     
17860   
17861 });
17862
17863  
17864
17865  
17866  
17867 Roo.apply(Roo.bootstrap.TabGroup, {
17868     
17869     groups: {},
17870      /**
17871     * register a Navigation Group
17872     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17873     */
17874     register : function(navgrp)
17875     {
17876         this.groups[navgrp.navId] = navgrp;
17877         
17878     },
17879     /**
17880     * fetch a Navigation Group based on the navigation ID
17881     * if one does not exist , it will get created.
17882     * @param {string} the navgroup to add
17883     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17884     */
17885     get: function(navId) {
17886         if (typeof(this.groups[navId]) == 'undefined') {
17887             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17888         }
17889         return this.groups[navId] ;
17890     }
17891     
17892     
17893     
17894 });
17895
17896  /*
17897  * - LGPL
17898  *
17899  * TabPanel
17900  * 
17901  */
17902
17903 /**
17904  * @class Roo.bootstrap.TabPanel
17905  * @extends Roo.bootstrap.Component
17906  * Bootstrap TabPanel class
17907  * @cfg {Boolean} active panel active
17908  * @cfg {String} html panel content
17909  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17910  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17911  * @cfg {String} href click to link..
17912  * 
17913  * 
17914  * @constructor
17915  * Create a new TabPanel
17916  * @param {Object} config The config object
17917  */
17918
17919 Roo.bootstrap.TabPanel = function(config){
17920     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17921     this.addEvents({
17922         /**
17923              * @event changed
17924              * Fires when the active status changes
17925              * @param {Roo.bootstrap.TabPanel} this
17926              * @param {Boolean} state the new state
17927             
17928          */
17929         'changed': true,
17930         /**
17931              * @event beforedeactivate
17932              * Fires before a tab is de-activated - can be used to do validation on a form.
17933              * @param {Roo.bootstrap.TabPanel} this
17934              * @return {Boolean} false if there is an error
17935             
17936          */
17937         'beforedeactivate': true
17938      });
17939     
17940     this.tabId = this.tabId || Roo.id();
17941   
17942 };
17943
17944 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17945     
17946     active: false,
17947     html: false,
17948     tabId: false,
17949     navId : false,
17950     href : '',
17951     
17952     getAutoCreate : function(){
17953         var cfg = {
17954             tag: 'div',
17955             // item is needed for carousel - not sure if it has any effect otherwise
17956             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17957             html: this.html || ''
17958         };
17959         
17960         if(this.active){
17961             cfg.cls += ' active';
17962         }
17963         
17964         if(this.tabId){
17965             cfg.tabId = this.tabId;
17966         }
17967         
17968         
17969         return cfg;
17970     },
17971     
17972     initEvents:  function()
17973     {
17974         var p = this.parent();
17975         
17976         this.navId = this.navId || p.navId;
17977         
17978         if (typeof(this.navId) != 'undefined') {
17979             // not really needed.. but just in case.. parent should be a NavGroup.
17980             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17981             
17982             tg.register(this);
17983             
17984             var i = tg.tabs.length - 1;
17985             
17986             if(this.active && tg.bullets > 0 && i < tg.bullets){
17987                 tg.setActiveBullet(i);
17988             }
17989         }
17990         
17991         this.el.on('click', this.onClick, this);
17992         
17993         if(Roo.isTouch){
17994             this.el.on("touchstart", this.onTouchStart, this);
17995             this.el.on("touchmove", this.onTouchMove, this);
17996             this.el.on("touchend", this.onTouchEnd, this);
17997         }
17998         
17999     },
18000     
18001     onRender : function(ct, position)
18002     {
18003         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18004     },
18005     
18006     setActive : function(state)
18007     {
18008         Roo.log("panel - set active " + this.tabId + "=" + state);
18009         
18010         this.active = state;
18011         if (!state) {
18012             this.el.removeClass('active');
18013             
18014         } else  if (!this.el.hasClass('active')) {
18015             this.el.addClass('active');
18016         }
18017         
18018         this.fireEvent('changed', this, state);
18019     },
18020     
18021     onClick : function(e)
18022     {
18023         e.preventDefault();
18024         
18025         if(!this.href.length){
18026             return;
18027         }
18028         
18029         window.location.href = this.href;
18030     },
18031     
18032     startX : 0,
18033     startY : 0,
18034     endX : 0,
18035     endY : 0,
18036     swiping : false,
18037     
18038     onTouchStart : function(e)
18039     {
18040         this.swiping = false;
18041         
18042         this.startX = e.browserEvent.touches[0].clientX;
18043         this.startY = e.browserEvent.touches[0].clientY;
18044     },
18045     
18046     onTouchMove : function(e)
18047     {
18048         this.swiping = true;
18049         
18050         this.endX = e.browserEvent.touches[0].clientX;
18051         this.endY = e.browserEvent.touches[0].clientY;
18052     },
18053     
18054     onTouchEnd : function(e)
18055     {
18056         if(!this.swiping){
18057             this.onClick(e);
18058             return;
18059         }
18060         
18061         var tabGroup = this.parent();
18062         
18063         if(this.endX > this.startX){ // swiping right
18064             tabGroup.showPanelPrev();
18065             return;
18066         }
18067         
18068         if(this.startX > this.endX){ // swiping left
18069             tabGroup.showPanelNext();
18070             return;
18071         }
18072     }
18073     
18074     
18075 });
18076  
18077
18078  
18079
18080  /*
18081  * - LGPL
18082  *
18083  * DateField
18084  * 
18085  */
18086
18087 /**
18088  * @class Roo.bootstrap.DateField
18089  * @extends Roo.bootstrap.Input
18090  * Bootstrap DateField class
18091  * @cfg {Number} weekStart default 0
18092  * @cfg {String} viewMode default empty, (months|years)
18093  * @cfg {String} minViewMode default empty, (months|years)
18094  * @cfg {Number} startDate default -Infinity
18095  * @cfg {Number} endDate default Infinity
18096  * @cfg {Boolean} todayHighlight default false
18097  * @cfg {Boolean} todayBtn default false
18098  * @cfg {Boolean} calendarWeeks default false
18099  * @cfg {Object} daysOfWeekDisabled default empty
18100  * @cfg {Boolean} singleMode default false (true | false)
18101  * 
18102  * @cfg {Boolean} keyboardNavigation default true
18103  * @cfg {String} language default en
18104  * 
18105  * @constructor
18106  * Create a new DateField
18107  * @param {Object} config The config object
18108  */
18109
18110 Roo.bootstrap.DateField = function(config){
18111     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18112      this.addEvents({
18113             /**
18114              * @event show
18115              * Fires when this field show.
18116              * @param {Roo.bootstrap.DateField} this
18117              * @param {Mixed} date The date value
18118              */
18119             show : true,
18120             /**
18121              * @event show
18122              * Fires when this field hide.
18123              * @param {Roo.bootstrap.DateField} this
18124              * @param {Mixed} date The date value
18125              */
18126             hide : true,
18127             /**
18128              * @event select
18129              * Fires when select a date.
18130              * @param {Roo.bootstrap.DateField} this
18131              * @param {Mixed} date The date value
18132              */
18133             select : true,
18134             /**
18135              * @event beforeselect
18136              * Fires when before select a date.
18137              * @param {Roo.bootstrap.DateField} this
18138              * @param {Mixed} date The date value
18139              */
18140             beforeselect : true
18141         });
18142 };
18143
18144 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18145     
18146     /**
18147      * @cfg {String} format
18148      * The default date format string which can be overriden for localization support.  The format must be
18149      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18150      */
18151     format : "m/d/y",
18152     /**
18153      * @cfg {String} altFormats
18154      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18155      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18156      */
18157     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18158     
18159     weekStart : 0,
18160     
18161     viewMode : '',
18162     
18163     minViewMode : '',
18164     
18165     todayHighlight : false,
18166     
18167     todayBtn: false,
18168     
18169     language: 'en',
18170     
18171     keyboardNavigation: true,
18172     
18173     calendarWeeks: false,
18174     
18175     startDate: -Infinity,
18176     
18177     endDate: Infinity,
18178     
18179     daysOfWeekDisabled: [],
18180     
18181     _events: [],
18182     
18183     singleMode : false,
18184     
18185     UTCDate: function()
18186     {
18187         return new Date(Date.UTC.apply(Date, arguments));
18188     },
18189     
18190     UTCToday: function()
18191     {
18192         var today = new Date();
18193         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18194     },
18195     
18196     getDate: function() {
18197             var d = this.getUTCDate();
18198             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18199     },
18200     
18201     getUTCDate: function() {
18202             return this.date;
18203     },
18204     
18205     setDate: function(d) {
18206             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18207     },
18208     
18209     setUTCDate: function(d) {
18210             this.date = d;
18211             this.setValue(this.formatDate(this.date));
18212     },
18213         
18214     onRender: function(ct, position)
18215     {
18216         
18217         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18218         
18219         this.language = this.language || 'en';
18220         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18221         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18222         
18223         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18224         this.format = this.format || 'm/d/y';
18225         this.isInline = false;
18226         this.isInput = true;
18227         this.component = this.el.select('.add-on', true).first() || false;
18228         this.component = (this.component && this.component.length === 0) ? false : this.component;
18229         this.hasInput = this.component && this.inputEl().length;
18230         
18231         if (typeof(this.minViewMode === 'string')) {
18232             switch (this.minViewMode) {
18233                 case 'months':
18234                     this.minViewMode = 1;
18235                     break;
18236                 case 'years':
18237                     this.minViewMode = 2;
18238                     break;
18239                 default:
18240                     this.minViewMode = 0;
18241                     break;
18242             }
18243         }
18244         
18245         if (typeof(this.viewMode === 'string')) {
18246             switch (this.viewMode) {
18247                 case 'months':
18248                     this.viewMode = 1;
18249                     break;
18250                 case 'years':
18251                     this.viewMode = 2;
18252                     break;
18253                 default:
18254                     this.viewMode = 0;
18255                     break;
18256             }
18257         }
18258                 
18259         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18260         
18261 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18262         
18263         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18264         
18265         this.picker().on('mousedown', this.onMousedown, this);
18266         this.picker().on('click', this.onClick, this);
18267         
18268         this.picker().addClass('datepicker-dropdown');
18269         
18270         this.startViewMode = this.viewMode;
18271         
18272         if(this.singleMode){
18273             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18274                 v.setVisibilityMode(Roo.Element.DISPLAY);
18275                 v.hide();
18276             });
18277             
18278             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18279                 v.setStyle('width', '189px');
18280             });
18281         }
18282         
18283         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18284             if(!this.calendarWeeks){
18285                 v.remove();
18286                 return;
18287             }
18288             
18289             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18290             v.attr('colspan', function(i, val){
18291                 return parseInt(val) + 1;
18292             });
18293         });
18294                         
18295         
18296         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18297         
18298         this.setStartDate(this.startDate);
18299         this.setEndDate(this.endDate);
18300         
18301         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18302         
18303         this.fillDow();
18304         this.fillMonths();
18305         this.update();
18306         this.showMode();
18307         
18308         if(this.isInline) {
18309             this.show();
18310         }
18311     },
18312     
18313     picker : function()
18314     {
18315         return this.pickerEl;
18316 //        return this.el.select('.datepicker', true).first();
18317     },
18318     
18319     fillDow: function()
18320     {
18321         var dowCnt = this.weekStart;
18322         
18323         var dow = {
18324             tag: 'tr',
18325             cn: [
18326                 
18327             ]
18328         };
18329         
18330         if(this.calendarWeeks){
18331             dow.cn.push({
18332                 tag: 'th',
18333                 cls: 'cw',
18334                 html: '&nbsp;'
18335             })
18336         }
18337         
18338         while (dowCnt < this.weekStart + 7) {
18339             dow.cn.push({
18340                 tag: 'th',
18341                 cls: 'dow',
18342                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18343             });
18344         }
18345         
18346         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18347     },
18348     
18349     fillMonths: function()
18350     {    
18351         var i = 0;
18352         var months = this.picker().select('>.datepicker-months td', true).first();
18353         
18354         months.dom.innerHTML = '';
18355         
18356         while (i < 12) {
18357             var month = {
18358                 tag: 'span',
18359                 cls: 'month',
18360                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18361             };
18362             
18363             months.createChild(month);
18364         }
18365         
18366     },
18367     
18368     update: function()
18369     {
18370         this.date = (typeof(this.date) === 'undefined' || ((typeof(this.date) === 'string') && !this.date.length)) ? this.UTCToday() : (typeof(this.date) === 'string') ? this.parseDate(this.date) : this.date;
18371         
18372         if (this.date < this.startDate) {
18373             this.viewDate = new Date(this.startDate);
18374         } else if (this.date > this.endDate) {
18375             this.viewDate = new Date(this.endDate);
18376         } else {
18377             this.viewDate = new Date(this.date);
18378         }
18379         
18380         this.fill();
18381     },
18382     
18383     fill: function() 
18384     {
18385         var d = new Date(this.viewDate),
18386                 year = d.getUTCFullYear(),
18387                 month = d.getUTCMonth(),
18388                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18389                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18390                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18391                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18392                 currentDate = this.date && this.date.valueOf(),
18393                 today = this.UTCToday();
18394         
18395         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18396         
18397 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18398         
18399 //        this.picker.select('>tfoot th.today').
18400 //                                              .text(dates[this.language].today)
18401 //                                              .toggle(this.todayBtn !== false);
18402     
18403         this.updateNavArrows();
18404         this.fillMonths();
18405                                                 
18406         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18407         
18408         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18409          
18410         prevMonth.setUTCDate(day);
18411         
18412         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18413         
18414         var nextMonth = new Date(prevMonth);
18415         
18416         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18417         
18418         nextMonth = nextMonth.valueOf();
18419         
18420         var fillMonths = false;
18421         
18422         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18423         
18424         while(prevMonth.valueOf() < nextMonth) {
18425             var clsName = '';
18426             
18427             if (prevMonth.getUTCDay() === this.weekStart) {
18428                 if(fillMonths){
18429                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18430                 }
18431                     
18432                 fillMonths = {
18433                     tag: 'tr',
18434                     cn: []
18435                 };
18436                 
18437                 if(this.calendarWeeks){
18438                     // ISO 8601: First week contains first thursday.
18439                     // ISO also states week starts on Monday, but we can be more abstract here.
18440                     var
18441                     // Start of current week: based on weekstart/current date
18442                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18443                     // Thursday of this week
18444                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18445                     // First Thursday of year, year from thursday
18446                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18447                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18448                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18449                     
18450                     fillMonths.cn.push({
18451                         tag: 'td',
18452                         cls: 'cw',
18453                         html: calWeek
18454                     });
18455                 }
18456             }
18457             
18458             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18459                 clsName += ' old';
18460             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18461                 clsName += ' new';
18462             }
18463             if (this.todayHighlight &&
18464                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18465                 prevMonth.getUTCMonth() == today.getMonth() &&
18466                 prevMonth.getUTCDate() == today.getDate()) {
18467                 clsName += ' today';
18468             }
18469             
18470             if (currentDate && prevMonth.valueOf() === currentDate) {
18471                 clsName += ' active';
18472             }
18473             
18474             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18475                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18476                     clsName += ' disabled';
18477             }
18478             
18479             fillMonths.cn.push({
18480                 tag: 'td',
18481                 cls: 'day ' + clsName,
18482                 html: prevMonth.getDate()
18483             });
18484             
18485             prevMonth.setDate(prevMonth.getDate()+1);
18486         }
18487           
18488         var currentYear = this.date && this.date.getUTCFullYear();
18489         var currentMonth = this.date && this.date.getUTCMonth();
18490         
18491         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18492         
18493         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18494             v.removeClass('active');
18495             
18496             if(currentYear === year && k === currentMonth){
18497                 v.addClass('active');
18498             }
18499             
18500             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18501                 v.addClass('disabled');
18502             }
18503             
18504         });
18505         
18506         
18507         year = parseInt(year/10, 10) * 10;
18508         
18509         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18510         
18511         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18512         
18513         year -= 1;
18514         for (var i = -1; i < 11; i++) {
18515             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18516                 tag: 'span',
18517                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18518                 html: year
18519             });
18520             
18521             year += 1;
18522         }
18523     },
18524     
18525     showMode: function(dir) 
18526     {
18527         if (dir) {
18528             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18529         }
18530         
18531         Roo.each(this.picker().select('>div',true).elements, function(v){
18532             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18533             v.hide();
18534         });
18535         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18536     },
18537     
18538     place: function()
18539     {
18540         if(this.isInline) {
18541             return;
18542         }
18543         
18544         this.picker().removeClass(['bottom', 'top']);
18545         
18546         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18547             /*
18548              * place to the top of element!
18549              *
18550              */
18551             
18552             this.picker().addClass('top');
18553             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18554             
18555             return;
18556         }
18557         
18558         this.picker().addClass('bottom');
18559         
18560         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18561     },
18562     
18563     parseDate : function(value)
18564     {
18565         if(!value || value instanceof Date){
18566             return value;
18567         }
18568         var v = Date.parseDate(value, this.format);
18569         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18570             v = Date.parseDate(value, 'Y-m-d');
18571         }
18572         if(!v && this.altFormats){
18573             if(!this.altFormatsArray){
18574                 this.altFormatsArray = this.altFormats.split("|");
18575             }
18576             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18577                 v = Date.parseDate(value, this.altFormatsArray[i]);
18578             }
18579         }
18580         return v;
18581     },
18582     
18583     formatDate : function(date, fmt)
18584     {   
18585         return (!date || !(date instanceof Date)) ?
18586         date : date.dateFormat(fmt || this.format);
18587     },
18588     
18589     onFocus : function()
18590     {
18591         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18592         this.show();
18593     },
18594     
18595     onBlur : function()
18596     {
18597         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18598         
18599         var d = this.inputEl().getValue();
18600         
18601         this.setValue(d);
18602                 
18603         this.hide();
18604     },
18605     
18606     show : function()
18607     {
18608         this.picker().show();
18609         this.update();
18610         this.place();
18611         
18612         this.fireEvent('show', this, this.date);
18613     },
18614     
18615     hide : function()
18616     {
18617         if(this.isInline) {
18618             return;
18619         }
18620         this.picker().hide();
18621         this.viewMode = this.startViewMode;
18622         this.showMode();
18623         
18624         this.fireEvent('hide', this, this.date);
18625         
18626     },
18627     
18628     onMousedown: function(e)
18629     {
18630         e.stopPropagation();
18631         e.preventDefault();
18632     },
18633     
18634     keyup: function(e)
18635     {
18636         Roo.bootstrap.DateField.superclass.keyup.call(this);
18637         this.update();
18638     },
18639
18640     setValue: function(v)
18641     {
18642         if(this.fireEvent('beforeselect', this, v) !== false){
18643             var d = new Date(this.parseDate(v) ).clearTime();
18644         
18645             if(isNaN(d.getTime())){
18646                 this.date = this.viewDate = '';
18647                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18648                 return;
18649             }
18650
18651             v = this.formatDate(d);
18652
18653             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18654
18655             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18656
18657             this.update();
18658
18659             this.fireEvent('select', this, this.date);
18660         }
18661     },
18662     
18663     getValue: function()
18664     {
18665         return this.formatDate(this.date);
18666     },
18667     
18668     fireKey: function(e)
18669     {
18670         if (!this.picker().isVisible()){
18671             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18672                 this.show();
18673             }
18674             return;
18675         }
18676         
18677         var dateChanged = false,
18678         dir, day, month,
18679         newDate, newViewDate;
18680         
18681         switch(e.keyCode){
18682             case 27: // escape
18683                 this.hide();
18684                 e.preventDefault();
18685                 break;
18686             case 37: // left
18687             case 39: // right
18688                 if (!this.keyboardNavigation) {
18689                     break;
18690                 }
18691                 dir = e.keyCode == 37 ? -1 : 1;
18692                 
18693                 if (e.ctrlKey){
18694                     newDate = this.moveYear(this.date, dir);
18695                     newViewDate = this.moveYear(this.viewDate, dir);
18696                 } else if (e.shiftKey){
18697                     newDate = this.moveMonth(this.date, dir);
18698                     newViewDate = this.moveMonth(this.viewDate, dir);
18699                 } else {
18700                     newDate = new Date(this.date);
18701                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18702                     newViewDate = new Date(this.viewDate);
18703                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18704                 }
18705                 if (this.dateWithinRange(newDate)){
18706                     this.date = newDate;
18707                     this.viewDate = newViewDate;
18708                     this.setValue(this.formatDate(this.date));
18709 //                    this.update();
18710                     e.preventDefault();
18711                     dateChanged = true;
18712                 }
18713                 break;
18714             case 38: // up
18715             case 40: // down
18716                 if (!this.keyboardNavigation) {
18717                     break;
18718                 }
18719                 dir = e.keyCode == 38 ? -1 : 1;
18720                 if (e.ctrlKey){
18721                     newDate = this.moveYear(this.date, dir);
18722                     newViewDate = this.moveYear(this.viewDate, dir);
18723                 } else if (e.shiftKey){
18724                     newDate = this.moveMonth(this.date, dir);
18725                     newViewDate = this.moveMonth(this.viewDate, dir);
18726                 } else {
18727                     newDate = new Date(this.date);
18728                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18729                     newViewDate = new Date(this.viewDate);
18730                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18731                 }
18732                 if (this.dateWithinRange(newDate)){
18733                     this.date = newDate;
18734                     this.viewDate = newViewDate;
18735                     this.setValue(this.formatDate(this.date));
18736 //                    this.update();
18737                     e.preventDefault();
18738                     dateChanged = true;
18739                 }
18740                 break;
18741             case 13: // enter
18742                 this.setValue(this.formatDate(this.date));
18743                 this.hide();
18744                 e.preventDefault();
18745                 break;
18746             case 9: // tab
18747                 this.setValue(this.formatDate(this.date));
18748                 this.hide();
18749                 break;
18750             case 16: // shift
18751             case 17: // ctrl
18752             case 18: // alt
18753                 break;
18754             default :
18755                 this.hide();
18756                 
18757         }
18758     },
18759     
18760     
18761     onClick: function(e) 
18762     {
18763         e.stopPropagation();
18764         e.preventDefault();
18765         
18766         var target = e.getTarget();
18767         
18768         if(target.nodeName.toLowerCase() === 'i'){
18769             target = Roo.get(target).dom.parentNode;
18770         }
18771         
18772         var nodeName = target.nodeName;
18773         var className = target.className;
18774         var html = target.innerHTML;
18775         //Roo.log(nodeName);
18776         
18777         switch(nodeName.toLowerCase()) {
18778             case 'th':
18779                 switch(className) {
18780                     case 'switch':
18781                         this.showMode(1);
18782                         break;
18783                     case 'prev':
18784                     case 'next':
18785                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18786                         switch(this.viewMode){
18787                                 case 0:
18788                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18789                                         break;
18790                                 case 1:
18791                                 case 2:
18792                                         this.viewDate = this.moveYear(this.viewDate, dir);
18793                                         break;
18794                         }
18795                         this.fill();
18796                         break;
18797                     case 'today':
18798                         var date = new Date();
18799                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18800 //                        this.fill()
18801                         this.setValue(this.formatDate(this.date));
18802                         
18803                         this.hide();
18804                         break;
18805                 }
18806                 break;
18807             case 'span':
18808                 if (className.indexOf('disabled') < 0) {
18809                     this.viewDate.setUTCDate(1);
18810                     if (className.indexOf('month') > -1) {
18811                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18812                     } else {
18813                         var year = parseInt(html, 10) || 0;
18814                         this.viewDate.setUTCFullYear(year);
18815                         
18816                     }
18817                     
18818                     if(this.singleMode){
18819                         this.setValue(this.formatDate(this.viewDate));
18820                         this.hide();
18821                         return;
18822                     }
18823                     
18824                     this.showMode(-1);
18825                     this.fill();
18826                 }
18827                 break;
18828                 
18829             case 'td':
18830                 //Roo.log(className);
18831                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18832                     var day = parseInt(html, 10) || 1;
18833                     var year = this.viewDate.getUTCFullYear(),
18834                         month = this.viewDate.getUTCMonth();
18835
18836                     if (className.indexOf('old') > -1) {
18837                         if(month === 0 ){
18838                             month = 11;
18839                             year -= 1;
18840                         }else{
18841                             month -= 1;
18842                         }
18843                     } else if (className.indexOf('new') > -1) {
18844                         if (month == 11) {
18845                             month = 0;
18846                             year += 1;
18847                         } else {
18848                             month += 1;
18849                         }
18850                     }
18851                     //Roo.log([year,month,day]);
18852                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18853                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18854 //                    this.fill();
18855                     //Roo.log(this.formatDate(this.date));
18856                     this.setValue(this.formatDate(this.date));
18857                     this.hide();
18858                 }
18859                 break;
18860         }
18861     },
18862     
18863     setStartDate: function(startDate)
18864     {
18865         this.startDate = startDate || -Infinity;
18866         if (this.startDate !== -Infinity) {
18867             this.startDate = this.parseDate(this.startDate);
18868         }
18869         this.update();
18870         this.updateNavArrows();
18871     },
18872
18873     setEndDate: function(endDate)
18874     {
18875         this.endDate = endDate || Infinity;
18876         if (this.endDate !== Infinity) {
18877             this.endDate = this.parseDate(this.endDate);
18878         }
18879         this.update();
18880         this.updateNavArrows();
18881     },
18882     
18883     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18884     {
18885         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18886         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18887             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18888         }
18889         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18890             return parseInt(d, 10);
18891         });
18892         this.update();
18893         this.updateNavArrows();
18894     },
18895     
18896     updateNavArrows: function() 
18897     {
18898         if(this.singleMode){
18899             return;
18900         }
18901         
18902         var d = new Date(this.viewDate),
18903         year = d.getUTCFullYear(),
18904         month = d.getUTCMonth();
18905         
18906         Roo.each(this.picker().select('.prev', true).elements, function(v){
18907             v.show();
18908             switch (this.viewMode) {
18909                 case 0:
18910
18911                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18912                         v.hide();
18913                     }
18914                     break;
18915                 case 1:
18916                 case 2:
18917                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18918                         v.hide();
18919                     }
18920                     break;
18921             }
18922         });
18923         
18924         Roo.each(this.picker().select('.next', true).elements, function(v){
18925             v.show();
18926             switch (this.viewMode) {
18927                 case 0:
18928
18929                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18930                         v.hide();
18931                     }
18932                     break;
18933                 case 1:
18934                 case 2:
18935                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18936                         v.hide();
18937                     }
18938                     break;
18939             }
18940         })
18941     },
18942     
18943     moveMonth: function(date, dir)
18944     {
18945         if (!dir) {
18946             return date;
18947         }
18948         var new_date = new Date(date.valueOf()),
18949         day = new_date.getUTCDate(),
18950         month = new_date.getUTCMonth(),
18951         mag = Math.abs(dir),
18952         new_month, test;
18953         dir = dir > 0 ? 1 : -1;
18954         if (mag == 1){
18955             test = dir == -1
18956             // If going back one month, make sure month is not current month
18957             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18958             ? function(){
18959                 return new_date.getUTCMonth() == month;
18960             }
18961             // If going forward one month, make sure month is as expected
18962             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18963             : function(){
18964                 return new_date.getUTCMonth() != new_month;
18965             };
18966             new_month = month + dir;
18967             new_date.setUTCMonth(new_month);
18968             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18969             if (new_month < 0 || new_month > 11) {
18970                 new_month = (new_month + 12) % 12;
18971             }
18972         } else {
18973             // For magnitudes >1, move one month at a time...
18974             for (var i=0; i<mag; i++) {
18975                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18976                 new_date = this.moveMonth(new_date, dir);
18977             }
18978             // ...then reset the day, keeping it in the new month
18979             new_month = new_date.getUTCMonth();
18980             new_date.setUTCDate(day);
18981             test = function(){
18982                 return new_month != new_date.getUTCMonth();
18983             };
18984         }
18985         // Common date-resetting loop -- if date is beyond end of month, make it
18986         // end of month
18987         while (test()){
18988             new_date.setUTCDate(--day);
18989             new_date.setUTCMonth(new_month);
18990         }
18991         return new_date;
18992     },
18993
18994     moveYear: function(date, dir)
18995     {
18996         return this.moveMonth(date, dir*12);
18997     },
18998
18999     dateWithinRange: function(date)
19000     {
19001         return date >= this.startDate && date <= this.endDate;
19002     },
19003
19004     
19005     remove: function() 
19006     {
19007         this.picker().remove();
19008     },
19009     
19010     validateValue : function(value)
19011     {
19012         if(value.length < 1)  {
19013             if(this.allowBlank){
19014                 return true;
19015             }
19016             return false;
19017         }
19018         
19019         if(value.length < this.minLength){
19020             return false;
19021         }
19022         if(value.length > this.maxLength){
19023             return false;
19024         }
19025         if(this.vtype){
19026             var vt = Roo.form.VTypes;
19027             if(!vt[this.vtype](value, this)){
19028                 return false;
19029             }
19030         }
19031         if(typeof this.validator == "function"){
19032             var msg = this.validator(value);
19033             if(msg !== true){
19034                 return false;
19035             }
19036         }
19037         
19038         if(this.regex && !this.regex.test(value)){
19039             return false;
19040         }
19041         
19042         if(typeof(this.parseDate(value)) == 'undefined'){
19043             return false;
19044         }
19045         
19046         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19047             return false;
19048         }      
19049         
19050         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19051             return false;
19052         } 
19053         
19054         
19055         return true;
19056     }
19057    
19058 });
19059
19060 Roo.apply(Roo.bootstrap.DateField,  {
19061     
19062     head : {
19063         tag: 'thead',
19064         cn: [
19065         {
19066             tag: 'tr',
19067             cn: [
19068             {
19069                 tag: 'th',
19070                 cls: 'prev',
19071                 html: '<i class="fa fa-arrow-left"/>'
19072             },
19073             {
19074                 tag: 'th',
19075                 cls: 'switch',
19076                 colspan: '5'
19077             },
19078             {
19079                 tag: 'th',
19080                 cls: 'next',
19081                 html: '<i class="fa fa-arrow-right"/>'
19082             }
19083
19084             ]
19085         }
19086         ]
19087     },
19088     
19089     content : {
19090         tag: 'tbody',
19091         cn: [
19092         {
19093             tag: 'tr',
19094             cn: [
19095             {
19096                 tag: 'td',
19097                 colspan: '7'
19098             }
19099             ]
19100         }
19101         ]
19102     },
19103     
19104     footer : {
19105         tag: 'tfoot',
19106         cn: [
19107         {
19108             tag: 'tr',
19109             cn: [
19110             {
19111                 tag: 'th',
19112                 colspan: '7',
19113                 cls: 'today'
19114             }
19115                     
19116             ]
19117         }
19118         ]
19119     },
19120     
19121     dates:{
19122         en: {
19123             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19124             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19125             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19126             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19127             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19128             today: "Today"
19129         }
19130     },
19131     
19132     modes: [
19133     {
19134         clsName: 'days',
19135         navFnc: 'Month',
19136         navStep: 1
19137     },
19138     {
19139         clsName: 'months',
19140         navFnc: 'FullYear',
19141         navStep: 1
19142     },
19143     {
19144         clsName: 'years',
19145         navFnc: 'FullYear',
19146         navStep: 10
19147     }]
19148 });
19149
19150 Roo.apply(Roo.bootstrap.DateField,  {
19151   
19152     template : {
19153         tag: 'div',
19154         cls: 'datepicker dropdown-menu roo-dynamic',
19155         cn: [
19156         {
19157             tag: 'div',
19158             cls: 'datepicker-days',
19159             cn: [
19160             {
19161                 tag: 'table',
19162                 cls: 'table-condensed',
19163                 cn:[
19164                 Roo.bootstrap.DateField.head,
19165                 {
19166                     tag: 'tbody'
19167                 },
19168                 Roo.bootstrap.DateField.footer
19169                 ]
19170             }
19171             ]
19172         },
19173         {
19174             tag: 'div',
19175             cls: 'datepicker-months',
19176             cn: [
19177             {
19178                 tag: 'table',
19179                 cls: 'table-condensed',
19180                 cn:[
19181                 Roo.bootstrap.DateField.head,
19182                 Roo.bootstrap.DateField.content,
19183                 Roo.bootstrap.DateField.footer
19184                 ]
19185             }
19186             ]
19187         },
19188         {
19189             tag: 'div',
19190             cls: 'datepicker-years',
19191             cn: [
19192             {
19193                 tag: 'table',
19194                 cls: 'table-condensed',
19195                 cn:[
19196                 Roo.bootstrap.DateField.head,
19197                 Roo.bootstrap.DateField.content,
19198                 Roo.bootstrap.DateField.footer
19199                 ]
19200             }
19201             ]
19202         }
19203         ]
19204     }
19205 });
19206
19207  
19208
19209  /*
19210  * - LGPL
19211  *
19212  * TimeField
19213  * 
19214  */
19215
19216 /**
19217  * @class Roo.bootstrap.TimeField
19218  * @extends Roo.bootstrap.Input
19219  * Bootstrap DateField class
19220  * 
19221  * 
19222  * @constructor
19223  * Create a new TimeField
19224  * @param {Object} config The config object
19225  */
19226
19227 Roo.bootstrap.TimeField = function(config){
19228     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19229     this.addEvents({
19230             /**
19231              * @event show
19232              * Fires when this field show.
19233              * @param {Roo.bootstrap.DateField} thisthis
19234              * @param {Mixed} date The date value
19235              */
19236             show : true,
19237             /**
19238              * @event show
19239              * Fires when this field hide.
19240              * @param {Roo.bootstrap.DateField} this
19241              * @param {Mixed} date The date value
19242              */
19243             hide : true,
19244             /**
19245              * @event select
19246              * Fires when select a date.
19247              * @param {Roo.bootstrap.DateField} this
19248              * @param {Mixed} date The date value
19249              */
19250             select : true
19251         });
19252 };
19253
19254 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19255     
19256     /**
19257      * @cfg {String} format
19258      * The default time format string which can be overriden for localization support.  The format must be
19259      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19260      */
19261     format : "H:i",
19262        
19263     onRender: function(ct, position)
19264     {
19265         
19266         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19267                 
19268         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19269         
19270         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19271         
19272         this.pop = this.picker().select('>.datepicker-time',true).first();
19273         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19274         
19275         this.picker().on('mousedown', this.onMousedown, this);
19276         this.picker().on('click', this.onClick, this);
19277         
19278         this.picker().addClass('datepicker-dropdown');
19279     
19280         this.fillTime();
19281         this.update();
19282             
19283         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19284         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19285         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19286         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19287         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19288         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19289
19290     },
19291     
19292     fireKey: function(e){
19293         if (!this.picker().isVisible()){
19294             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19295                 this.show();
19296             }
19297             return;
19298         }
19299
19300         e.preventDefault();
19301         
19302         switch(e.keyCode){
19303             case 27: // escape
19304                 this.hide();
19305                 break;
19306             case 37: // left
19307             case 39: // right
19308                 this.onTogglePeriod();
19309                 break;
19310             case 38: // up
19311                 this.onIncrementMinutes();
19312                 break;
19313             case 40: // down
19314                 this.onDecrementMinutes();
19315                 break;
19316             case 13: // enter
19317             case 9: // tab
19318                 this.setTime();
19319                 break;
19320         }
19321     },
19322     
19323     onClick: function(e) {
19324         e.stopPropagation();
19325         e.preventDefault();
19326     },
19327     
19328     picker : function()
19329     {
19330         return this.el.select('.datepicker', true).first();
19331     },
19332     
19333     fillTime: function()
19334     {    
19335         var time = this.pop.select('tbody', true).first();
19336         
19337         time.dom.innerHTML = '';
19338         
19339         time.createChild({
19340             tag: 'tr',
19341             cn: [
19342                 {
19343                     tag: 'td',
19344                     cn: [
19345                         {
19346                             tag: 'a',
19347                             href: '#',
19348                             cls: 'btn',
19349                             cn: [
19350                                 {
19351                                     tag: 'span',
19352                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19353                                 }
19354                             ]
19355                         } 
19356                     ]
19357                 },
19358                 {
19359                     tag: 'td',
19360                     cls: 'separator'
19361                 },
19362                 {
19363                     tag: 'td',
19364                     cn: [
19365                         {
19366                             tag: 'a',
19367                             href: '#',
19368                             cls: 'btn',
19369                             cn: [
19370                                 {
19371                                     tag: 'span',
19372                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19373                                 }
19374                             ]
19375                         }
19376                     ]
19377                 },
19378                 {
19379                     tag: 'td',
19380                     cls: 'separator'
19381                 }
19382             ]
19383         });
19384         
19385         time.createChild({
19386             tag: 'tr',
19387             cn: [
19388                 {
19389                     tag: 'td',
19390                     cn: [
19391                         {
19392                             tag: 'span',
19393                             cls: 'timepicker-hour',
19394                             html: '00'
19395                         }  
19396                     ]
19397                 },
19398                 {
19399                     tag: 'td',
19400                     cls: 'separator',
19401                     html: ':'
19402                 },
19403                 {
19404                     tag: 'td',
19405                     cn: [
19406                         {
19407                             tag: 'span',
19408                             cls: 'timepicker-minute',
19409                             html: '00'
19410                         }  
19411                     ]
19412                 },
19413                 {
19414                     tag: 'td',
19415                     cls: 'separator'
19416                 },
19417                 {
19418                     tag: 'td',
19419                     cn: [
19420                         {
19421                             tag: 'button',
19422                             type: 'button',
19423                             cls: 'btn btn-primary period',
19424                             html: 'AM'
19425                             
19426                         }
19427                     ]
19428                 }
19429             ]
19430         });
19431         
19432         time.createChild({
19433             tag: 'tr',
19434             cn: [
19435                 {
19436                     tag: 'td',
19437                     cn: [
19438                         {
19439                             tag: 'a',
19440                             href: '#',
19441                             cls: 'btn',
19442                             cn: [
19443                                 {
19444                                     tag: 'span',
19445                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19446                                 }
19447                             ]
19448                         }
19449                     ]
19450                 },
19451                 {
19452                     tag: 'td',
19453                     cls: 'separator'
19454                 },
19455                 {
19456                     tag: 'td',
19457                     cn: [
19458                         {
19459                             tag: 'a',
19460                             href: '#',
19461                             cls: 'btn',
19462                             cn: [
19463                                 {
19464                                     tag: 'span',
19465                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19466                                 }
19467                             ]
19468                         }
19469                     ]
19470                 },
19471                 {
19472                     tag: 'td',
19473                     cls: 'separator'
19474                 }
19475             ]
19476         });
19477         
19478     },
19479     
19480     update: function()
19481     {
19482         
19483         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19484         
19485         this.fill();
19486     },
19487     
19488     fill: function() 
19489     {
19490         var hours = this.time.getHours();
19491         var minutes = this.time.getMinutes();
19492         var period = 'AM';
19493         
19494         if(hours > 11){
19495             period = 'PM';
19496         }
19497         
19498         if(hours == 0){
19499             hours = 12;
19500         }
19501         
19502         
19503         if(hours > 12){
19504             hours = hours - 12;
19505         }
19506         
19507         if(hours < 10){
19508             hours = '0' + hours;
19509         }
19510         
19511         if(minutes < 10){
19512             minutes = '0' + minutes;
19513         }
19514         
19515         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19516         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19517         this.pop.select('button', true).first().dom.innerHTML = period;
19518         
19519     },
19520     
19521     place: function()
19522     {   
19523         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19524         
19525         var cls = ['bottom'];
19526         
19527         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19528             cls.pop();
19529             cls.push('top');
19530         }
19531         
19532         cls.push('right');
19533         
19534         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19535             cls.pop();
19536             cls.push('left');
19537         }
19538         
19539         this.picker().addClass(cls.join('-'));
19540         
19541         var _this = this;
19542         
19543         Roo.each(cls, function(c){
19544             if(c == 'bottom'){
19545                 _this.picker().setTop(_this.inputEl().getHeight());
19546                 return;
19547             }
19548             if(c == 'top'){
19549                 _this.picker().setTop(0 - _this.picker().getHeight());
19550                 return;
19551             }
19552             
19553             if(c == 'left'){
19554                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19555                 return;
19556             }
19557             if(c == 'right'){
19558                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19559                 return;
19560             }
19561         });
19562         
19563     },
19564   
19565     onFocus : function()
19566     {
19567         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19568         this.show();
19569     },
19570     
19571     onBlur : function()
19572     {
19573         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19574         this.hide();
19575     },
19576     
19577     show : function()
19578     {
19579         this.picker().show();
19580         this.pop.show();
19581         this.update();
19582         this.place();
19583         
19584         this.fireEvent('show', this, this.date);
19585     },
19586     
19587     hide : function()
19588     {
19589         this.picker().hide();
19590         this.pop.hide();
19591         
19592         this.fireEvent('hide', this, this.date);
19593     },
19594     
19595     setTime : function()
19596     {
19597         this.hide();
19598         this.setValue(this.time.format(this.format));
19599         
19600         this.fireEvent('select', this, this.date);
19601         
19602         
19603     },
19604     
19605     onMousedown: function(e){
19606         e.stopPropagation();
19607         e.preventDefault();
19608     },
19609     
19610     onIncrementHours: function()
19611     {
19612         Roo.log('onIncrementHours');
19613         this.time = this.time.add(Date.HOUR, 1);
19614         this.update();
19615         
19616     },
19617     
19618     onDecrementHours: function()
19619     {
19620         Roo.log('onDecrementHours');
19621         this.time = this.time.add(Date.HOUR, -1);
19622         this.update();
19623     },
19624     
19625     onIncrementMinutes: function()
19626     {
19627         Roo.log('onIncrementMinutes');
19628         this.time = this.time.add(Date.MINUTE, 1);
19629         this.update();
19630     },
19631     
19632     onDecrementMinutes: function()
19633     {
19634         Roo.log('onDecrementMinutes');
19635         this.time = this.time.add(Date.MINUTE, -1);
19636         this.update();
19637     },
19638     
19639     onTogglePeriod: function()
19640     {
19641         Roo.log('onTogglePeriod');
19642         this.time = this.time.add(Date.HOUR, 12);
19643         this.update();
19644     }
19645     
19646    
19647 });
19648
19649 Roo.apply(Roo.bootstrap.TimeField,  {
19650     
19651     content : {
19652         tag: 'tbody',
19653         cn: [
19654             {
19655                 tag: 'tr',
19656                 cn: [
19657                 {
19658                     tag: 'td',
19659                     colspan: '7'
19660                 }
19661                 ]
19662             }
19663         ]
19664     },
19665     
19666     footer : {
19667         tag: 'tfoot',
19668         cn: [
19669             {
19670                 tag: 'tr',
19671                 cn: [
19672                 {
19673                     tag: 'th',
19674                     colspan: '7',
19675                     cls: '',
19676                     cn: [
19677                         {
19678                             tag: 'button',
19679                             cls: 'btn btn-info ok',
19680                             html: 'OK'
19681                         }
19682                     ]
19683                 }
19684
19685                 ]
19686             }
19687         ]
19688     }
19689 });
19690
19691 Roo.apply(Roo.bootstrap.TimeField,  {
19692   
19693     template : {
19694         tag: 'div',
19695         cls: 'datepicker dropdown-menu',
19696         cn: [
19697             {
19698                 tag: 'div',
19699                 cls: 'datepicker-time',
19700                 cn: [
19701                 {
19702                     tag: 'table',
19703                     cls: 'table-condensed',
19704                     cn:[
19705                     Roo.bootstrap.TimeField.content,
19706                     Roo.bootstrap.TimeField.footer
19707                     ]
19708                 }
19709                 ]
19710             }
19711         ]
19712     }
19713 });
19714
19715  
19716
19717  /*
19718  * - LGPL
19719  *
19720  * MonthField
19721  * 
19722  */
19723
19724 /**
19725  * @class Roo.bootstrap.MonthField
19726  * @extends Roo.bootstrap.Input
19727  * Bootstrap MonthField class
19728  * 
19729  * @cfg {String} language default en
19730  * 
19731  * @constructor
19732  * Create a new MonthField
19733  * @param {Object} config The config object
19734  */
19735
19736 Roo.bootstrap.MonthField = function(config){
19737     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19738     
19739     this.addEvents({
19740         /**
19741          * @event show
19742          * Fires when this field show.
19743          * @param {Roo.bootstrap.MonthField} this
19744          * @param {Mixed} date The date value
19745          */
19746         show : true,
19747         /**
19748          * @event show
19749          * Fires when this field hide.
19750          * @param {Roo.bootstrap.MonthField} this
19751          * @param {Mixed} date The date value
19752          */
19753         hide : true,
19754         /**
19755          * @event select
19756          * Fires when select a date.
19757          * @param {Roo.bootstrap.MonthField} this
19758          * @param {String} oldvalue The old value
19759          * @param {String} newvalue The new value
19760          */
19761         select : true
19762     });
19763 };
19764
19765 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19766     
19767     onRender: function(ct, position)
19768     {
19769         
19770         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19771         
19772         this.language = this.language || 'en';
19773         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19774         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19775         
19776         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19777         this.isInline = false;
19778         this.isInput = true;
19779         this.component = this.el.select('.add-on', true).first() || false;
19780         this.component = (this.component && this.component.length === 0) ? false : this.component;
19781         this.hasInput = this.component && this.inputEL().length;
19782         
19783         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19784         
19785         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19786         
19787         this.picker().on('mousedown', this.onMousedown, this);
19788         this.picker().on('click', this.onClick, this);
19789         
19790         this.picker().addClass('datepicker-dropdown');
19791         
19792         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19793             v.setStyle('width', '189px');
19794         });
19795         
19796         this.fillMonths();
19797         
19798         this.update();
19799         
19800         if(this.isInline) {
19801             this.show();
19802         }
19803         
19804     },
19805     
19806     setValue: function(v, suppressEvent)
19807     {   
19808         var o = this.getValue();
19809         
19810         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19811         
19812         this.update();
19813
19814         if(suppressEvent !== true){
19815             this.fireEvent('select', this, o, v);
19816         }
19817         
19818     },
19819     
19820     getValue: function()
19821     {
19822         return this.value;
19823     },
19824     
19825     onClick: function(e) 
19826     {
19827         e.stopPropagation();
19828         e.preventDefault();
19829         
19830         var target = e.getTarget();
19831         
19832         if(target.nodeName.toLowerCase() === 'i'){
19833             target = Roo.get(target).dom.parentNode;
19834         }
19835         
19836         var nodeName = target.nodeName;
19837         var className = target.className;
19838         var html = target.innerHTML;
19839         
19840         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19841             return;
19842         }
19843         
19844         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19845         
19846         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19847         
19848         this.hide();
19849                         
19850     },
19851     
19852     picker : function()
19853     {
19854         return this.pickerEl;
19855     },
19856     
19857     fillMonths: function()
19858     {    
19859         var i = 0;
19860         var months = this.picker().select('>.datepicker-months td', true).first();
19861         
19862         months.dom.innerHTML = '';
19863         
19864         while (i < 12) {
19865             var month = {
19866                 tag: 'span',
19867                 cls: 'month',
19868                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19869             };
19870             
19871             months.createChild(month);
19872         }
19873         
19874     },
19875     
19876     update: function()
19877     {
19878         var _this = this;
19879         
19880         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19881             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19882         }
19883         
19884         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19885             e.removeClass('active');
19886             
19887             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19888                 e.addClass('active');
19889             }
19890         })
19891     },
19892     
19893     place: function()
19894     {
19895         if(this.isInline) {
19896             return;
19897         }
19898         
19899         this.picker().removeClass(['bottom', 'top']);
19900         
19901         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19902             /*
19903              * place to the top of element!
19904              *
19905              */
19906             
19907             this.picker().addClass('top');
19908             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19909             
19910             return;
19911         }
19912         
19913         this.picker().addClass('bottom');
19914         
19915         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19916     },
19917     
19918     onFocus : function()
19919     {
19920         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19921         this.show();
19922     },
19923     
19924     onBlur : function()
19925     {
19926         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19927         
19928         var d = this.inputEl().getValue();
19929         
19930         this.setValue(d);
19931                 
19932         this.hide();
19933     },
19934     
19935     show : function()
19936     {
19937         this.picker().show();
19938         this.picker().select('>.datepicker-months', true).first().show();
19939         this.update();
19940         this.place();
19941         
19942         this.fireEvent('show', this, this.date);
19943     },
19944     
19945     hide : function()
19946     {
19947         if(this.isInline) {
19948             return;
19949         }
19950         this.picker().hide();
19951         this.fireEvent('hide', this, this.date);
19952         
19953     },
19954     
19955     onMousedown: function(e)
19956     {
19957         e.stopPropagation();
19958         e.preventDefault();
19959     },
19960     
19961     keyup: function(e)
19962     {
19963         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19964         this.update();
19965     },
19966
19967     fireKey: function(e)
19968     {
19969         if (!this.picker().isVisible()){
19970             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19971                 this.show();
19972             }
19973             return;
19974         }
19975         
19976         var dir;
19977         
19978         switch(e.keyCode){
19979             case 27: // escape
19980                 this.hide();
19981                 e.preventDefault();
19982                 break;
19983             case 37: // left
19984             case 39: // right
19985                 dir = e.keyCode == 37 ? -1 : 1;
19986                 
19987                 this.vIndex = this.vIndex + dir;
19988                 
19989                 if(this.vIndex < 0){
19990                     this.vIndex = 0;
19991                 }
19992                 
19993                 if(this.vIndex > 11){
19994                     this.vIndex = 11;
19995                 }
19996                 
19997                 if(isNaN(this.vIndex)){
19998                     this.vIndex = 0;
19999                 }
20000                 
20001                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20002                 
20003                 break;
20004             case 38: // up
20005             case 40: // down
20006                 
20007                 dir = e.keyCode == 38 ? -1 : 1;
20008                 
20009                 this.vIndex = this.vIndex + dir * 4;
20010                 
20011                 if(this.vIndex < 0){
20012                     this.vIndex = 0;
20013                 }
20014                 
20015                 if(this.vIndex > 11){
20016                     this.vIndex = 11;
20017                 }
20018                 
20019                 if(isNaN(this.vIndex)){
20020                     this.vIndex = 0;
20021                 }
20022                 
20023                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20024                 break;
20025                 
20026             case 13: // enter
20027                 
20028                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20029                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20030                 }
20031                 
20032                 this.hide();
20033                 e.preventDefault();
20034                 break;
20035             case 9: // tab
20036                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20037                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20038                 }
20039                 this.hide();
20040                 break;
20041             case 16: // shift
20042             case 17: // ctrl
20043             case 18: // alt
20044                 break;
20045             default :
20046                 this.hide();
20047                 
20048         }
20049     },
20050     
20051     remove: function() 
20052     {
20053         this.picker().remove();
20054     }
20055    
20056 });
20057
20058 Roo.apply(Roo.bootstrap.MonthField,  {
20059     
20060     content : {
20061         tag: 'tbody',
20062         cn: [
20063         {
20064             tag: 'tr',
20065             cn: [
20066             {
20067                 tag: 'td',
20068                 colspan: '7'
20069             }
20070             ]
20071         }
20072         ]
20073     },
20074     
20075     dates:{
20076         en: {
20077             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20078             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20079         }
20080     }
20081 });
20082
20083 Roo.apply(Roo.bootstrap.MonthField,  {
20084   
20085     template : {
20086         tag: 'div',
20087         cls: 'datepicker dropdown-menu roo-dynamic',
20088         cn: [
20089             {
20090                 tag: 'div',
20091                 cls: 'datepicker-months',
20092                 cn: [
20093                 {
20094                     tag: 'table',
20095                     cls: 'table-condensed',
20096                     cn:[
20097                         Roo.bootstrap.DateField.content
20098                     ]
20099                 }
20100                 ]
20101             }
20102         ]
20103     }
20104 });
20105
20106  
20107
20108  
20109  /*
20110  * - LGPL
20111  *
20112  * CheckBox
20113  * 
20114  */
20115
20116 /**
20117  * @class Roo.bootstrap.CheckBox
20118  * @extends Roo.bootstrap.Input
20119  * Bootstrap CheckBox class
20120  * 
20121  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20122  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20123  * @cfg {String} boxLabel The text that appears beside the checkbox
20124  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20125  * @cfg {Boolean} checked initnal the element
20126  * @cfg {Boolean} inline inline the element (default false)
20127  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20128  * 
20129  * @constructor
20130  * Create a new CheckBox
20131  * @param {Object} config The config object
20132  */
20133
20134 Roo.bootstrap.CheckBox = function(config){
20135     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20136    
20137     this.addEvents({
20138         /**
20139         * @event check
20140         * Fires when the element is checked or unchecked.
20141         * @param {Roo.bootstrap.CheckBox} this This input
20142         * @param {Boolean} checked The new checked value
20143         */
20144        check : true
20145     });
20146     
20147 };
20148
20149 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20150   
20151     inputType: 'checkbox',
20152     inputValue: 1,
20153     valueOff: 0,
20154     boxLabel: false,
20155     checked: false,
20156     weight : false,
20157     inline: false,
20158     
20159     getAutoCreate : function()
20160     {
20161         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20162         
20163         var id = Roo.id();
20164         
20165         var cfg = {};
20166         
20167         cfg.cls = 'form-group ' + this.inputType; //input-group
20168         
20169         if(this.inline){
20170             cfg.cls += ' ' + this.inputType + '-inline';
20171         }
20172         
20173         var input =  {
20174             tag: 'input',
20175             id : id,
20176             type : this.inputType,
20177             value : this.inputValue,
20178             cls : 'roo-' + this.inputType, //'form-box',
20179             placeholder : this.placeholder || ''
20180             
20181         };
20182         
20183         if(this.inputType != 'radio'){
20184             var hidden =  {
20185                 tag: 'input',
20186                 type : 'hidden',
20187                 cls : 'roo-hidden-value',
20188                 value : this.checked ? this.valueOff : this.inputValue
20189             };
20190         }
20191         
20192             
20193         if (this.weight) { // Validity check?
20194             cfg.cls += " " + this.inputType + "-" + this.weight;
20195         }
20196         
20197         if (this.disabled) {
20198             input.disabled=true;
20199         }
20200         
20201         if(this.checked){
20202             input.checked = this.checked;
20203             
20204         }
20205         
20206         
20207         if (this.name) {
20208             
20209             input.name = this.name;
20210             
20211             if(this.inputType != 'radio'){
20212                 hidden.name = this.name;
20213                 input.name = '_hidden_' + this.name;
20214             }
20215         }
20216         
20217         if (this.size) {
20218             input.cls += ' input-' + this.size;
20219         }
20220         
20221         var settings=this;
20222         
20223         ['xs','sm','md','lg'].map(function(size){
20224             if (settings[size]) {
20225                 cfg.cls += ' col-' + size + '-' + settings[size];
20226             }
20227         });
20228         
20229         var inputblock = input;
20230          
20231         if (this.before || this.after) {
20232             
20233             inputblock = {
20234                 cls : 'input-group',
20235                 cn :  [] 
20236             };
20237             
20238             if (this.before) {
20239                 inputblock.cn.push({
20240                     tag :'span',
20241                     cls : 'input-group-addon',
20242                     html : this.before
20243                 });
20244             }
20245             
20246             inputblock.cn.push(input);
20247             
20248             if(this.inputType != 'radio'){
20249                 inputblock.cn.push(hidden);
20250             }
20251             
20252             if (this.after) {
20253                 inputblock.cn.push({
20254                     tag :'span',
20255                     cls : 'input-group-addon',
20256                     html : this.after
20257                 });
20258             }
20259             
20260         }
20261         
20262         if (align ==='left' && this.fieldLabel.length) {
20263 //                Roo.log("left and has label");
20264             cfg.cn = [
20265                 {
20266                     tag: 'label',
20267                     'for' :  id,
20268                     cls : 'control-label',
20269                     html : this.fieldLabel
20270
20271                 },
20272                 {
20273                     cls : "", 
20274                     cn: [
20275                         inputblock
20276                     ]
20277                 }
20278             ];
20279             
20280             if(this.labelWidth > 12){
20281                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20282             }
20283             
20284             if(this.labelWidth < 13 && this.labelmd == 0){
20285                 this.labelmd = this.labelWidth;
20286             }
20287             
20288             if(this.labellg > 0){
20289                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20290                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20291             }
20292             
20293             if(this.labelmd > 0){
20294                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20295                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20296             }
20297             
20298             if(this.labelsm > 0){
20299                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20300                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20301             }
20302             
20303             if(this.labelxs > 0){
20304                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20305                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20306             }
20307             
20308         } else if ( this.fieldLabel.length) {
20309 //                Roo.log(" label");
20310                 cfg.cn = [
20311                    
20312                     {
20313                         tag: this.boxLabel ? 'span' : 'label',
20314                         'for': id,
20315                         cls: 'control-label box-input-label',
20316                         //cls : 'input-group-addon',
20317                         html : this.fieldLabel
20318                         
20319                     },
20320                     
20321                     inputblock
20322                     
20323                 ];
20324
20325         } else {
20326             
20327 //                Roo.log(" no label && no align");
20328                 cfg.cn = [  inputblock ] ;
20329                 
20330                 
20331         }
20332         
20333         if(this.boxLabel){
20334              var boxLabelCfg = {
20335                 tag: 'label',
20336                 //'for': id, // box label is handled by onclick - so no for...
20337                 cls: 'box-label',
20338                 html: this.boxLabel
20339             };
20340             
20341             if(this.tooltip){
20342                 boxLabelCfg.tooltip = this.tooltip;
20343             }
20344              
20345             cfg.cn.push(boxLabelCfg);
20346         }
20347         
20348         if(this.inputType != 'radio'){
20349             cfg.cn.push(hidden);
20350         }
20351         
20352         return cfg;
20353         
20354     },
20355     
20356     /**
20357      * return the real input element.
20358      */
20359     inputEl: function ()
20360     {
20361         return this.el.select('input.roo-' + this.inputType,true).first();
20362     },
20363     hiddenEl: function ()
20364     {
20365         return this.el.select('input.roo-hidden-value',true).first();
20366     },
20367     
20368     labelEl: function()
20369     {
20370         return this.el.select('label.control-label',true).first();
20371     },
20372     /* depricated... */
20373     
20374     label: function()
20375     {
20376         return this.labelEl();
20377     },
20378     
20379     boxLabelEl: function()
20380     {
20381         return this.el.select('label.box-label',true).first();
20382     },
20383     
20384     initEvents : function()
20385     {
20386 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20387         
20388         this.inputEl().on('click', this.onClick,  this);
20389         
20390         if (this.boxLabel) { 
20391             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20392         }
20393         
20394         this.startValue = this.getValue();
20395         
20396         if(this.groupId){
20397             Roo.bootstrap.CheckBox.register(this);
20398         }
20399     },
20400     
20401     onClick : function()
20402     {   
20403         this.setChecked(!this.checked);
20404     },
20405     
20406     setChecked : function(state,suppressEvent)
20407     {
20408         this.startValue = this.getValue();
20409
20410         if(this.inputType == 'radio'){
20411             
20412             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20413                 e.dom.checked = false;
20414             });
20415             
20416             this.inputEl().dom.checked = true;
20417             
20418             this.inputEl().dom.value = this.inputValue;
20419             
20420             if(suppressEvent !== true){
20421                 this.fireEvent('check', this, true);
20422             }
20423             
20424             this.validate();
20425             
20426             return;
20427         }
20428         
20429         this.checked = state;
20430         
20431         this.inputEl().dom.checked = state;
20432         
20433         
20434         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20435         
20436         if(suppressEvent !== true){
20437             this.fireEvent('check', this, state);
20438         }
20439         
20440         this.validate();
20441     },
20442     
20443     getValue : function()
20444     {
20445         if(this.inputType == 'radio'){
20446             return this.getGroupValue();
20447         }
20448         
20449         return this.hiddenEl().dom.value;
20450         
20451     },
20452     
20453     getGroupValue : function()
20454     {
20455         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20456             return '';
20457         }
20458         
20459         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20460     },
20461     
20462     setValue : function(v,suppressEvent)
20463     {
20464         if(this.inputType == 'radio'){
20465             this.setGroupValue(v, suppressEvent);
20466             return;
20467         }
20468         
20469         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20470         
20471         this.validate();
20472     },
20473     
20474     setGroupValue : function(v, suppressEvent)
20475     {
20476         this.startValue = this.getValue();
20477         
20478         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20479             e.dom.checked = false;
20480             
20481             if(e.dom.value == v){
20482                 e.dom.checked = true;
20483             }
20484         });
20485         
20486         if(suppressEvent !== true){
20487             this.fireEvent('check', this, true);
20488         }
20489
20490         this.validate();
20491         
20492         return;
20493     },
20494     
20495     validate : function()
20496     {
20497         if(
20498                 this.disabled || 
20499                 (this.inputType == 'radio' && this.validateRadio()) ||
20500                 (this.inputType == 'checkbox' && this.validateCheckbox())
20501         ){
20502             this.markValid();
20503             return true;
20504         }
20505         
20506         this.markInvalid();
20507         return false;
20508     },
20509     
20510     validateRadio : function()
20511     {
20512         if(this.allowBlank){
20513             return true;
20514         }
20515         
20516         var valid = false;
20517         
20518         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20519             if(!e.dom.checked){
20520                 return;
20521             }
20522             
20523             valid = true;
20524             
20525             return false;
20526         });
20527         
20528         return valid;
20529     },
20530     
20531     validateCheckbox : function()
20532     {
20533         if(!this.groupId){
20534             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20535             //return (this.getValue() == this.inputValue) ? true : false;
20536         }
20537         
20538         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20539         
20540         if(!group){
20541             return false;
20542         }
20543         
20544         var r = false;
20545         
20546         for(var i in group){
20547             if(r){
20548                 break;
20549             }
20550             
20551             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20552         }
20553         
20554         return r;
20555     },
20556     
20557     /**
20558      * Mark this field as valid
20559      */
20560     markValid : function()
20561     {
20562         var _this = this;
20563         
20564         this.fireEvent('valid', this);
20565         
20566         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20567         
20568         if(this.groupId){
20569             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20570         }
20571         
20572         if(label){
20573             label.markValid();
20574         }
20575
20576         if(this.inputType == 'radio'){
20577             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20578                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20579                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20580             });
20581             
20582             return;
20583         }
20584
20585         if(!this.groupId){
20586             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20587             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20588             return;
20589         }
20590         
20591         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20592         
20593         if(!group){
20594             return;
20595         }
20596         
20597         for(var i in group){
20598             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20599             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20600         }
20601     },
20602     
20603      /**
20604      * Mark this field as invalid
20605      * @param {String} msg The validation message
20606      */
20607     markInvalid : function(msg)
20608     {
20609         if(this.allowBlank){
20610             return;
20611         }
20612         
20613         var _this = this;
20614         
20615         this.fireEvent('invalid', this, msg);
20616         
20617         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20618         
20619         if(this.groupId){
20620             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20621         }
20622         
20623         if(label){
20624             label.markInvalid();
20625         }
20626             
20627         if(this.inputType == 'radio'){
20628             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20629                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20630                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20631             });
20632             
20633             return;
20634         }
20635         
20636         if(!this.groupId){
20637             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20638             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20639             return;
20640         }
20641         
20642         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20643         
20644         if(!group){
20645             return;
20646         }
20647         
20648         for(var i in group){
20649             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20650             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20651         }
20652         
20653     },
20654     
20655     clearInvalid : function()
20656     {
20657         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20658         
20659         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20660         
20661         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20662         
20663         if (label) {
20664             label.iconEl.removeClass(label.validClass);
20665             label.iconEl.removeClass(label.invalidClass);
20666         }
20667     },
20668     
20669     disable : function()
20670     {
20671         if(this.inputType != 'radio'){
20672             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20673             return;
20674         }
20675         
20676         var _this = this;
20677         
20678         if(this.rendered){
20679             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20680                 _this.getActionEl().addClass(this.disabledClass);
20681                 e.dom.disabled = true;
20682             });
20683         }
20684         
20685         this.disabled = true;
20686         this.fireEvent("disable", this);
20687         return this;
20688     },
20689
20690     enable : function()
20691     {
20692         if(this.inputType != 'radio'){
20693             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20694             return;
20695         }
20696         
20697         var _this = this;
20698         
20699         if(this.rendered){
20700             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20701                 _this.getActionEl().removeClass(this.disabledClass);
20702                 e.dom.disabled = false;
20703             });
20704         }
20705         
20706         this.disabled = false;
20707         this.fireEvent("enable", this);
20708         return this;
20709     }
20710
20711 });
20712
20713 Roo.apply(Roo.bootstrap.CheckBox, {
20714     
20715     groups: {},
20716     
20717      /**
20718     * register a CheckBox Group
20719     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20720     */
20721     register : function(checkbox)
20722     {
20723         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20724             this.groups[checkbox.groupId] = {};
20725         }
20726         
20727         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20728             return;
20729         }
20730         
20731         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20732         
20733     },
20734     /**
20735     * fetch a CheckBox Group based on the group ID
20736     * @param {string} the group ID
20737     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20738     */
20739     get: function(groupId) {
20740         if (typeof(this.groups[groupId]) == 'undefined') {
20741             return false;
20742         }
20743         
20744         return this.groups[groupId] ;
20745     }
20746     
20747     
20748 });
20749 /*
20750  * - LGPL
20751  *
20752  * RadioItem
20753  * 
20754  */
20755
20756 /**
20757  * @class Roo.bootstrap.Radio
20758  * @extends Roo.bootstrap.Component
20759  * Bootstrap Radio class
20760  * @cfg {String} boxLabel - the label associated
20761  * @cfg {String} value - the value of radio
20762  * 
20763  * @constructor
20764  * Create a new Radio
20765  * @param {Object} config The config object
20766  */
20767 Roo.bootstrap.Radio = function(config){
20768     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20769     
20770 };
20771
20772 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20773     
20774     boxLabel : '',
20775     
20776     value : '',
20777     
20778     getAutoCreate : function()
20779     {
20780         var cfg = {
20781             tag : 'div',
20782             cls : 'form-group radio',
20783             cn : [
20784                 {
20785                     tag : 'label',
20786                     cls : 'box-label',
20787                     html : this.boxLabel
20788                 }
20789             ]
20790         };
20791         
20792         return cfg;
20793     },
20794     
20795     initEvents : function() 
20796     {
20797         this.parent().register(this);
20798         
20799         this.el.on('click', this.onClick, this);
20800         
20801     },
20802     
20803     onClick : function()
20804     {
20805         this.setChecked(true);
20806     },
20807     
20808     setChecked : function(state, suppressEvent)
20809     {
20810         this.parent().setValue(this.value, suppressEvent);
20811         
20812     },
20813     
20814     setBoxLabel : function(v)
20815     {
20816         this.boxLabel = v;
20817         
20818         if(this.rendered){
20819             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20820         }
20821     }
20822     
20823 });
20824  
20825
20826  /*
20827  * - LGPL
20828  *
20829  * Input
20830  * 
20831  */
20832
20833 /**
20834  * @class Roo.bootstrap.SecurePass
20835  * @extends Roo.bootstrap.Input
20836  * Bootstrap SecurePass class
20837  *
20838  * 
20839  * @constructor
20840  * Create a new SecurePass
20841  * @param {Object} config The config object
20842  */
20843  
20844 Roo.bootstrap.SecurePass = function (config) {
20845     // these go here, so the translation tool can replace them..
20846     this.errors = {
20847         PwdEmpty: "Please type a password, and then retype it to confirm.",
20848         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20849         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20850         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20851         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20852         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20853         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20854         TooWeak: "Your password is Too Weak."
20855     },
20856     this.meterLabel = "Password strength:";
20857     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20858     this.meterClass = [
20859         "roo-password-meter-tooweak", 
20860         "roo-password-meter-weak", 
20861         "roo-password-meter-medium", 
20862         "roo-password-meter-strong", 
20863         "roo-password-meter-grey"
20864     ];
20865     
20866     this.errors = {};
20867     
20868     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20869 }
20870
20871 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20872     /**
20873      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20874      * {
20875      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20876      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20877      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20878      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20879      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20880      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20881      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20882      * })
20883      */
20884     // private
20885     
20886     meterWidth: 300,
20887     errorMsg :'',    
20888     errors: false,
20889     imageRoot: '/',
20890     /**
20891      * @cfg {String/Object} Label for the strength meter (defaults to
20892      * 'Password strength:')
20893      */
20894     // private
20895     meterLabel: '',
20896     /**
20897      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20898      * ['Weak', 'Medium', 'Strong'])
20899      */
20900     // private    
20901     pwdStrengths: false,    
20902     // private
20903     strength: 0,
20904     // private
20905     _lastPwd: null,
20906     // private
20907     kCapitalLetter: 0,
20908     kSmallLetter: 1,
20909     kDigit: 2,
20910     kPunctuation: 3,
20911     
20912     insecure: false,
20913     // private
20914     initEvents: function ()
20915     {
20916         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20917
20918         if (this.el.is('input[type=password]') && Roo.isSafari) {
20919             this.el.on('keydown', this.SafariOnKeyDown, this);
20920         }
20921
20922         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20923     },
20924     // private
20925     onRender: function (ct, position)
20926     {
20927         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20928         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20929         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20930
20931         this.trigger.createChild({
20932                    cn: [
20933                     {
20934                     //id: 'PwdMeter',
20935                     tag: 'div',
20936                     cls: 'roo-password-meter-grey col-xs-12',
20937                     style: {
20938                         //width: 0,
20939                         //width: this.meterWidth + 'px'                                                
20940                         }
20941                     },
20942                     {                            
20943                          cls: 'roo-password-meter-text'                          
20944                     }
20945                 ]            
20946         });
20947
20948          
20949         if (this.hideTrigger) {
20950             this.trigger.setDisplayed(false);
20951         }
20952         this.setSize(this.width || '', this.height || '');
20953     },
20954     // private
20955     onDestroy: function ()
20956     {
20957         if (this.trigger) {
20958             this.trigger.removeAllListeners();
20959             this.trigger.remove();
20960         }
20961         if (this.wrap) {
20962             this.wrap.remove();
20963         }
20964         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20965     },
20966     // private
20967     checkStrength: function ()
20968     {
20969         var pwd = this.inputEl().getValue();
20970         if (pwd == this._lastPwd) {
20971             return;
20972         }
20973
20974         var strength;
20975         if (this.ClientSideStrongPassword(pwd)) {
20976             strength = 3;
20977         } else if (this.ClientSideMediumPassword(pwd)) {
20978             strength = 2;
20979         } else if (this.ClientSideWeakPassword(pwd)) {
20980             strength = 1;
20981         } else {
20982             strength = 0;
20983         }
20984         
20985         Roo.log('strength1: ' + strength);
20986         
20987         //var pm = this.trigger.child('div/div/div').dom;
20988         var pm = this.trigger.child('div/div');
20989         pm.removeClass(this.meterClass);
20990         pm.addClass(this.meterClass[strength]);
20991                 
20992         
20993         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20994                 
20995         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20996         
20997         this._lastPwd = pwd;
20998     },
20999     reset: function ()
21000     {
21001         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21002         
21003         this._lastPwd = '';
21004         
21005         var pm = this.trigger.child('div/div');
21006         pm.removeClass(this.meterClass);
21007         pm.addClass('roo-password-meter-grey');        
21008         
21009         
21010         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21011         
21012         pt.innerHTML = '';
21013         this.inputEl().dom.type='password';
21014     },
21015     // private
21016     validateValue: function (value)
21017     {
21018         
21019         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21020             return false;
21021         }
21022         if (value.length == 0) {
21023             if (this.allowBlank) {
21024                 this.clearInvalid();
21025                 return true;
21026             }
21027
21028             this.markInvalid(this.errors.PwdEmpty);
21029             this.errorMsg = this.errors.PwdEmpty;
21030             return false;
21031         }
21032         
21033         if(this.insecure){
21034             return true;
21035         }
21036         
21037         if ('[\x21-\x7e]*'.match(value)) {
21038             this.markInvalid(this.errors.PwdBadChar);
21039             this.errorMsg = this.errors.PwdBadChar;
21040             return false;
21041         }
21042         if (value.length < 6) {
21043             this.markInvalid(this.errors.PwdShort);
21044             this.errorMsg = this.errors.PwdShort;
21045             return false;
21046         }
21047         if (value.length > 16) {
21048             this.markInvalid(this.errors.PwdLong);
21049             this.errorMsg = this.errors.PwdLong;
21050             return false;
21051         }
21052         var strength;
21053         if (this.ClientSideStrongPassword(value)) {
21054             strength = 3;
21055         } else if (this.ClientSideMediumPassword(value)) {
21056             strength = 2;
21057         } else if (this.ClientSideWeakPassword(value)) {
21058             strength = 1;
21059         } else {
21060             strength = 0;
21061         }
21062
21063         
21064         if (strength < 2) {
21065             //this.markInvalid(this.errors.TooWeak);
21066             this.errorMsg = this.errors.TooWeak;
21067             //return false;
21068         }
21069         
21070         
21071         console.log('strength2: ' + strength);
21072         
21073         //var pm = this.trigger.child('div/div/div').dom;
21074         
21075         var pm = this.trigger.child('div/div');
21076         pm.removeClass(this.meterClass);
21077         pm.addClass(this.meterClass[strength]);
21078                 
21079         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21080                 
21081         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21082         
21083         this.errorMsg = ''; 
21084         return true;
21085     },
21086     // private
21087     CharacterSetChecks: function (type)
21088     {
21089         this.type = type;
21090         this.fResult = false;
21091     },
21092     // private
21093     isctype: function (character, type)
21094     {
21095         switch (type) {  
21096             case this.kCapitalLetter:
21097                 if (character >= 'A' && character <= 'Z') {
21098                     return true;
21099                 }
21100                 break;
21101             
21102             case this.kSmallLetter:
21103                 if (character >= 'a' && character <= 'z') {
21104                     return true;
21105                 }
21106                 break;
21107             
21108             case this.kDigit:
21109                 if (character >= '0' && character <= '9') {
21110                     return true;
21111                 }
21112                 break;
21113             
21114             case this.kPunctuation:
21115                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21116                     return true;
21117                 }
21118                 break;
21119             
21120             default:
21121                 return false;
21122         }
21123
21124     },
21125     // private
21126     IsLongEnough: function (pwd, size)
21127     {
21128         return !(pwd == null || isNaN(size) || pwd.length < size);
21129     },
21130     // private
21131     SpansEnoughCharacterSets: function (word, nb)
21132     {
21133         if (!this.IsLongEnough(word, nb))
21134         {
21135             return false;
21136         }
21137
21138         var characterSetChecks = new Array(
21139             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21140             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21141         );
21142         
21143         for (var index = 0; index < word.length; ++index) {
21144             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21145                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21146                     characterSetChecks[nCharSet].fResult = true;
21147                     break;
21148                 }
21149             }
21150         }
21151
21152         var nCharSets = 0;
21153         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21154             if (characterSetChecks[nCharSet].fResult) {
21155                 ++nCharSets;
21156             }
21157         }
21158
21159         if (nCharSets < nb) {
21160             return false;
21161         }
21162         return true;
21163     },
21164     // private
21165     ClientSideStrongPassword: function (pwd)
21166     {
21167         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21168     },
21169     // private
21170     ClientSideMediumPassword: function (pwd)
21171     {
21172         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21173     },
21174     // private
21175     ClientSideWeakPassword: function (pwd)
21176     {
21177         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21178     }
21179           
21180 })//<script type="text/javascript">
21181
21182 /*
21183  * Based  Ext JS Library 1.1.1
21184  * Copyright(c) 2006-2007, Ext JS, LLC.
21185  * LGPL
21186  *
21187  */
21188  
21189 /**
21190  * @class Roo.HtmlEditorCore
21191  * @extends Roo.Component
21192  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21193  *
21194  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21195  */
21196
21197 Roo.HtmlEditorCore = function(config){
21198     
21199     
21200     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21201     
21202     
21203     this.addEvents({
21204         /**
21205          * @event initialize
21206          * Fires when the editor is fully initialized (including the iframe)
21207          * @param {Roo.HtmlEditorCore} this
21208          */
21209         initialize: true,
21210         /**
21211          * @event activate
21212          * Fires when the editor is first receives the focus. Any insertion must wait
21213          * until after this event.
21214          * @param {Roo.HtmlEditorCore} this
21215          */
21216         activate: true,
21217          /**
21218          * @event beforesync
21219          * Fires before the textarea is updated with content from the editor iframe. Return false
21220          * to cancel the sync.
21221          * @param {Roo.HtmlEditorCore} this
21222          * @param {String} html
21223          */
21224         beforesync: true,
21225          /**
21226          * @event beforepush
21227          * Fires before the iframe editor is updated with content from the textarea. Return false
21228          * to cancel the push.
21229          * @param {Roo.HtmlEditorCore} this
21230          * @param {String} html
21231          */
21232         beforepush: true,
21233          /**
21234          * @event sync
21235          * Fires when the textarea is updated with content from the editor iframe.
21236          * @param {Roo.HtmlEditorCore} this
21237          * @param {String} html
21238          */
21239         sync: true,
21240          /**
21241          * @event push
21242          * Fires when the iframe editor is updated with content from the textarea.
21243          * @param {Roo.HtmlEditorCore} this
21244          * @param {String} html
21245          */
21246         push: true,
21247         
21248         /**
21249          * @event editorevent
21250          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21251          * @param {Roo.HtmlEditorCore} this
21252          */
21253         editorevent: true
21254         
21255     });
21256     
21257     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21258     
21259     // defaults : white / black...
21260     this.applyBlacklists();
21261     
21262     
21263     
21264 };
21265
21266
21267 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21268
21269
21270      /**
21271      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21272      */
21273     
21274     owner : false,
21275     
21276      /**
21277      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21278      *                        Roo.resizable.
21279      */
21280     resizable : false,
21281      /**
21282      * @cfg {Number} height (in pixels)
21283      */   
21284     height: 300,
21285    /**
21286      * @cfg {Number} width (in pixels)
21287      */   
21288     width: 500,
21289     
21290     /**
21291      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21292      * 
21293      */
21294     stylesheets: false,
21295     
21296     // id of frame..
21297     frameId: false,
21298     
21299     // private properties
21300     validationEvent : false,
21301     deferHeight: true,
21302     initialized : false,
21303     activated : false,
21304     sourceEditMode : false,
21305     onFocus : Roo.emptyFn,
21306     iframePad:3,
21307     hideMode:'offsets',
21308     
21309     clearUp: true,
21310     
21311     // blacklist + whitelisted elements..
21312     black: false,
21313     white: false,
21314      
21315     bodyCls : '',
21316
21317     /**
21318      * Protected method that will not generally be called directly. It
21319      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21320      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21321      */
21322     getDocMarkup : function(){
21323         // body styles..
21324         var st = '';
21325         
21326         // inherit styels from page...?? 
21327         if (this.stylesheets === false) {
21328             
21329             Roo.get(document.head).select('style').each(function(node) {
21330                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21331             });
21332             
21333             Roo.get(document.head).select('link').each(function(node) { 
21334                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21335             });
21336             
21337         } else if (!this.stylesheets.length) {
21338                 // simple..
21339                 st = '<style type="text/css">' +
21340                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21341                    '</style>';
21342         } else { 
21343             st = '<style type="text/css">' +
21344                     this.stylesheets +
21345                 '</style>';
21346         }
21347         
21348         st +=  '<style type="text/css">' +
21349             'IMG { cursor: pointer } ' +
21350         '</style>';
21351
21352         var cls = 'roo-htmleditor-body';
21353         
21354         if(this.bodyCls.length){
21355             cls += ' ' + this.bodyCls;
21356         }
21357         
21358         return '<html><head>' + st  +
21359             //<style type="text/css">' +
21360             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21361             //'</style>' +
21362             ' </head><body class="' +  cls + '"></body></html>';
21363     },
21364
21365     // private
21366     onRender : function(ct, position)
21367     {
21368         var _t = this;
21369         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21370         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21371         
21372         
21373         this.el.dom.style.border = '0 none';
21374         this.el.dom.setAttribute('tabIndex', -1);
21375         this.el.addClass('x-hidden hide');
21376         
21377         
21378         
21379         if(Roo.isIE){ // fix IE 1px bogus margin
21380             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21381         }
21382        
21383         
21384         this.frameId = Roo.id();
21385         
21386          
21387         
21388         var iframe = this.owner.wrap.createChild({
21389             tag: 'iframe',
21390             cls: 'form-control', // bootstrap..
21391             id: this.frameId,
21392             name: this.frameId,
21393             frameBorder : 'no',
21394             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21395         }, this.el
21396         );
21397         
21398         
21399         this.iframe = iframe.dom;
21400
21401          this.assignDocWin();
21402         
21403         this.doc.designMode = 'on';
21404        
21405         this.doc.open();
21406         this.doc.write(this.getDocMarkup());
21407         this.doc.close();
21408
21409         
21410         var task = { // must defer to wait for browser to be ready
21411             run : function(){
21412                 //console.log("run task?" + this.doc.readyState);
21413                 this.assignDocWin();
21414                 if(this.doc.body || this.doc.readyState == 'complete'){
21415                     try {
21416                         this.doc.designMode="on";
21417                     } catch (e) {
21418                         return;
21419                     }
21420                     Roo.TaskMgr.stop(task);
21421                     this.initEditor.defer(10, this);
21422                 }
21423             },
21424             interval : 10,
21425             duration: 10000,
21426             scope: this
21427         };
21428         Roo.TaskMgr.start(task);
21429
21430     },
21431
21432     // private
21433     onResize : function(w, h)
21434     {
21435          Roo.log('resize: ' +w + ',' + h );
21436         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21437         if(!this.iframe){
21438             return;
21439         }
21440         if(typeof w == 'number'){
21441             
21442             this.iframe.style.width = w + 'px';
21443         }
21444         if(typeof h == 'number'){
21445             
21446             this.iframe.style.height = h + 'px';
21447             if(this.doc){
21448                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21449             }
21450         }
21451         
21452     },
21453
21454     /**
21455      * Toggles the editor between standard and source edit mode.
21456      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21457      */
21458     toggleSourceEdit : function(sourceEditMode){
21459         
21460         this.sourceEditMode = sourceEditMode === true;
21461         
21462         if(this.sourceEditMode){
21463  
21464             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21465             
21466         }else{
21467             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21468             //this.iframe.className = '';
21469             this.deferFocus();
21470         }
21471         //this.setSize(this.owner.wrap.getSize());
21472         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21473     },
21474
21475     
21476   
21477
21478     /**
21479      * Protected method that will not generally be called directly. If you need/want
21480      * custom HTML cleanup, this is the method you should override.
21481      * @param {String} html The HTML to be cleaned
21482      * return {String} The cleaned HTML
21483      */
21484     cleanHtml : function(html){
21485         html = String(html);
21486         if(html.length > 5){
21487             if(Roo.isSafari){ // strip safari nonsense
21488                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21489             }
21490         }
21491         if(html == '&nbsp;'){
21492             html = '';
21493         }
21494         return html;
21495     },
21496
21497     /**
21498      * HTML Editor -> Textarea
21499      * Protected method that will not generally be called directly. Syncs the contents
21500      * of the editor iframe with the textarea.
21501      */
21502     syncValue : function(){
21503         if(this.initialized){
21504             var bd = (this.doc.body || this.doc.documentElement);
21505             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21506             var html = bd.innerHTML;
21507             if(Roo.isSafari){
21508                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21509                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21510                 if(m && m[1]){
21511                     html = '<div style="'+m[0]+'">' + html + '</div>';
21512                 }
21513             }
21514             html = this.cleanHtml(html);
21515             // fix up the special chars.. normaly like back quotes in word...
21516             // however we do not want to do this with chinese..
21517             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21518                 var cc = b.charCodeAt();
21519                 if (
21520                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21521                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21522                     (cc >= 0xf900 && cc < 0xfb00 )
21523                 ) {
21524                         return b;
21525                 }
21526                 return "&#"+cc+";" 
21527             });
21528             if(this.owner.fireEvent('beforesync', this, html) !== false){
21529                 this.el.dom.value = html;
21530                 this.owner.fireEvent('sync', this, html);
21531             }
21532         }
21533     },
21534
21535     /**
21536      * Protected method that will not generally be called directly. Pushes the value of the textarea
21537      * into the iframe editor.
21538      */
21539     pushValue : function(){
21540         if(this.initialized){
21541             var v = this.el.dom.value.trim();
21542             
21543 //            if(v.length < 1){
21544 //                v = '&#160;';
21545 //            }
21546             
21547             if(this.owner.fireEvent('beforepush', this, v) !== false){
21548                 var d = (this.doc.body || this.doc.documentElement);
21549                 d.innerHTML = v;
21550                 this.cleanUpPaste();
21551                 this.el.dom.value = d.innerHTML;
21552                 this.owner.fireEvent('push', this, v);
21553             }
21554         }
21555     },
21556
21557     // private
21558     deferFocus : function(){
21559         this.focus.defer(10, this);
21560     },
21561
21562     // doc'ed in Field
21563     focus : function(){
21564         if(this.win && !this.sourceEditMode){
21565             this.win.focus();
21566         }else{
21567             this.el.focus();
21568         }
21569     },
21570     
21571     assignDocWin: function()
21572     {
21573         var iframe = this.iframe;
21574         
21575          if(Roo.isIE){
21576             this.doc = iframe.contentWindow.document;
21577             this.win = iframe.contentWindow;
21578         } else {
21579 //            if (!Roo.get(this.frameId)) {
21580 //                return;
21581 //            }
21582 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21583 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21584             
21585             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21586                 return;
21587             }
21588             
21589             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21590             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21591         }
21592     },
21593     
21594     // private
21595     initEditor : function(){
21596         //console.log("INIT EDITOR");
21597         this.assignDocWin();
21598         
21599         
21600         
21601         this.doc.designMode="on";
21602         this.doc.open();
21603         this.doc.write(this.getDocMarkup());
21604         this.doc.close();
21605         
21606         var dbody = (this.doc.body || this.doc.documentElement);
21607         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21608         // this copies styles from the containing element into thsi one..
21609         // not sure why we need all of this..
21610         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21611         
21612         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21613         //ss['background-attachment'] = 'fixed'; // w3c
21614         dbody.bgProperties = 'fixed'; // ie
21615         //Roo.DomHelper.applyStyles(dbody, ss);
21616         Roo.EventManager.on(this.doc, {
21617             //'mousedown': this.onEditorEvent,
21618             'mouseup': this.onEditorEvent,
21619             'dblclick': this.onEditorEvent,
21620             'click': this.onEditorEvent,
21621             'keyup': this.onEditorEvent,
21622             buffer:100,
21623             scope: this
21624         });
21625         if(Roo.isGecko){
21626             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21627         }
21628         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21629             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21630         }
21631         this.initialized = true;
21632
21633         this.owner.fireEvent('initialize', this);
21634         this.pushValue();
21635     },
21636
21637     // private
21638     onDestroy : function(){
21639         
21640         
21641         
21642         if(this.rendered){
21643             
21644             //for (var i =0; i < this.toolbars.length;i++) {
21645             //    // fixme - ask toolbars for heights?
21646             //    this.toolbars[i].onDestroy();
21647            // }
21648             
21649             //this.wrap.dom.innerHTML = '';
21650             //this.wrap.remove();
21651         }
21652     },
21653
21654     // private
21655     onFirstFocus : function(){
21656         
21657         this.assignDocWin();
21658         
21659         
21660         this.activated = true;
21661          
21662     
21663         if(Roo.isGecko){ // prevent silly gecko errors
21664             this.win.focus();
21665             var s = this.win.getSelection();
21666             if(!s.focusNode || s.focusNode.nodeType != 3){
21667                 var r = s.getRangeAt(0);
21668                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21669                 r.collapse(true);
21670                 this.deferFocus();
21671             }
21672             try{
21673                 this.execCmd('useCSS', true);
21674                 this.execCmd('styleWithCSS', false);
21675             }catch(e){}
21676         }
21677         this.owner.fireEvent('activate', this);
21678     },
21679
21680     // private
21681     adjustFont: function(btn){
21682         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21683         //if(Roo.isSafari){ // safari
21684         //    adjust *= 2;
21685        // }
21686         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21687         if(Roo.isSafari){ // safari
21688             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21689             v =  (v < 10) ? 10 : v;
21690             v =  (v > 48) ? 48 : v;
21691             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21692             
21693         }
21694         
21695         
21696         v = Math.max(1, v+adjust);
21697         
21698         this.execCmd('FontSize', v  );
21699     },
21700
21701     onEditorEvent : function(e)
21702     {
21703         this.owner.fireEvent('editorevent', this, e);
21704       //  this.updateToolbar();
21705         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21706     },
21707
21708     insertTag : function(tg)
21709     {
21710         // could be a bit smarter... -> wrap the current selected tRoo..
21711         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21712             
21713             range = this.createRange(this.getSelection());
21714             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21715             wrappingNode.appendChild(range.extractContents());
21716             range.insertNode(wrappingNode);
21717
21718             return;
21719             
21720             
21721             
21722         }
21723         this.execCmd("formatblock",   tg);
21724         
21725     },
21726     
21727     insertText : function(txt)
21728     {
21729         
21730         
21731         var range = this.createRange();
21732         range.deleteContents();
21733                //alert(Sender.getAttribute('label'));
21734                
21735         range.insertNode(this.doc.createTextNode(txt));
21736     } ,
21737     
21738      
21739
21740     /**
21741      * Executes a Midas editor command on the editor document and performs necessary focus and
21742      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21743      * @param {String} cmd The Midas command
21744      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21745      */
21746     relayCmd : function(cmd, value){
21747         this.win.focus();
21748         this.execCmd(cmd, value);
21749         this.owner.fireEvent('editorevent', this);
21750         //this.updateToolbar();
21751         this.owner.deferFocus();
21752     },
21753
21754     /**
21755      * Executes a Midas editor command directly on the editor document.
21756      * For visual commands, you should use {@link #relayCmd} instead.
21757      * <b>This should only be called after the editor is initialized.</b>
21758      * @param {String} cmd The Midas command
21759      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21760      */
21761     execCmd : function(cmd, value){
21762         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21763         this.syncValue();
21764     },
21765  
21766  
21767    
21768     /**
21769      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21770      * to insert tRoo.
21771      * @param {String} text | dom node.. 
21772      */
21773     insertAtCursor : function(text)
21774     {
21775         
21776         if(!this.activated){
21777             return;
21778         }
21779         /*
21780         if(Roo.isIE){
21781             this.win.focus();
21782             var r = this.doc.selection.createRange();
21783             if(r){
21784                 r.collapse(true);
21785                 r.pasteHTML(text);
21786                 this.syncValue();
21787                 this.deferFocus();
21788             
21789             }
21790             return;
21791         }
21792         */
21793         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21794             this.win.focus();
21795             
21796             
21797             // from jquery ui (MIT licenced)
21798             var range, node;
21799             var win = this.win;
21800             
21801             if (win.getSelection && win.getSelection().getRangeAt) {
21802                 range = win.getSelection().getRangeAt(0);
21803                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21804                 range.insertNode(node);
21805             } else if (win.document.selection && win.document.selection.createRange) {
21806                 // no firefox support
21807                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21808                 win.document.selection.createRange().pasteHTML(txt);
21809             } else {
21810                 // no firefox support
21811                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21812                 this.execCmd('InsertHTML', txt);
21813             } 
21814             
21815             this.syncValue();
21816             
21817             this.deferFocus();
21818         }
21819     },
21820  // private
21821     mozKeyPress : function(e){
21822         if(e.ctrlKey){
21823             var c = e.getCharCode(), cmd;
21824           
21825             if(c > 0){
21826                 c = String.fromCharCode(c).toLowerCase();
21827                 switch(c){
21828                     case 'b':
21829                         cmd = 'bold';
21830                         break;
21831                     case 'i':
21832                         cmd = 'italic';
21833                         break;
21834                     
21835                     case 'u':
21836                         cmd = 'underline';
21837                         break;
21838                     
21839                     case 'v':
21840                         this.cleanUpPaste.defer(100, this);
21841                         return;
21842                         
21843                 }
21844                 if(cmd){
21845                     this.win.focus();
21846                     this.execCmd(cmd);
21847                     this.deferFocus();
21848                     e.preventDefault();
21849                 }
21850                 
21851             }
21852         }
21853     },
21854
21855     // private
21856     fixKeys : function(){ // load time branching for fastest keydown performance
21857         if(Roo.isIE){
21858             return function(e){
21859                 var k = e.getKey(), r;
21860                 if(k == e.TAB){
21861                     e.stopEvent();
21862                     r = this.doc.selection.createRange();
21863                     if(r){
21864                         r.collapse(true);
21865                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21866                         this.deferFocus();
21867                     }
21868                     return;
21869                 }
21870                 
21871                 if(k == e.ENTER){
21872                     r = this.doc.selection.createRange();
21873                     if(r){
21874                         var target = r.parentElement();
21875                         if(!target || target.tagName.toLowerCase() != 'li'){
21876                             e.stopEvent();
21877                             r.pasteHTML('<br />');
21878                             r.collapse(false);
21879                             r.select();
21880                         }
21881                     }
21882                 }
21883                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21884                     this.cleanUpPaste.defer(100, this);
21885                     return;
21886                 }
21887                 
21888                 
21889             };
21890         }else if(Roo.isOpera){
21891             return function(e){
21892                 var k = e.getKey();
21893                 if(k == e.TAB){
21894                     e.stopEvent();
21895                     this.win.focus();
21896                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21897                     this.deferFocus();
21898                 }
21899                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21900                     this.cleanUpPaste.defer(100, this);
21901                     return;
21902                 }
21903                 
21904             };
21905         }else if(Roo.isSafari){
21906             return function(e){
21907                 var k = e.getKey();
21908                 
21909                 if(k == e.TAB){
21910                     e.stopEvent();
21911                     this.execCmd('InsertText','\t');
21912                     this.deferFocus();
21913                     return;
21914                 }
21915                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21916                     this.cleanUpPaste.defer(100, this);
21917                     return;
21918                 }
21919                 
21920              };
21921         }
21922     }(),
21923     
21924     getAllAncestors: function()
21925     {
21926         var p = this.getSelectedNode();
21927         var a = [];
21928         if (!p) {
21929             a.push(p); // push blank onto stack..
21930             p = this.getParentElement();
21931         }
21932         
21933         
21934         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21935             a.push(p);
21936             p = p.parentNode;
21937         }
21938         a.push(this.doc.body);
21939         return a;
21940     },
21941     lastSel : false,
21942     lastSelNode : false,
21943     
21944     
21945     getSelection : function() 
21946     {
21947         this.assignDocWin();
21948         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21949     },
21950     
21951     getSelectedNode: function() 
21952     {
21953         // this may only work on Gecko!!!
21954         
21955         // should we cache this!!!!
21956         
21957         
21958         
21959          
21960         var range = this.createRange(this.getSelection()).cloneRange();
21961         
21962         if (Roo.isIE) {
21963             var parent = range.parentElement();
21964             while (true) {
21965                 var testRange = range.duplicate();
21966                 testRange.moveToElementText(parent);
21967                 if (testRange.inRange(range)) {
21968                     break;
21969                 }
21970                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21971                     break;
21972                 }
21973                 parent = parent.parentElement;
21974             }
21975             return parent;
21976         }
21977         
21978         // is ancestor a text element.
21979         var ac =  range.commonAncestorContainer;
21980         if (ac.nodeType == 3) {
21981             ac = ac.parentNode;
21982         }
21983         
21984         var ar = ac.childNodes;
21985          
21986         var nodes = [];
21987         var other_nodes = [];
21988         var has_other_nodes = false;
21989         for (var i=0;i<ar.length;i++) {
21990             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21991                 continue;
21992             }
21993             // fullly contained node.
21994             
21995             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21996                 nodes.push(ar[i]);
21997                 continue;
21998             }
21999             
22000             // probably selected..
22001             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22002                 other_nodes.push(ar[i]);
22003                 continue;
22004             }
22005             // outer..
22006             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22007                 continue;
22008             }
22009             
22010             
22011             has_other_nodes = true;
22012         }
22013         if (!nodes.length && other_nodes.length) {
22014             nodes= other_nodes;
22015         }
22016         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22017             return false;
22018         }
22019         
22020         return nodes[0];
22021     },
22022     createRange: function(sel)
22023     {
22024         // this has strange effects when using with 
22025         // top toolbar - not sure if it's a great idea.
22026         //this.editor.contentWindow.focus();
22027         if (typeof sel != "undefined") {
22028             try {
22029                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22030             } catch(e) {
22031                 return this.doc.createRange();
22032             }
22033         } else {
22034             return this.doc.createRange();
22035         }
22036     },
22037     getParentElement: function()
22038     {
22039         
22040         this.assignDocWin();
22041         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22042         
22043         var range = this.createRange(sel);
22044          
22045         try {
22046             var p = range.commonAncestorContainer;
22047             while (p.nodeType == 3) { // text node
22048                 p = p.parentNode;
22049             }
22050             return p;
22051         } catch (e) {
22052             return null;
22053         }
22054     
22055     },
22056     /***
22057      *
22058      * Range intersection.. the hard stuff...
22059      *  '-1' = before
22060      *  '0' = hits..
22061      *  '1' = after.
22062      *         [ -- selected range --- ]
22063      *   [fail]                        [fail]
22064      *
22065      *    basically..
22066      *      if end is before start or  hits it. fail.
22067      *      if start is after end or hits it fail.
22068      *
22069      *   if either hits (but other is outside. - then it's not 
22070      *   
22071      *    
22072      **/
22073     
22074     
22075     // @see http://www.thismuchiknow.co.uk/?p=64.
22076     rangeIntersectsNode : function(range, node)
22077     {
22078         var nodeRange = node.ownerDocument.createRange();
22079         try {
22080             nodeRange.selectNode(node);
22081         } catch (e) {
22082             nodeRange.selectNodeContents(node);
22083         }
22084     
22085         var rangeStartRange = range.cloneRange();
22086         rangeStartRange.collapse(true);
22087     
22088         var rangeEndRange = range.cloneRange();
22089         rangeEndRange.collapse(false);
22090     
22091         var nodeStartRange = nodeRange.cloneRange();
22092         nodeStartRange.collapse(true);
22093     
22094         var nodeEndRange = nodeRange.cloneRange();
22095         nodeEndRange.collapse(false);
22096     
22097         return rangeStartRange.compareBoundaryPoints(
22098                  Range.START_TO_START, nodeEndRange) == -1 &&
22099                rangeEndRange.compareBoundaryPoints(
22100                  Range.START_TO_START, nodeStartRange) == 1;
22101         
22102          
22103     },
22104     rangeCompareNode : function(range, node)
22105     {
22106         var nodeRange = node.ownerDocument.createRange();
22107         try {
22108             nodeRange.selectNode(node);
22109         } catch (e) {
22110             nodeRange.selectNodeContents(node);
22111         }
22112         
22113         
22114         range.collapse(true);
22115     
22116         nodeRange.collapse(true);
22117      
22118         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22119         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22120          
22121         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22122         
22123         var nodeIsBefore   =  ss == 1;
22124         var nodeIsAfter    = ee == -1;
22125         
22126         if (nodeIsBefore && nodeIsAfter) {
22127             return 0; // outer
22128         }
22129         if (!nodeIsBefore && nodeIsAfter) {
22130             return 1; //right trailed.
22131         }
22132         
22133         if (nodeIsBefore && !nodeIsAfter) {
22134             return 2;  // left trailed.
22135         }
22136         // fully contined.
22137         return 3;
22138     },
22139
22140     // private? - in a new class?
22141     cleanUpPaste :  function()
22142     {
22143         // cleans up the whole document..
22144         Roo.log('cleanuppaste');
22145         
22146         this.cleanUpChildren(this.doc.body);
22147         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22148         if (clean != this.doc.body.innerHTML) {
22149             this.doc.body.innerHTML = clean;
22150         }
22151         
22152     },
22153     
22154     cleanWordChars : function(input) {// change the chars to hex code
22155         var he = Roo.HtmlEditorCore;
22156         
22157         var output = input;
22158         Roo.each(he.swapCodes, function(sw) { 
22159             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22160             
22161             output = output.replace(swapper, sw[1]);
22162         });
22163         
22164         return output;
22165     },
22166     
22167     
22168     cleanUpChildren : function (n)
22169     {
22170         if (!n.childNodes.length) {
22171             return;
22172         }
22173         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22174            this.cleanUpChild(n.childNodes[i]);
22175         }
22176     },
22177     
22178     
22179         
22180     
22181     cleanUpChild : function (node)
22182     {
22183         var ed = this;
22184         //console.log(node);
22185         if (node.nodeName == "#text") {
22186             // clean up silly Windows -- stuff?
22187             return; 
22188         }
22189         if (node.nodeName == "#comment") {
22190             node.parentNode.removeChild(node);
22191             // clean up silly Windows -- stuff?
22192             return; 
22193         }
22194         var lcname = node.tagName.toLowerCase();
22195         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22196         // whitelist of tags..
22197         
22198         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22199             // remove node.
22200             node.parentNode.removeChild(node);
22201             return;
22202             
22203         }
22204         
22205         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22206         
22207         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22208         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22209         
22210         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22211         //    remove_keep_children = true;
22212         //}
22213         
22214         if (remove_keep_children) {
22215             this.cleanUpChildren(node);
22216             // inserts everything just before this node...
22217             while (node.childNodes.length) {
22218                 var cn = node.childNodes[0];
22219                 node.removeChild(cn);
22220                 node.parentNode.insertBefore(cn, node);
22221             }
22222             node.parentNode.removeChild(node);
22223             return;
22224         }
22225         
22226         if (!node.attributes || !node.attributes.length) {
22227             this.cleanUpChildren(node);
22228             return;
22229         }
22230         
22231         function cleanAttr(n,v)
22232         {
22233             
22234             if (v.match(/^\./) || v.match(/^\//)) {
22235                 return;
22236             }
22237             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22238                 return;
22239             }
22240             if (v.match(/^#/)) {
22241                 return;
22242             }
22243 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22244             node.removeAttribute(n);
22245             
22246         }
22247         
22248         var cwhite = this.cwhite;
22249         var cblack = this.cblack;
22250             
22251         function cleanStyle(n,v)
22252         {
22253             if (v.match(/expression/)) { //XSS?? should we even bother..
22254                 node.removeAttribute(n);
22255                 return;
22256             }
22257             
22258             var parts = v.split(/;/);
22259             var clean = [];
22260             
22261             Roo.each(parts, function(p) {
22262                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22263                 if (!p.length) {
22264                     return true;
22265                 }
22266                 var l = p.split(':').shift().replace(/\s+/g,'');
22267                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22268                 
22269                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22270 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22271                     //node.removeAttribute(n);
22272                     return true;
22273                 }
22274                 //Roo.log()
22275                 // only allow 'c whitelisted system attributes'
22276                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22277 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22278                     //node.removeAttribute(n);
22279                     return true;
22280                 }
22281                 
22282                 
22283                  
22284                 
22285                 clean.push(p);
22286                 return true;
22287             });
22288             if (clean.length) { 
22289                 node.setAttribute(n, clean.join(';'));
22290             } else {
22291                 node.removeAttribute(n);
22292             }
22293             
22294         }
22295         
22296         
22297         for (var i = node.attributes.length-1; i > -1 ; i--) {
22298             var a = node.attributes[i];
22299             //console.log(a);
22300             
22301             if (a.name.toLowerCase().substr(0,2)=='on')  {
22302                 node.removeAttribute(a.name);
22303                 continue;
22304             }
22305             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22306                 node.removeAttribute(a.name);
22307                 continue;
22308             }
22309             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22310                 cleanAttr(a.name,a.value); // fixme..
22311                 continue;
22312             }
22313             if (a.name == 'style') {
22314                 cleanStyle(a.name,a.value);
22315                 continue;
22316             }
22317             /// clean up MS crap..
22318             // tecnically this should be a list of valid class'es..
22319             
22320             
22321             if (a.name == 'class') {
22322                 if (a.value.match(/^Mso/)) {
22323                     node.className = '';
22324                 }
22325                 
22326                 if (a.value.match(/^body$/)) {
22327                     node.className = '';
22328                 }
22329                 continue;
22330             }
22331             
22332             // style cleanup!?
22333             // class cleanup?
22334             
22335         }
22336         
22337         
22338         this.cleanUpChildren(node);
22339         
22340         
22341     },
22342     
22343     /**
22344      * Clean up MS wordisms...
22345      */
22346     cleanWord : function(node)
22347     {
22348         
22349         
22350         if (!node) {
22351             this.cleanWord(this.doc.body);
22352             return;
22353         }
22354         if (node.nodeName == "#text") {
22355             // clean up silly Windows -- stuff?
22356             return; 
22357         }
22358         if (node.nodeName == "#comment") {
22359             node.parentNode.removeChild(node);
22360             // clean up silly Windows -- stuff?
22361             return; 
22362         }
22363         
22364         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22365             node.parentNode.removeChild(node);
22366             return;
22367         }
22368         
22369         // remove - but keep children..
22370         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22371             while (node.childNodes.length) {
22372                 var cn = node.childNodes[0];
22373                 node.removeChild(cn);
22374                 node.parentNode.insertBefore(cn, node);
22375             }
22376             node.parentNode.removeChild(node);
22377             this.iterateChildren(node, this.cleanWord);
22378             return;
22379         }
22380         // clean styles
22381         if (node.className.length) {
22382             
22383             var cn = node.className.split(/\W+/);
22384             var cna = [];
22385             Roo.each(cn, function(cls) {
22386                 if (cls.match(/Mso[a-zA-Z]+/)) {
22387                     return;
22388                 }
22389                 cna.push(cls);
22390             });
22391             node.className = cna.length ? cna.join(' ') : '';
22392             if (!cna.length) {
22393                 node.removeAttribute("class");
22394             }
22395         }
22396         
22397         if (node.hasAttribute("lang")) {
22398             node.removeAttribute("lang");
22399         }
22400         
22401         if (node.hasAttribute("style")) {
22402             
22403             var styles = node.getAttribute("style").split(";");
22404             var nstyle = [];
22405             Roo.each(styles, function(s) {
22406                 if (!s.match(/:/)) {
22407                     return;
22408                 }
22409                 var kv = s.split(":");
22410                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22411                     return;
22412                 }
22413                 // what ever is left... we allow.
22414                 nstyle.push(s);
22415             });
22416             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22417             if (!nstyle.length) {
22418                 node.removeAttribute('style');
22419             }
22420         }
22421         this.iterateChildren(node, this.cleanWord);
22422         
22423         
22424         
22425     },
22426     /**
22427      * iterateChildren of a Node, calling fn each time, using this as the scole..
22428      * @param {DomNode} node node to iterate children of.
22429      * @param {Function} fn method of this class to call on each item.
22430      */
22431     iterateChildren : function(node, fn)
22432     {
22433         if (!node.childNodes.length) {
22434                 return;
22435         }
22436         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22437            fn.call(this, node.childNodes[i])
22438         }
22439     },
22440     
22441     
22442     /**
22443      * cleanTableWidths.
22444      *
22445      * Quite often pasting from word etc.. results in tables with column and widths.
22446      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22447      *
22448      */
22449     cleanTableWidths : function(node)
22450     {
22451          
22452          
22453         if (!node) {
22454             this.cleanTableWidths(this.doc.body);
22455             return;
22456         }
22457         
22458         // ignore list...
22459         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22460             return; 
22461         }
22462         Roo.log(node.tagName);
22463         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22464             this.iterateChildren(node, this.cleanTableWidths);
22465             return;
22466         }
22467         if (node.hasAttribute('width')) {
22468             node.removeAttribute('width');
22469         }
22470         
22471          
22472         if (node.hasAttribute("style")) {
22473             // pretty basic...
22474             
22475             var styles = node.getAttribute("style").split(";");
22476             var nstyle = [];
22477             Roo.each(styles, function(s) {
22478                 if (!s.match(/:/)) {
22479                     return;
22480                 }
22481                 var kv = s.split(":");
22482                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22483                     return;
22484                 }
22485                 // what ever is left... we allow.
22486                 nstyle.push(s);
22487             });
22488             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22489             if (!nstyle.length) {
22490                 node.removeAttribute('style');
22491             }
22492         }
22493         
22494         this.iterateChildren(node, this.cleanTableWidths);
22495         
22496         
22497     },
22498     
22499     
22500     
22501     
22502     domToHTML : function(currentElement, depth, nopadtext) {
22503         
22504         depth = depth || 0;
22505         nopadtext = nopadtext || false;
22506     
22507         if (!currentElement) {
22508             return this.domToHTML(this.doc.body);
22509         }
22510         
22511         //Roo.log(currentElement);
22512         var j;
22513         var allText = false;
22514         var nodeName = currentElement.nodeName;
22515         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22516         
22517         if  (nodeName == '#text') {
22518             
22519             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22520         }
22521         
22522         
22523         var ret = '';
22524         if (nodeName != 'BODY') {
22525              
22526             var i = 0;
22527             // Prints the node tagName, such as <A>, <IMG>, etc
22528             if (tagName) {
22529                 var attr = [];
22530                 for(i = 0; i < currentElement.attributes.length;i++) {
22531                     // quoting?
22532                     var aname = currentElement.attributes.item(i).name;
22533                     if (!currentElement.attributes.item(i).value.length) {
22534                         continue;
22535                     }
22536                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22537                 }
22538                 
22539                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22540             } 
22541             else {
22542                 
22543                 // eack
22544             }
22545         } else {
22546             tagName = false;
22547         }
22548         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22549             return ret;
22550         }
22551         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22552             nopadtext = true;
22553         }
22554         
22555         
22556         // Traverse the tree
22557         i = 0;
22558         var currentElementChild = currentElement.childNodes.item(i);
22559         var allText = true;
22560         var innerHTML  = '';
22561         lastnode = '';
22562         while (currentElementChild) {
22563             // Formatting code (indent the tree so it looks nice on the screen)
22564             var nopad = nopadtext;
22565             if (lastnode == 'SPAN') {
22566                 nopad  = true;
22567             }
22568             // text
22569             if  (currentElementChild.nodeName == '#text') {
22570                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22571                 toadd = nopadtext ? toadd : toadd.trim();
22572                 if (!nopad && toadd.length > 80) {
22573                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22574                 }
22575                 innerHTML  += toadd;
22576                 
22577                 i++;
22578                 currentElementChild = currentElement.childNodes.item(i);
22579                 lastNode = '';
22580                 continue;
22581             }
22582             allText = false;
22583             
22584             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22585                 
22586             // Recursively traverse the tree structure of the child node
22587             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22588             lastnode = currentElementChild.nodeName;
22589             i++;
22590             currentElementChild=currentElement.childNodes.item(i);
22591         }
22592         
22593         ret += innerHTML;
22594         
22595         if (!allText) {
22596                 // The remaining code is mostly for formatting the tree
22597             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22598         }
22599         
22600         
22601         if (tagName) {
22602             ret+= "</"+tagName+">";
22603         }
22604         return ret;
22605         
22606     },
22607         
22608     applyBlacklists : function()
22609     {
22610         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22611         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22612         
22613         this.white = [];
22614         this.black = [];
22615         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22616             if (b.indexOf(tag) > -1) {
22617                 return;
22618             }
22619             this.white.push(tag);
22620             
22621         }, this);
22622         
22623         Roo.each(w, function(tag) {
22624             if (b.indexOf(tag) > -1) {
22625                 return;
22626             }
22627             if (this.white.indexOf(tag) > -1) {
22628                 return;
22629             }
22630             this.white.push(tag);
22631             
22632         }, this);
22633         
22634         
22635         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22636             if (w.indexOf(tag) > -1) {
22637                 return;
22638             }
22639             this.black.push(tag);
22640             
22641         }, this);
22642         
22643         Roo.each(b, function(tag) {
22644             if (w.indexOf(tag) > -1) {
22645                 return;
22646             }
22647             if (this.black.indexOf(tag) > -1) {
22648                 return;
22649             }
22650             this.black.push(tag);
22651             
22652         }, this);
22653         
22654         
22655         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22656         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22657         
22658         this.cwhite = [];
22659         this.cblack = [];
22660         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22661             if (b.indexOf(tag) > -1) {
22662                 return;
22663             }
22664             this.cwhite.push(tag);
22665             
22666         }, this);
22667         
22668         Roo.each(w, function(tag) {
22669             if (b.indexOf(tag) > -1) {
22670                 return;
22671             }
22672             if (this.cwhite.indexOf(tag) > -1) {
22673                 return;
22674             }
22675             this.cwhite.push(tag);
22676             
22677         }, this);
22678         
22679         
22680         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22681             if (w.indexOf(tag) > -1) {
22682                 return;
22683             }
22684             this.cblack.push(tag);
22685             
22686         }, this);
22687         
22688         Roo.each(b, function(tag) {
22689             if (w.indexOf(tag) > -1) {
22690                 return;
22691             }
22692             if (this.cblack.indexOf(tag) > -1) {
22693                 return;
22694             }
22695             this.cblack.push(tag);
22696             
22697         }, this);
22698     },
22699     
22700     setStylesheets : function(stylesheets)
22701     {
22702         if(typeof(stylesheets) == 'string'){
22703             Roo.get(this.iframe.contentDocument.head).createChild({
22704                 tag : 'link',
22705                 rel : 'stylesheet',
22706                 type : 'text/css',
22707                 href : stylesheets
22708             });
22709             
22710             return;
22711         }
22712         var _this = this;
22713      
22714         Roo.each(stylesheets, function(s) {
22715             if(!s.length){
22716                 return;
22717             }
22718             
22719             Roo.get(_this.iframe.contentDocument.head).createChild({
22720                 tag : 'link',
22721                 rel : 'stylesheet',
22722                 type : 'text/css',
22723                 href : s
22724             });
22725         });
22726
22727         
22728     },
22729     
22730     removeStylesheets : function()
22731     {
22732         var _this = this;
22733         
22734         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22735             s.remove();
22736         });
22737     },
22738     
22739     setStyle : function(style)
22740     {
22741         Roo.get(this.iframe.contentDocument.head).createChild({
22742             tag : 'style',
22743             type : 'text/css',
22744             html : style
22745         });
22746
22747         return;
22748     }
22749     
22750     // hide stuff that is not compatible
22751     /**
22752      * @event blur
22753      * @hide
22754      */
22755     /**
22756      * @event change
22757      * @hide
22758      */
22759     /**
22760      * @event focus
22761      * @hide
22762      */
22763     /**
22764      * @event specialkey
22765      * @hide
22766      */
22767     /**
22768      * @cfg {String} fieldClass @hide
22769      */
22770     /**
22771      * @cfg {String} focusClass @hide
22772      */
22773     /**
22774      * @cfg {String} autoCreate @hide
22775      */
22776     /**
22777      * @cfg {String} inputType @hide
22778      */
22779     /**
22780      * @cfg {String} invalidClass @hide
22781      */
22782     /**
22783      * @cfg {String} invalidText @hide
22784      */
22785     /**
22786      * @cfg {String} msgFx @hide
22787      */
22788     /**
22789      * @cfg {String} validateOnBlur @hide
22790      */
22791 });
22792
22793 Roo.HtmlEditorCore.white = [
22794         'area', 'br', 'img', 'input', 'hr', 'wbr',
22795         
22796        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22797        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22798        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22799        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22800        'table',   'ul',         'xmp', 
22801        
22802        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22803       'thead',   'tr', 
22804      
22805       'dir', 'menu', 'ol', 'ul', 'dl',
22806        
22807       'embed',  'object'
22808 ];
22809
22810
22811 Roo.HtmlEditorCore.black = [
22812     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22813         'applet', // 
22814         'base',   'basefont', 'bgsound', 'blink',  'body', 
22815         'frame',  'frameset', 'head',    'html',   'ilayer', 
22816         'iframe', 'layer',  'link',     'meta',    'object',   
22817         'script', 'style' ,'title',  'xml' // clean later..
22818 ];
22819 Roo.HtmlEditorCore.clean = [
22820     'script', 'style', 'title', 'xml'
22821 ];
22822 Roo.HtmlEditorCore.remove = [
22823     'font'
22824 ];
22825 // attributes..
22826
22827 Roo.HtmlEditorCore.ablack = [
22828     'on'
22829 ];
22830     
22831 Roo.HtmlEditorCore.aclean = [ 
22832     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22833 ];
22834
22835 // protocols..
22836 Roo.HtmlEditorCore.pwhite= [
22837         'http',  'https',  'mailto'
22838 ];
22839
22840 // white listed style attributes.
22841 Roo.HtmlEditorCore.cwhite= [
22842       //  'text-align', /// default is to allow most things..
22843       
22844          
22845 //        'font-size'//??
22846 ];
22847
22848 // black listed style attributes.
22849 Roo.HtmlEditorCore.cblack= [
22850       //  'font-size' -- this can be set by the project 
22851 ];
22852
22853
22854 Roo.HtmlEditorCore.swapCodes   =[ 
22855     [    8211, "--" ], 
22856     [    8212, "--" ], 
22857     [    8216,  "'" ],  
22858     [    8217, "'" ],  
22859     [    8220, '"' ],  
22860     [    8221, '"' ],  
22861     [    8226, "*" ],  
22862     [    8230, "..." ]
22863 ]; 
22864
22865     /*
22866  * - LGPL
22867  *
22868  * HtmlEditor
22869  * 
22870  */
22871
22872 /**
22873  * @class Roo.bootstrap.HtmlEditor
22874  * @extends Roo.bootstrap.TextArea
22875  * Bootstrap HtmlEditor class
22876
22877  * @constructor
22878  * Create a new HtmlEditor
22879  * @param {Object} config The config object
22880  */
22881
22882 Roo.bootstrap.HtmlEditor = function(config){
22883     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22884     if (!this.toolbars) {
22885         this.toolbars = [];
22886     }
22887     
22888     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22889     this.addEvents({
22890             /**
22891              * @event initialize
22892              * Fires when the editor is fully initialized (including the iframe)
22893              * @param {HtmlEditor} this
22894              */
22895             initialize: true,
22896             /**
22897              * @event activate
22898              * Fires when the editor is first receives the focus. Any insertion must wait
22899              * until after this event.
22900              * @param {HtmlEditor} this
22901              */
22902             activate: true,
22903              /**
22904              * @event beforesync
22905              * Fires before the textarea is updated with content from the editor iframe. Return false
22906              * to cancel the sync.
22907              * @param {HtmlEditor} this
22908              * @param {String} html
22909              */
22910             beforesync: true,
22911              /**
22912              * @event beforepush
22913              * Fires before the iframe editor is updated with content from the textarea. Return false
22914              * to cancel the push.
22915              * @param {HtmlEditor} this
22916              * @param {String} html
22917              */
22918             beforepush: true,
22919              /**
22920              * @event sync
22921              * Fires when the textarea is updated with content from the editor iframe.
22922              * @param {HtmlEditor} this
22923              * @param {String} html
22924              */
22925             sync: true,
22926              /**
22927              * @event push
22928              * Fires when the iframe editor is updated with content from the textarea.
22929              * @param {HtmlEditor} this
22930              * @param {String} html
22931              */
22932             push: true,
22933              /**
22934              * @event editmodechange
22935              * Fires when the editor switches edit modes
22936              * @param {HtmlEditor} this
22937              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22938              */
22939             editmodechange: true,
22940             /**
22941              * @event editorevent
22942              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22943              * @param {HtmlEditor} this
22944              */
22945             editorevent: true,
22946             /**
22947              * @event firstfocus
22948              * Fires when on first focus - needed by toolbars..
22949              * @param {HtmlEditor} this
22950              */
22951             firstfocus: true,
22952             /**
22953              * @event autosave
22954              * Auto save the htmlEditor value as a file into Events
22955              * @param {HtmlEditor} this
22956              */
22957             autosave: true,
22958             /**
22959              * @event savedpreview
22960              * preview the saved version of htmlEditor
22961              * @param {HtmlEditor} this
22962              */
22963             savedpreview: true
22964         });
22965 };
22966
22967
22968 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22969     
22970     
22971       /**
22972      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22973      */
22974     toolbars : false,
22975     
22976      /**
22977     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22978     */
22979     btns : [],
22980    
22981      /**
22982      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22983      *                        Roo.resizable.
22984      */
22985     resizable : false,
22986      /**
22987      * @cfg {Number} height (in pixels)
22988      */   
22989     height: 300,
22990    /**
22991      * @cfg {Number} width (in pixels)
22992      */   
22993     width: false,
22994     
22995     /**
22996      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22997      * 
22998      */
22999     stylesheets: false,
23000     
23001     // id of frame..
23002     frameId: false,
23003     
23004     // private properties
23005     validationEvent : false,
23006     deferHeight: true,
23007     initialized : false,
23008     activated : false,
23009     
23010     onFocus : Roo.emptyFn,
23011     iframePad:3,
23012     hideMode:'offsets',
23013     
23014     tbContainer : false,
23015     
23016     bodyCls : '',
23017     
23018     toolbarContainer :function() {
23019         return this.wrap.select('.x-html-editor-tb',true).first();
23020     },
23021
23022     /**
23023      * Protected method that will not generally be called directly. It
23024      * is called when the editor creates its toolbar. Override this method if you need to
23025      * add custom toolbar buttons.
23026      * @param {HtmlEditor} editor
23027      */
23028     createToolbar : function(){
23029         Roo.log('renewing');
23030         Roo.log("create toolbars");
23031         
23032         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23033         this.toolbars[0].render(this.toolbarContainer());
23034         
23035         return;
23036         
23037 //        if (!editor.toolbars || !editor.toolbars.length) {
23038 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23039 //        }
23040 //        
23041 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23042 //            editor.toolbars[i] = Roo.factory(
23043 //                    typeof(editor.toolbars[i]) == 'string' ?
23044 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23045 //                Roo.bootstrap.HtmlEditor);
23046 //            editor.toolbars[i].init(editor);
23047 //        }
23048     },
23049
23050      
23051     // private
23052     onRender : function(ct, position)
23053     {
23054        // Roo.log("Call onRender: " + this.xtype);
23055         var _t = this;
23056         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23057       
23058         this.wrap = this.inputEl().wrap({
23059             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23060         });
23061         
23062         this.editorcore.onRender(ct, position);
23063          
23064         if (this.resizable) {
23065             this.resizeEl = new Roo.Resizable(this.wrap, {
23066                 pinned : true,
23067                 wrap: true,
23068                 dynamic : true,
23069                 minHeight : this.height,
23070                 height: this.height,
23071                 handles : this.resizable,
23072                 width: this.width,
23073                 listeners : {
23074                     resize : function(r, w, h) {
23075                         _t.onResize(w,h); // -something
23076                     }
23077                 }
23078             });
23079             
23080         }
23081         this.createToolbar(this);
23082        
23083         
23084         if(!this.width && this.resizable){
23085             this.setSize(this.wrap.getSize());
23086         }
23087         if (this.resizeEl) {
23088             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23089             // should trigger onReize..
23090         }
23091         
23092     },
23093
23094     // private
23095     onResize : function(w, h)
23096     {
23097         Roo.log('resize: ' +w + ',' + h );
23098         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23099         var ew = false;
23100         var eh = false;
23101         
23102         if(this.inputEl() ){
23103             if(typeof w == 'number'){
23104                 var aw = w - this.wrap.getFrameWidth('lr');
23105                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23106                 ew = aw;
23107             }
23108             if(typeof h == 'number'){
23109                  var tbh = -11;  // fixme it needs to tool bar size!
23110                 for (var i =0; i < this.toolbars.length;i++) {
23111                     // fixme - ask toolbars for heights?
23112                     tbh += this.toolbars[i].el.getHeight();
23113                     //if (this.toolbars[i].footer) {
23114                     //    tbh += this.toolbars[i].footer.el.getHeight();
23115                     //}
23116                 }
23117               
23118                 
23119                 
23120                 
23121                 
23122                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23123                 ah -= 5; // knock a few pixes off for look..
23124                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23125                 var eh = ah;
23126             }
23127         }
23128         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23129         this.editorcore.onResize(ew,eh);
23130         
23131     },
23132
23133     /**
23134      * Toggles the editor between standard and source edit mode.
23135      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23136      */
23137     toggleSourceEdit : function(sourceEditMode)
23138     {
23139         this.editorcore.toggleSourceEdit(sourceEditMode);
23140         
23141         if(this.editorcore.sourceEditMode){
23142             Roo.log('editor - showing textarea');
23143             
23144 //            Roo.log('in');
23145 //            Roo.log(this.syncValue());
23146             this.syncValue();
23147             this.inputEl().removeClass(['hide', 'x-hidden']);
23148             this.inputEl().dom.removeAttribute('tabIndex');
23149             this.inputEl().focus();
23150         }else{
23151             Roo.log('editor - hiding textarea');
23152 //            Roo.log('out')
23153 //            Roo.log(this.pushValue()); 
23154             this.pushValue();
23155             
23156             this.inputEl().addClass(['hide', 'x-hidden']);
23157             this.inputEl().dom.setAttribute('tabIndex', -1);
23158             //this.deferFocus();
23159         }
23160          
23161         if(this.resizable){
23162             this.setSize(this.wrap.getSize());
23163         }
23164         
23165         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23166     },
23167  
23168     // private (for BoxComponent)
23169     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23170
23171     // private (for BoxComponent)
23172     getResizeEl : function(){
23173         return this.wrap;
23174     },
23175
23176     // private (for BoxComponent)
23177     getPositionEl : function(){
23178         return this.wrap;
23179     },
23180
23181     // private
23182     initEvents : function(){
23183         this.originalValue = this.getValue();
23184     },
23185
23186 //    /**
23187 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23188 //     * @method
23189 //     */
23190 //    markInvalid : Roo.emptyFn,
23191 //    /**
23192 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23193 //     * @method
23194 //     */
23195 //    clearInvalid : Roo.emptyFn,
23196
23197     setValue : function(v){
23198         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23199         this.editorcore.pushValue();
23200     },
23201
23202      
23203     // private
23204     deferFocus : function(){
23205         this.focus.defer(10, this);
23206     },
23207
23208     // doc'ed in Field
23209     focus : function(){
23210         this.editorcore.focus();
23211         
23212     },
23213       
23214
23215     // private
23216     onDestroy : function(){
23217         
23218         
23219         
23220         if(this.rendered){
23221             
23222             for (var i =0; i < this.toolbars.length;i++) {
23223                 // fixme - ask toolbars for heights?
23224                 this.toolbars[i].onDestroy();
23225             }
23226             
23227             this.wrap.dom.innerHTML = '';
23228             this.wrap.remove();
23229         }
23230     },
23231
23232     // private
23233     onFirstFocus : function(){
23234         //Roo.log("onFirstFocus");
23235         this.editorcore.onFirstFocus();
23236          for (var i =0; i < this.toolbars.length;i++) {
23237             this.toolbars[i].onFirstFocus();
23238         }
23239         
23240     },
23241     
23242     // private
23243     syncValue : function()
23244     {   
23245         this.editorcore.syncValue();
23246     },
23247     
23248     pushValue : function()
23249     {   
23250         this.editorcore.pushValue();
23251     }
23252      
23253     
23254     // hide stuff that is not compatible
23255     /**
23256      * @event blur
23257      * @hide
23258      */
23259     /**
23260      * @event change
23261      * @hide
23262      */
23263     /**
23264      * @event focus
23265      * @hide
23266      */
23267     /**
23268      * @event specialkey
23269      * @hide
23270      */
23271     /**
23272      * @cfg {String} fieldClass @hide
23273      */
23274     /**
23275      * @cfg {String} focusClass @hide
23276      */
23277     /**
23278      * @cfg {String} autoCreate @hide
23279      */
23280     /**
23281      * @cfg {String} inputType @hide
23282      */
23283     /**
23284      * @cfg {String} invalidClass @hide
23285      */
23286     /**
23287      * @cfg {String} invalidText @hide
23288      */
23289     /**
23290      * @cfg {String} msgFx @hide
23291      */
23292     /**
23293      * @cfg {String} validateOnBlur @hide
23294      */
23295 });
23296  
23297     
23298    
23299    
23300    
23301       
23302 Roo.namespace('Roo.bootstrap.htmleditor');
23303 /**
23304  * @class Roo.bootstrap.HtmlEditorToolbar1
23305  * Basic Toolbar
23306  * 
23307  * Usage:
23308  *
23309  new Roo.bootstrap.HtmlEditor({
23310     ....
23311     toolbars : [
23312         new Roo.bootstrap.HtmlEditorToolbar1({
23313             disable : { fonts: 1 , format: 1, ..., ... , ...],
23314             btns : [ .... ]
23315         })
23316     }
23317      
23318  * 
23319  * @cfg {Object} disable List of elements to disable..
23320  * @cfg {Array} btns List of additional buttons.
23321  * 
23322  * 
23323  * NEEDS Extra CSS? 
23324  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23325  */
23326  
23327 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23328 {
23329     
23330     Roo.apply(this, config);
23331     
23332     // default disabled, based on 'good practice'..
23333     this.disable = this.disable || {};
23334     Roo.applyIf(this.disable, {
23335         fontSize : true,
23336         colors : true,
23337         specialElements : true
23338     });
23339     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23340     
23341     this.editor = config.editor;
23342     this.editorcore = config.editor.editorcore;
23343     
23344     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23345     
23346     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23347     // dont call parent... till later.
23348 }
23349 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23350      
23351     bar : true,
23352     
23353     editor : false,
23354     editorcore : false,
23355     
23356     
23357     formats : [
23358         "p" ,  
23359         "h1","h2","h3","h4","h5","h6", 
23360         "pre", "code", 
23361         "abbr", "acronym", "address", "cite", "samp", "var",
23362         'div','span'
23363     ],
23364     
23365     onRender : function(ct, position)
23366     {
23367        // Roo.log("Call onRender: " + this.xtype);
23368         
23369        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23370        Roo.log(this.el);
23371        this.el.dom.style.marginBottom = '0';
23372        var _this = this;
23373        var editorcore = this.editorcore;
23374        var editor= this.editor;
23375        
23376        var children = [];
23377        var btn = function(id,cmd , toggle, handler, html){
23378        
23379             var  event = toggle ? 'toggle' : 'click';
23380        
23381             var a = {
23382                 size : 'sm',
23383                 xtype: 'Button',
23384                 xns: Roo.bootstrap,
23385                 glyphicon : id,
23386                 cmd : id || cmd,
23387                 enableToggle:toggle !== false,
23388                 html : html || '',
23389                 pressed : toggle ? false : null,
23390                 listeners : {}
23391             };
23392             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23393                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23394             };
23395             children.push(a);
23396             return a;
23397        }
23398        
23399     //    var cb_box = function...
23400         
23401         var style = {
23402                 xtype: 'Button',
23403                 size : 'sm',
23404                 xns: Roo.bootstrap,
23405                 glyphicon : 'font',
23406                 //html : 'submit'
23407                 menu : {
23408                     xtype: 'Menu',
23409                     xns: Roo.bootstrap,
23410                     items:  []
23411                 }
23412         };
23413         Roo.each(this.formats, function(f) {
23414             style.menu.items.push({
23415                 xtype :'MenuItem',
23416                 xns: Roo.bootstrap,
23417                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23418                 tagname : f,
23419                 listeners : {
23420                     click : function()
23421                     {
23422                         editorcore.insertTag(this.tagname);
23423                         editor.focus();
23424                     }
23425                 }
23426                 
23427             });
23428         });
23429         children.push(style);   
23430         
23431         btn('bold',false,true);
23432         btn('italic',false,true);
23433         btn('align-left', 'justifyleft',true);
23434         btn('align-center', 'justifycenter',true);
23435         btn('align-right' , 'justifyright',true);
23436         btn('link', false, false, function(btn) {
23437             //Roo.log("create link?");
23438             var url = prompt(this.createLinkText, this.defaultLinkValue);
23439             if(url && url != 'http:/'+'/'){
23440                 this.editorcore.relayCmd('createlink', url);
23441             }
23442         }),
23443         btn('list','insertunorderedlist',true);
23444         btn('pencil', false,true, function(btn){
23445                 Roo.log(this);
23446                 this.toggleSourceEdit(btn.pressed);
23447         });
23448         
23449         if (this.editor.btns.length > 0) {
23450             for (var i = 0; i<this.editor.btns.length; i++) {
23451                 children.push(this.editor.btns[i]);
23452             }
23453         }
23454         
23455         /*
23456         var cog = {
23457                 xtype: 'Button',
23458                 size : 'sm',
23459                 xns: Roo.bootstrap,
23460                 glyphicon : 'cog',
23461                 //html : 'submit'
23462                 menu : {
23463                     xtype: 'Menu',
23464                     xns: Roo.bootstrap,
23465                     items:  []
23466                 }
23467         };
23468         
23469         cog.menu.items.push({
23470             xtype :'MenuItem',
23471             xns: Roo.bootstrap,
23472             html : Clean styles,
23473             tagname : f,
23474             listeners : {
23475                 click : function()
23476                 {
23477                     editorcore.insertTag(this.tagname);
23478                     editor.focus();
23479                 }
23480             }
23481             
23482         });
23483        */
23484         
23485          
23486        this.xtype = 'NavSimplebar';
23487         
23488         for(var i=0;i< children.length;i++) {
23489             
23490             this.buttons.add(this.addxtypeChild(children[i]));
23491             
23492         }
23493         
23494         editor.on('editorevent', this.updateToolbar, this);
23495     },
23496     onBtnClick : function(id)
23497     {
23498        this.editorcore.relayCmd(id);
23499        this.editorcore.focus();
23500     },
23501     
23502     /**
23503      * Protected method that will not generally be called directly. It triggers
23504      * a toolbar update by reading the markup state of the current selection in the editor.
23505      */
23506     updateToolbar: function(){
23507
23508         if(!this.editorcore.activated){
23509             this.editor.onFirstFocus(); // is this neeed?
23510             return;
23511         }
23512
23513         var btns = this.buttons; 
23514         var doc = this.editorcore.doc;
23515         btns.get('bold').setActive(doc.queryCommandState('bold'));
23516         btns.get('italic').setActive(doc.queryCommandState('italic'));
23517         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23518         
23519         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23520         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23521         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23522         
23523         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23524         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23525          /*
23526         
23527         var ans = this.editorcore.getAllAncestors();
23528         if (this.formatCombo) {
23529             
23530             
23531             var store = this.formatCombo.store;
23532             this.formatCombo.setValue("");
23533             for (var i =0; i < ans.length;i++) {
23534                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23535                     // select it..
23536                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23537                     break;
23538                 }
23539             }
23540         }
23541         
23542         
23543         
23544         // hides menus... - so this cant be on a menu...
23545         Roo.bootstrap.MenuMgr.hideAll();
23546         */
23547         Roo.bootstrap.MenuMgr.hideAll();
23548         //this.editorsyncValue();
23549     },
23550     onFirstFocus: function() {
23551         this.buttons.each(function(item){
23552            item.enable();
23553         });
23554     },
23555     toggleSourceEdit : function(sourceEditMode){
23556         
23557           
23558         if(sourceEditMode){
23559             Roo.log("disabling buttons");
23560            this.buttons.each( function(item){
23561                 if(item.cmd != 'pencil'){
23562                     item.disable();
23563                 }
23564             });
23565           
23566         }else{
23567             Roo.log("enabling buttons");
23568             if(this.editorcore.initialized){
23569                 this.buttons.each( function(item){
23570                     item.enable();
23571                 });
23572             }
23573             
23574         }
23575         Roo.log("calling toggole on editor");
23576         // tell the editor that it's been pressed..
23577         this.editor.toggleSourceEdit(sourceEditMode);
23578        
23579     }
23580 });
23581
23582
23583
23584
23585
23586 /**
23587  * @class Roo.bootstrap.Table.AbstractSelectionModel
23588  * @extends Roo.util.Observable
23589  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23590  * implemented by descendant classes.  This class should not be directly instantiated.
23591  * @constructor
23592  */
23593 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23594     this.locked = false;
23595     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23596 };
23597
23598
23599 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23600     /** @ignore Called by the grid automatically. Do not call directly. */
23601     init : function(grid){
23602         this.grid = grid;
23603         this.initEvents();
23604     },
23605
23606     /**
23607      * Locks the selections.
23608      */
23609     lock : function(){
23610         this.locked = true;
23611     },
23612
23613     /**
23614      * Unlocks the selections.
23615      */
23616     unlock : function(){
23617         this.locked = false;
23618     },
23619
23620     /**
23621      * Returns true if the selections are locked.
23622      * @return {Boolean}
23623      */
23624     isLocked : function(){
23625         return this.locked;
23626     }
23627 });
23628 /**
23629  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23630  * @class Roo.bootstrap.Table.RowSelectionModel
23631  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23632  * It supports multiple selections and keyboard selection/navigation. 
23633  * @constructor
23634  * @param {Object} config
23635  */
23636
23637 Roo.bootstrap.Table.RowSelectionModel = function(config){
23638     Roo.apply(this, config);
23639     this.selections = new Roo.util.MixedCollection(false, function(o){
23640         return o.id;
23641     });
23642
23643     this.last = false;
23644     this.lastActive = false;
23645
23646     this.addEvents({
23647         /**
23648              * @event selectionchange
23649              * Fires when the selection changes
23650              * @param {SelectionModel} this
23651              */
23652             "selectionchange" : true,
23653         /**
23654              * @event afterselectionchange
23655              * Fires after the selection changes (eg. by key press or clicking)
23656              * @param {SelectionModel} this
23657              */
23658             "afterselectionchange" : true,
23659         /**
23660              * @event beforerowselect
23661              * Fires when a row is selected being selected, return false to cancel.
23662              * @param {SelectionModel} this
23663              * @param {Number} rowIndex The selected index
23664              * @param {Boolean} keepExisting False if other selections will be cleared
23665              */
23666             "beforerowselect" : true,
23667         /**
23668              * @event rowselect
23669              * Fires when a row is selected.
23670              * @param {SelectionModel} this
23671              * @param {Number} rowIndex The selected index
23672              * @param {Roo.data.Record} r The record
23673              */
23674             "rowselect" : true,
23675         /**
23676              * @event rowdeselect
23677              * Fires when a row is deselected.
23678              * @param {SelectionModel} this
23679              * @param {Number} rowIndex The selected index
23680              */
23681         "rowdeselect" : true
23682     });
23683     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23684     this.locked = false;
23685  };
23686
23687 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23688     /**
23689      * @cfg {Boolean} singleSelect
23690      * True to allow selection of only one row at a time (defaults to false)
23691      */
23692     singleSelect : false,
23693
23694     // private
23695     initEvents : function()
23696     {
23697
23698         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23699         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23700         //}else{ // allow click to work like normal
23701          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23702         //}
23703         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23704         this.grid.on("rowclick", this.handleMouseDown, this);
23705         
23706         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23707             "up" : function(e){
23708                 if(!e.shiftKey){
23709                     this.selectPrevious(e.shiftKey);
23710                 }else if(this.last !== false && this.lastActive !== false){
23711                     var last = this.last;
23712                     this.selectRange(this.last,  this.lastActive-1);
23713                     this.grid.getView().focusRow(this.lastActive);
23714                     if(last !== false){
23715                         this.last = last;
23716                     }
23717                 }else{
23718                     this.selectFirstRow();
23719                 }
23720                 this.fireEvent("afterselectionchange", this);
23721             },
23722             "down" : function(e){
23723                 if(!e.shiftKey){
23724                     this.selectNext(e.shiftKey);
23725                 }else if(this.last !== false && this.lastActive !== false){
23726                     var last = this.last;
23727                     this.selectRange(this.last,  this.lastActive+1);
23728                     this.grid.getView().focusRow(this.lastActive);
23729                     if(last !== false){
23730                         this.last = last;
23731                     }
23732                 }else{
23733                     this.selectFirstRow();
23734                 }
23735                 this.fireEvent("afterselectionchange", this);
23736             },
23737             scope: this
23738         });
23739         this.grid.store.on('load', function(){
23740             this.selections.clear();
23741         },this);
23742         /*
23743         var view = this.grid.view;
23744         view.on("refresh", this.onRefresh, this);
23745         view.on("rowupdated", this.onRowUpdated, this);
23746         view.on("rowremoved", this.onRemove, this);
23747         */
23748     },
23749
23750     // private
23751     onRefresh : function()
23752     {
23753         var ds = this.grid.store, i, v = this.grid.view;
23754         var s = this.selections;
23755         s.each(function(r){
23756             if((i = ds.indexOfId(r.id)) != -1){
23757                 v.onRowSelect(i);
23758             }else{
23759                 s.remove(r);
23760             }
23761         });
23762     },
23763
23764     // private
23765     onRemove : function(v, index, r){
23766         this.selections.remove(r);
23767     },
23768
23769     // private
23770     onRowUpdated : function(v, index, r){
23771         if(this.isSelected(r)){
23772             v.onRowSelect(index);
23773         }
23774     },
23775
23776     /**
23777      * Select records.
23778      * @param {Array} records The records to select
23779      * @param {Boolean} keepExisting (optional) True to keep existing selections
23780      */
23781     selectRecords : function(records, keepExisting)
23782     {
23783         if(!keepExisting){
23784             this.clearSelections();
23785         }
23786             var ds = this.grid.store;
23787         for(var i = 0, len = records.length; i < len; i++){
23788             this.selectRow(ds.indexOf(records[i]), true);
23789         }
23790     },
23791
23792     /**
23793      * Gets the number of selected rows.
23794      * @return {Number}
23795      */
23796     getCount : function(){
23797         return this.selections.length;
23798     },
23799
23800     /**
23801      * Selects the first row in the grid.
23802      */
23803     selectFirstRow : function(){
23804         this.selectRow(0);
23805     },
23806
23807     /**
23808      * Select the last row.
23809      * @param {Boolean} keepExisting (optional) True to keep existing selections
23810      */
23811     selectLastRow : function(keepExisting){
23812         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23813         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23814     },
23815
23816     /**
23817      * Selects the row immediately following the last selected row.
23818      * @param {Boolean} keepExisting (optional) True to keep existing selections
23819      */
23820     selectNext : function(keepExisting)
23821     {
23822             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23823             this.selectRow(this.last+1, keepExisting);
23824             this.grid.getView().focusRow(this.last);
23825         }
23826     },
23827
23828     /**
23829      * Selects the row that precedes the last selected row.
23830      * @param {Boolean} keepExisting (optional) True to keep existing selections
23831      */
23832     selectPrevious : function(keepExisting){
23833         if(this.last){
23834             this.selectRow(this.last-1, keepExisting);
23835             this.grid.getView().focusRow(this.last);
23836         }
23837     },
23838
23839     /**
23840      * Returns the selected records
23841      * @return {Array} Array of selected records
23842      */
23843     getSelections : function(){
23844         return [].concat(this.selections.items);
23845     },
23846
23847     /**
23848      * Returns the first selected record.
23849      * @return {Record}
23850      */
23851     getSelected : function(){
23852         return this.selections.itemAt(0);
23853     },
23854
23855
23856     /**
23857      * Clears all selections.
23858      */
23859     clearSelections : function(fast)
23860     {
23861         if(this.locked) {
23862             return;
23863         }
23864         if(fast !== true){
23865                 var ds = this.grid.store;
23866             var s = this.selections;
23867             s.each(function(r){
23868                 this.deselectRow(ds.indexOfId(r.id));
23869             }, this);
23870             s.clear();
23871         }else{
23872             this.selections.clear();
23873         }
23874         this.last = false;
23875     },
23876
23877
23878     /**
23879      * Selects all rows.
23880      */
23881     selectAll : function(){
23882         if(this.locked) {
23883             return;
23884         }
23885         this.selections.clear();
23886         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23887             this.selectRow(i, true);
23888         }
23889     },
23890
23891     /**
23892      * Returns True if there is a selection.
23893      * @return {Boolean}
23894      */
23895     hasSelection : function(){
23896         return this.selections.length > 0;
23897     },
23898
23899     /**
23900      * Returns True if the specified row is selected.
23901      * @param {Number/Record} record The record or index of the record to check
23902      * @return {Boolean}
23903      */
23904     isSelected : function(index){
23905             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23906         return (r && this.selections.key(r.id) ? true : false);
23907     },
23908
23909     /**
23910      * Returns True if the specified record id is selected.
23911      * @param {String} id The id of record to check
23912      * @return {Boolean}
23913      */
23914     isIdSelected : function(id){
23915         return (this.selections.key(id) ? true : false);
23916     },
23917
23918
23919     // private
23920     handleMouseDBClick : function(e, t){
23921         
23922     },
23923     // private
23924     handleMouseDown : function(e, t)
23925     {
23926             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23927         if(this.isLocked() || rowIndex < 0 ){
23928             return;
23929         };
23930         if(e.shiftKey && this.last !== false){
23931             var last = this.last;
23932             this.selectRange(last, rowIndex, e.ctrlKey);
23933             this.last = last; // reset the last
23934             t.focus();
23935     
23936         }else{
23937             var isSelected = this.isSelected(rowIndex);
23938             //Roo.log("select row:" + rowIndex);
23939             if(isSelected){
23940                 this.deselectRow(rowIndex);
23941             } else {
23942                         this.selectRow(rowIndex, true);
23943             }
23944     
23945             /*
23946                 if(e.button !== 0 && isSelected){
23947                 alert('rowIndex 2: ' + rowIndex);
23948                     view.focusRow(rowIndex);
23949                 }else if(e.ctrlKey && isSelected){
23950                     this.deselectRow(rowIndex);
23951                 }else if(!isSelected){
23952                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23953                     view.focusRow(rowIndex);
23954                 }
23955             */
23956         }
23957         this.fireEvent("afterselectionchange", this);
23958     },
23959     // private
23960     handleDragableRowClick :  function(grid, rowIndex, e) 
23961     {
23962         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23963             this.selectRow(rowIndex, false);
23964             grid.view.focusRow(rowIndex);
23965              this.fireEvent("afterselectionchange", this);
23966         }
23967     },
23968     
23969     /**
23970      * Selects multiple rows.
23971      * @param {Array} rows Array of the indexes of the row to select
23972      * @param {Boolean} keepExisting (optional) True to keep existing selections
23973      */
23974     selectRows : function(rows, keepExisting){
23975         if(!keepExisting){
23976             this.clearSelections();
23977         }
23978         for(var i = 0, len = rows.length; i < len; i++){
23979             this.selectRow(rows[i], true);
23980         }
23981     },
23982
23983     /**
23984      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23985      * @param {Number} startRow The index of the first row in the range
23986      * @param {Number} endRow The index of the last row in the range
23987      * @param {Boolean} keepExisting (optional) True to retain existing selections
23988      */
23989     selectRange : function(startRow, endRow, keepExisting){
23990         if(this.locked) {
23991             return;
23992         }
23993         if(!keepExisting){
23994             this.clearSelections();
23995         }
23996         if(startRow <= endRow){
23997             for(var i = startRow; i <= endRow; i++){
23998                 this.selectRow(i, true);
23999             }
24000         }else{
24001             for(var i = startRow; i >= endRow; i--){
24002                 this.selectRow(i, true);
24003             }
24004         }
24005     },
24006
24007     /**
24008      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24009      * @param {Number} startRow The index of the first row in the range
24010      * @param {Number} endRow The index of the last row in the range
24011      */
24012     deselectRange : function(startRow, endRow, preventViewNotify){
24013         if(this.locked) {
24014             return;
24015         }
24016         for(var i = startRow; i <= endRow; i++){
24017             this.deselectRow(i, preventViewNotify);
24018         }
24019     },
24020
24021     /**
24022      * Selects a row.
24023      * @param {Number} row The index of the row to select
24024      * @param {Boolean} keepExisting (optional) True to keep existing selections
24025      */
24026     selectRow : function(index, keepExisting, preventViewNotify)
24027     {
24028             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24029             return;
24030         }
24031         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24032             if(!keepExisting || this.singleSelect){
24033                 this.clearSelections();
24034             }
24035             
24036             var r = this.grid.store.getAt(index);
24037             //console.log('selectRow - record id :' + r.id);
24038             
24039             this.selections.add(r);
24040             this.last = this.lastActive = index;
24041             if(!preventViewNotify){
24042                 var proxy = new Roo.Element(
24043                                 this.grid.getRowDom(index)
24044                 );
24045                 proxy.addClass('bg-info info');
24046             }
24047             this.fireEvent("rowselect", this, index, r);
24048             this.fireEvent("selectionchange", this);
24049         }
24050     },
24051
24052     /**
24053      * Deselects a row.
24054      * @param {Number} row The index of the row to deselect
24055      */
24056     deselectRow : function(index, preventViewNotify)
24057     {
24058         if(this.locked) {
24059             return;
24060         }
24061         if(this.last == index){
24062             this.last = false;
24063         }
24064         if(this.lastActive == index){
24065             this.lastActive = false;
24066         }
24067         
24068         var r = this.grid.store.getAt(index);
24069         if (!r) {
24070             return;
24071         }
24072         
24073         this.selections.remove(r);
24074         //.console.log('deselectRow - record id :' + r.id);
24075         if(!preventViewNotify){
24076         
24077             var proxy = new Roo.Element(
24078                 this.grid.getRowDom(index)
24079             );
24080             proxy.removeClass('bg-info info');
24081         }
24082         this.fireEvent("rowdeselect", this, index);
24083         this.fireEvent("selectionchange", this);
24084     },
24085
24086     // private
24087     restoreLast : function(){
24088         if(this._last){
24089             this.last = this._last;
24090         }
24091     },
24092
24093     // private
24094     acceptsNav : function(row, col, cm){
24095         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24096     },
24097
24098     // private
24099     onEditorKey : function(field, e){
24100         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24101         if(k == e.TAB){
24102             e.stopEvent();
24103             ed.completeEdit();
24104             if(e.shiftKey){
24105                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24106             }else{
24107                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24108             }
24109         }else if(k == e.ENTER && !e.ctrlKey){
24110             e.stopEvent();
24111             ed.completeEdit();
24112             if(e.shiftKey){
24113                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24114             }else{
24115                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24116             }
24117         }else if(k == e.ESC){
24118             ed.cancelEdit();
24119         }
24120         if(newCell){
24121             g.startEditing(newCell[0], newCell[1]);
24122         }
24123     }
24124 });
24125 /*
24126  * Based on:
24127  * Ext JS Library 1.1.1
24128  * Copyright(c) 2006-2007, Ext JS, LLC.
24129  *
24130  * Originally Released Under LGPL - original licence link has changed is not relivant.
24131  *
24132  * Fork - LGPL
24133  * <script type="text/javascript">
24134  */
24135  
24136 /**
24137  * @class Roo.bootstrap.PagingToolbar
24138  * @extends Roo.bootstrap.NavSimplebar
24139  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24140  * @constructor
24141  * Create a new PagingToolbar
24142  * @param {Object} config The config object
24143  * @param {Roo.data.Store} store
24144  */
24145 Roo.bootstrap.PagingToolbar = function(config)
24146 {
24147     // old args format still supported... - xtype is prefered..
24148         // created from xtype...
24149     
24150     this.ds = config.dataSource;
24151     
24152     if (config.store && !this.ds) {
24153         this.store= Roo.factory(config.store, Roo.data);
24154         this.ds = this.store;
24155         this.ds.xmodule = this.xmodule || false;
24156     }
24157     
24158     this.toolbarItems = [];
24159     if (config.items) {
24160         this.toolbarItems = config.items;
24161     }
24162     
24163     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24164     
24165     this.cursor = 0;
24166     
24167     if (this.ds) { 
24168         this.bind(this.ds);
24169     }
24170     
24171     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24172     
24173 };
24174
24175 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24176     /**
24177      * @cfg {Roo.data.Store} dataSource
24178      * The underlying data store providing the paged data
24179      */
24180     /**
24181      * @cfg {String/HTMLElement/Element} container
24182      * container The id or element that will contain the toolbar
24183      */
24184     /**
24185      * @cfg {Boolean} displayInfo
24186      * True to display the displayMsg (defaults to false)
24187      */
24188     /**
24189      * @cfg {Number} pageSize
24190      * The number of records to display per page (defaults to 20)
24191      */
24192     pageSize: 20,
24193     /**
24194      * @cfg {String} displayMsg
24195      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24196      */
24197     displayMsg : 'Displaying {0} - {1} of {2}',
24198     /**
24199      * @cfg {String} emptyMsg
24200      * The message to display when no records are found (defaults to "No data to display")
24201      */
24202     emptyMsg : 'No data to display',
24203     /**
24204      * Customizable piece of the default paging text (defaults to "Page")
24205      * @type String
24206      */
24207     beforePageText : "Page",
24208     /**
24209      * Customizable piece of the default paging text (defaults to "of %0")
24210      * @type String
24211      */
24212     afterPageText : "of {0}",
24213     /**
24214      * Customizable piece of the default paging text (defaults to "First Page")
24215      * @type String
24216      */
24217     firstText : "First Page",
24218     /**
24219      * Customizable piece of the default paging text (defaults to "Previous Page")
24220      * @type String
24221      */
24222     prevText : "Previous Page",
24223     /**
24224      * Customizable piece of the default paging text (defaults to "Next Page")
24225      * @type String
24226      */
24227     nextText : "Next Page",
24228     /**
24229      * Customizable piece of the default paging text (defaults to "Last Page")
24230      * @type String
24231      */
24232     lastText : "Last Page",
24233     /**
24234      * Customizable piece of the default paging text (defaults to "Refresh")
24235      * @type String
24236      */
24237     refreshText : "Refresh",
24238
24239     buttons : false,
24240     // private
24241     onRender : function(ct, position) 
24242     {
24243         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24244         this.navgroup.parentId = this.id;
24245         this.navgroup.onRender(this.el, null);
24246         // add the buttons to the navgroup
24247         
24248         if(this.displayInfo){
24249             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24250             this.displayEl = this.el.select('.x-paging-info', true).first();
24251 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24252 //            this.displayEl = navel.el.select('span',true).first();
24253         }
24254         
24255         var _this = this;
24256         
24257         if(this.buttons){
24258             Roo.each(_this.buttons, function(e){ // this might need to use render????
24259                Roo.factory(e).onRender(_this.el, null);
24260             });
24261         }
24262             
24263         Roo.each(_this.toolbarItems, function(e) {
24264             _this.navgroup.addItem(e);
24265         });
24266         
24267         
24268         this.first = this.navgroup.addItem({
24269             tooltip: this.firstText,
24270             cls: "prev",
24271             icon : 'fa fa-backward',
24272             disabled: true,
24273             preventDefault: true,
24274             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24275         });
24276         
24277         this.prev =  this.navgroup.addItem({
24278             tooltip: this.prevText,
24279             cls: "prev",
24280             icon : 'fa fa-step-backward',
24281             disabled: true,
24282             preventDefault: true,
24283             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24284         });
24285     //this.addSeparator();
24286         
24287         
24288         var field = this.navgroup.addItem( {
24289             tagtype : 'span',
24290             cls : 'x-paging-position',
24291             
24292             html : this.beforePageText  +
24293                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24294                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24295          } ); //?? escaped?
24296         
24297         this.field = field.el.select('input', true).first();
24298         this.field.on("keydown", this.onPagingKeydown, this);
24299         this.field.on("focus", function(){this.dom.select();});
24300     
24301     
24302         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24303         //this.field.setHeight(18);
24304         //this.addSeparator();
24305         this.next = this.navgroup.addItem({
24306             tooltip: this.nextText,
24307             cls: "next",
24308             html : ' <i class="fa fa-step-forward">',
24309             disabled: true,
24310             preventDefault: true,
24311             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24312         });
24313         this.last = this.navgroup.addItem({
24314             tooltip: this.lastText,
24315             icon : 'fa fa-forward',
24316             cls: "next",
24317             disabled: true,
24318             preventDefault: true,
24319             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24320         });
24321     //this.addSeparator();
24322         this.loading = this.navgroup.addItem({
24323             tooltip: this.refreshText,
24324             icon: 'fa fa-refresh',
24325             preventDefault: true,
24326             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24327         });
24328         
24329     },
24330
24331     // private
24332     updateInfo : function(){
24333         if(this.displayEl){
24334             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24335             var msg = count == 0 ?
24336                 this.emptyMsg :
24337                 String.format(
24338                     this.displayMsg,
24339                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24340                 );
24341             this.displayEl.update(msg);
24342         }
24343     },
24344
24345     // private
24346     onLoad : function(ds, r, o)
24347     {
24348         this.cursor = o.params ? o.params.start : 0;
24349         var d = this.getPageData(),
24350             ap = d.activePage,
24351             ps = d.pages;
24352         
24353         
24354         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24355         this.field.dom.value = ap;
24356         this.first.setDisabled(ap == 1);
24357         this.prev.setDisabled(ap == 1);
24358         this.next.setDisabled(ap == ps);
24359         this.last.setDisabled(ap == ps);
24360         this.loading.enable();
24361         this.updateInfo();
24362     },
24363
24364     // private
24365     getPageData : function(){
24366         var total = this.ds.getTotalCount();
24367         return {
24368             total : total,
24369             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24370             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24371         };
24372     },
24373
24374     // private
24375     onLoadError : function(){
24376         this.loading.enable();
24377     },
24378
24379     // private
24380     onPagingKeydown : function(e){
24381         var k = e.getKey();
24382         var d = this.getPageData();
24383         if(k == e.RETURN){
24384             var v = this.field.dom.value, pageNum;
24385             if(!v || isNaN(pageNum = parseInt(v, 10))){
24386                 this.field.dom.value = d.activePage;
24387                 return;
24388             }
24389             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24390             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24391             e.stopEvent();
24392         }
24393         else if(k == e.HOME || (k == e.UP && e.ctrlKey) || (k == e.PAGEUP && e.ctrlKey) || (k == e.RIGHT && e.ctrlKey) || k == e.END || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey))
24394         {
24395           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24396           this.field.dom.value = pageNum;
24397           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24398           e.stopEvent();
24399         }
24400         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24401         {
24402           var v = this.field.dom.value, pageNum; 
24403           var increment = (e.shiftKey) ? 10 : 1;
24404           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24405                 increment *= -1;
24406           }
24407           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24408             this.field.dom.value = d.activePage;
24409             return;
24410           }
24411           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24412           {
24413             this.field.dom.value = parseInt(v, 10) + increment;
24414             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24415             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24416           }
24417           e.stopEvent();
24418         }
24419     },
24420
24421     // private
24422     beforeLoad : function(){
24423         if(this.loading){
24424             this.loading.disable();
24425         }
24426     },
24427
24428     // private
24429     onClick : function(which){
24430         
24431         var ds = this.ds;
24432         if (!ds) {
24433             return;
24434         }
24435         
24436         switch(which){
24437             case "first":
24438                 ds.load({params:{start: 0, limit: this.pageSize}});
24439             break;
24440             case "prev":
24441                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24442             break;
24443             case "next":
24444                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24445             break;
24446             case "last":
24447                 var total = ds.getTotalCount();
24448                 var extra = total % this.pageSize;
24449                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24450                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24451             break;
24452             case "refresh":
24453                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24454             break;
24455         }
24456     },
24457
24458     /**
24459      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24460      * @param {Roo.data.Store} store The data store to unbind
24461      */
24462     unbind : function(ds){
24463         ds.un("beforeload", this.beforeLoad, this);
24464         ds.un("load", this.onLoad, this);
24465         ds.un("loadexception", this.onLoadError, this);
24466         ds.un("remove", this.updateInfo, this);
24467         ds.un("add", this.updateInfo, this);
24468         this.ds = undefined;
24469     },
24470
24471     /**
24472      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24473      * @param {Roo.data.Store} store The data store to bind
24474      */
24475     bind : function(ds){
24476         ds.on("beforeload", this.beforeLoad, this);
24477         ds.on("load", this.onLoad, this);
24478         ds.on("loadexception", this.onLoadError, this);
24479         ds.on("remove", this.updateInfo, this);
24480         ds.on("add", this.updateInfo, this);
24481         this.ds = ds;
24482     }
24483 });/*
24484  * - LGPL
24485  *
24486  * element
24487  * 
24488  */
24489
24490 /**
24491  * @class Roo.bootstrap.MessageBar
24492  * @extends Roo.bootstrap.Component
24493  * Bootstrap MessageBar class
24494  * @cfg {String} html contents of the MessageBar
24495  * @cfg {String} weight (info | success | warning | danger) default info
24496  * @cfg {String} beforeClass insert the bar before the given class
24497  * @cfg {Boolean} closable (true | false) default false
24498  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24499  * 
24500  * @constructor
24501  * Create a new Element
24502  * @param {Object} config The config object
24503  */
24504
24505 Roo.bootstrap.MessageBar = function(config){
24506     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24507 };
24508
24509 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24510     
24511     html: '',
24512     weight: 'info',
24513     closable: false,
24514     fixed: false,
24515     beforeClass: 'bootstrap-sticky-wrap',
24516     
24517     getAutoCreate : function(){
24518         
24519         var cfg = {
24520             tag: 'div',
24521             cls: 'alert alert-dismissable alert-' + this.weight,
24522             cn: [
24523                 {
24524                     tag: 'span',
24525                     cls: 'message',
24526                     html: this.html || ''
24527                 }
24528             ]
24529         };
24530         
24531         if(this.fixed){
24532             cfg.cls += ' alert-messages-fixed';
24533         }
24534         
24535         if(this.closable){
24536             cfg.cn.push({
24537                 tag: 'button',
24538                 cls: 'close',
24539                 html: 'x'
24540             });
24541         }
24542         
24543         return cfg;
24544     },
24545     
24546     onRender : function(ct, position)
24547     {
24548         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24549         
24550         if(!this.el){
24551             var cfg = Roo.apply({},  this.getAutoCreate());
24552             cfg.id = Roo.id();
24553             
24554             if (this.cls) {
24555                 cfg.cls += ' ' + this.cls;
24556             }
24557             if (this.style) {
24558                 cfg.style = this.style;
24559             }
24560             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24561             
24562             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24563         }
24564         
24565         this.el.select('>button.close').on('click', this.hide, this);
24566         
24567     },
24568     
24569     show : function()
24570     {
24571         if (!this.rendered) {
24572             this.render();
24573         }
24574         
24575         this.el.show();
24576         
24577         this.fireEvent('show', this);
24578         
24579     },
24580     
24581     hide : function()
24582     {
24583         if (!this.rendered) {
24584             this.render();
24585         }
24586         
24587         this.el.hide();
24588         
24589         this.fireEvent('hide', this);
24590     },
24591     
24592     update : function()
24593     {
24594 //        var e = this.el.dom.firstChild;
24595 //        
24596 //        if(this.closable){
24597 //            e = e.nextSibling;
24598 //        }
24599 //        
24600 //        e.data = this.html || '';
24601
24602         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24603     }
24604    
24605 });
24606
24607  
24608
24609      /*
24610  * - LGPL
24611  *
24612  * Graph
24613  * 
24614  */
24615
24616
24617 /**
24618  * @class Roo.bootstrap.Graph
24619  * @extends Roo.bootstrap.Component
24620  * Bootstrap Graph class
24621 > Prameters
24622  -sm {number} sm 4
24623  -md {number} md 5
24624  @cfg {String} graphtype  bar | vbar | pie
24625  @cfg {number} g_x coodinator | centre x (pie)
24626  @cfg {number} g_y coodinator | centre y (pie)
24627  @cfg {number} g_r radius (pie)
24628  @cfg {number} g_height height of the chart (respected by all elements in the set)
24629  @cfg {number} g_width width of the chart (respected by all elements in the set)
24630  @cfg {Object} title The title of the chart
24631     
24632  -{Array}  values
24633  -opts (object) options for the chart 
24634      o {
24635      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24636      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24637      o vgutter (number)
24638      o colors (array) colors be used repeatedly to plot the bars. If multicolumn bar is used each sequence of bars with use a different color.
24639      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24640      o to
24641      o stretch (boolean)
24642      o }
24643  -opts (object) options for the pie
24644      o{
24645      o cut
24646      o startAngle (number)
24647      o endAngle (number)
24648      } 
24649  *
24650  * @constructor
24651  * Create a new Input
24652  * @param {Object} config The config object
24653  */
24654
24655 Roo.bootstrap.Graph = function(config){
24656     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24657     
24658     this.addEvents({
24659         // img events
24660         /**
24661          * @event click
24662          * The img click event for the img.
24663          * @param {Roo.EventObject} e
24664          */
24665         "click" : true
24666     });
24667 };
24668
24669 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24670     
24671     sm: 4,
24672     md: 5,
24673     graphtype: 'bar',
24674     g_height: 250,
24675     g_width: 400,
24676     g_x: 50,
24677     g_y: 50,
24678     g_r: 30,
24679     opts:{
24680         //g_colors: this.colors,
24681         g_type: 'soft',
24682         g_gutter: '20%'
24683
24684     },
24685     title : false,
24686
24687     getAutoCreate : function(){
24688         
24689         var cfg = {
24690             tag: 'div',
24691             html : null
24692         };
24693         
24694         
24695         return  cfg;
24696     },
24697
24698     onRender : function(ct,position){
24699         
24700         
24701         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24702         
24703         if (typeof(Raphael) == 'undefined') {
24704             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24705             return;
24706         }
24707         
24708         this.raphael = Raphael(this.el.dom);
24709         
24710                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24711                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24712                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24713                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24714                 /*
24715                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24716                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24717                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24718                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24719                 
24720                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24721                 r.barchart(330, 10, 300, 220, data1);
24722                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24723                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24724                 */
24725                 
24726                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24727                 // r.barchart(30, 30, 560, 250,  xdata, {
24728                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24729                 //     axis : "0 0 1 1",
24730                 //     axisxlabels :  xdata
24731                 //     //yvalues : cols,
24732                    
24733                 // });
24734 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24735 //        
24736 //        this.load(null,xdata,{
24737 //                axis : "0 0 1 1",
24738 //                axisxlabels :  xdata
24739 //                });
24740
24741     },
24742
24743     load : function(graphtype,xdata,opts)
24744     {
24745         this.raphael.clear();
24746         if(!graphtype) {
24747             graphtype = this.graphtype;
24748         }
24749         if(!opts){
24750             opts = this.opts;
24751         }
24752         var r = this.raphael,
24753             fin = function () {
24754                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24755             },
24756             fout = function () {
24757                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24758             },
24759             pfin = function() {
24760                 this.sector.stop();
24761                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24762
24763                 if (this.label) {
24764                     this.label[0].stop();
24765                     this.label[0].attr({ r: 7.5 });
24766                     this.label[1].attr({ "font-weight": 800 });
24767                 }
24768             },
24769             pfout = function() {
24770                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24771
24772                 if (this.label) {
24773                     this.label[0].animate({ r: 5 }, 500, "bounce");
24774                     this.label[1].attr({ "font-weight": 400 });
24775                 }
24776             };
24777
24778         switch(graphtype){
24779             case 'bar':
24780                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24781                 break;
24782             case 'hbar':
24783                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24784                 break;
24785             case 'pie':
24786 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24787 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24788 //            
24789                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24790                 
24791                 break;
24792
24793         }
24794         
24795         if(this.title){
24796             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24797         }
24798         
24799     },
24800     
24801     setTitle: function(o)
24802     {
24803         this.title = o;
24804     },
24805     
24806     initEvents: function() {
24807         
24808         if(!this.href){
24809             this.el.on('click', this.onClick, this);
24810         }
24811     },
24812     
24813     onClick : function(e)
24814     {
24815         Roo.log('img onclick');
24816         this.fireEvent('click', this, e);
24817     }
24818    
24819 });
24820
24821  
24822 /*
24823  * - LGPL
24824  *
24825  * numberBox
24826  * 
24827  */
24828 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24829
24830 /**
24831  * @class Roo.bootstrap.dash.NumberBox
24832  * @extends Roo.bootstrap.Component
24833  * Bootstrap NumberBox class
24834  * @cfg {String} headline Box headline
24835  * @cfg {String} content Box content
24836  * @cfg {String} icon Box icon
24837  * @cfg {String} footer Footer text
24838  * @cfg {String} fhref Footer href
24839  * 
24840  * @constructor
24841  * Create a new NumberBox
24842  * @param {Object} config The config object
24843  */
24844
24845
24846 Roo.bootstrap.dash.NumberBox = function(config){
24847     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24848     
24849 };
24850
24851 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24852     
24853     headline : '',
24854     content : '',
24855     icon : '',
24856     footer : '',
24857     fhref : '',
24858     ficon : '',
24859     
24860     getAutoCreate : function(){
24861         
24862         var cfg = {
24863             tag : 'div',
24864             cls : 'small-box ',
24865             cn : [
24866                 {
24867                     tag : 'div',
24868                     cls : 'inner',
24869                     cn :[
24870                         {
24871                             tag : 'h3',
24872                             cls : 'roo-headline',
24873                             html : this.headline
24874                         },
24875                         {
24876                             tag : 'p',
24877                             cls : 'roo-content',
24878                             html : this.content
24879                         }
24880                     ]
24881                 }
24882             ]
24883         };
24884         
24885         if(this.icon){
24886             cfg.cn.push({
24887                 tag : 'div',
24888                 cls : 'icon',
24889                 cn :[
24890                     {
24891                         tag : 'i',
24892                         cls : 'ion ' + this.icon
24893                     }
24894                 ]
24895             });
24896         }
24897         
24898         if(this.footer){
24899             var footer = {
24900                 tag : 'a',
24901                 cls : 'small-box-footer',
24902                 href : this.fhref || '#',
24903                 html : this.footer
24904             };
24905             
24906             cfg.cn.push(footer);
24907             
24908         }
24909         
24910         return  cfg;
24911     },
24912
24913     onRender : function(ct,position){
24914         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24915
24916
24917        
24918                 
24919     },
24920
24921     setHeadline: function (value)
24922     {
24923         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24924     },
24925     
24926     setFooter: function (value, href)
24927     {
24928         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24929         
24930         if(href){
24931             this.el.select('a.small-box-footer',true).first().attr('href', href);
24932         }
24933         
24934     },
24935
24936     setContent: function (value)
24937     {
24938         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24939     },
24940
24941     initEvents: function() 
24942     {   
24943         
24944     }
24945     
24946 });
24947
24948  
24949 /*
24950  * - LGPL
24951  *
24952  * TabBox
24953  * 
24954  */
24955 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24956
24957 /**
24958  * @class Roo.bootstrap.dash.TabBox
24959  * @extends Roo.bootstrap.Component
24960  * Bootstrap TabBox class
24961  * @cfg {String} title Title of the TabBox
24962  * @cfg {String} icon Icon of the TabBox
24963  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24964  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24965  * 
24966  * @constructor
24967  * Create a new TabBox
24968  * @param {Object} config The config object
24969  */
24970
24971
24972 Roo.bootstrap.dash.TabBox = function(config){
24973     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24974     this.addEvents({
24975         // raw events
24976         /**
24977          * @event addpane
24978          * When a pane is added
24979          * @param {Roo.bootstrap.dash.TabPane} pane
24980          */
24981         "addpane" : true,
24982         /**
24983          * @event activatepane
24984          * When a pane is activated
24985          * @param {Roo.bootstrap.dash.TabPane} pane
24986          */
24987         "activatepane" : true
24988         
24989          
24990     });
24991     
24992     this.panes = [];
24993 };
24994
24995 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24996
24997     title : '',
24998     icon : false,
24999     showtabs : true,
25000     tabScrollable : false,
25001     
25002     getChildContainer : function()
25003     {
25004         return this.el.select('.tab-content', true).first();
25005     },
25006     
25007     getAutoCreate : function(){
25008         
25009         var header = {
25010             tag: 'li',
25011             cls: 'pull-left header',
25012             html: this.title,
25013             cn : []
25014         };
25015         
25016         if(this.icon){
25017             header.cn.push({
25018                 tag: 'i',
25019                 cls: 'fa ' + this.icon
25020             });
25021         }
25022         
25023         var h = {
25024             tag: 'ul',
25025             cls: 'nav nav-tabs pull-right',
25026             cn: [
25027                 header
25028             ]
25029         };
25030         
25031         if(this.tabScrollable){
25032             h = {
25033                 tag: 'div',
25034                 cls: 'tab-header',
25035                 cn: [
25036                     {
25037                         tag: 'ul',
25038                         cls: 'nav nav-tabs pull-right',
25039                         cn: [
25040                             header
25041                         ]
25042                     }
25043                 ]
25044             };
25045         }
25046         
25047         var cfg = {
25048             tag: 'div',
25049             cls: 'nav-tabs-custom',
25050             cn: [
25051                 h,
25052                 {
25053                     tag: 'div',
25054                     cls: 'tab-content no-padding',
25055                     cn: []
25056                 }
25057             ]
25058         };
25059
25060         return  cfg;
25061     },
25062     initEvents : function()
25063     {
25064         //Roo.log('add add pane handler');
25065         this.on('addpane', this.onAddPane, this);
25066     },
25067      /**
25068      * Updates the box title
25069      * @param {String} html to set the title to.
25070      */
25071     setTitle : function(value)
25072     {
25073         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25074     },
25075     onAddPane : function(pane)
25076     {
25077         this.panes.push(pane);
25078         //Roo.log('addpane');
25079         //Roo.log(pane);
25080         // tabs are rendere left to right..
25081         if(!this.showtabs){
25082             return;
25083         }
25084         
25085         var ctr = this.el.select('.nav-tabs', true).first();
25086          
25087          
25088         var existing = ctr.select('.nav-tab',true);
25089         var qty = existing.getCount();;
25090         
25091         
25092         var tab = ctr.createChild({
25093             tag : 'li',
25094             cls : 'nav-tab' + (qty ? '' : ' active'),
25095             cn : [
25096                 {
25097                     tag : 'a',
25098                     href:'#',
25099                     html : pane.title
25100                 }
25101             ]
25102         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25103         pane.tab = tab;
25104         
25105         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25106         if (!qty) {
25107             pane.el.addClass('active');
25108         }
25109         
25110                 
25111     },
25112     onTabClick : function(ev,un,ob,pane)
25113     {
25114         //Roo.log('tab - prev default');
25115         ev.preventDefault();
25116         
25117         
25118         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25119         pane.tab.addClass('active');
25120         //Roo.log(pane.title);
25121         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25122         // technically we should have a deactivate event.. but maybe add later.
25123         // and it should not de-activate the selected tab...
25124         this.fireEvent('activatepane', pane);
25125         pane.el.addClass('active');
25126         pane.fireEvent('activate');
25127         
25128         
25129     },
25130     
25131     getActivePane : function()
25132     {
25133         var r = false;
25134         Roo.each(this.panes, function(p) {
25135             if(p.el.hasClass('active')){
25136                 r = p;
25137                 return false;
25138             }
25139             
25140             return;
25141         });
25142         
25143         return r;
25144     }
25145     
25146     
25147 });
25148
25149  
25150 /*
25151  * - LGPL
25152  *
25153  * Tab pane
25154  * 
25155  */
25156 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25157 /**
25158  * @class Roo.bootstrap.TabPane
25159  * @extends Roo.bootstrap.Component
25160  * Bootstrap TabPane class
25161  * @cfg {Boolean} active (false | true) Default false
25162  * @cfg {String} title title of panel
25163
25164  * 
25165  * @constructor
25166  * Create a new TabPane
25167  * @param {Object} config The config object
25168  */
25169
25170 Roo.bootstrap.dash.TabPane = function(config){
25171     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25172     
25173     this.addEvents({
25174         // raw events
25175         /**
25176          * @event activate
25177          * When a pane is activated
25178          * @param {Roo.bootstrap.dash.TabPane} pane
25179          */
25180         "activate" : true
25181          
25182     });
25183 };
25184
25185 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25186     
25187     active : false,
25188     title : '',
25189     
25190     // the tabBox that this is attached to.
25191     tab : false,
25192      
25193     getAutoCreate : function() 
25194     {
25195         var cfg = {
25196             tag: 'div',
25197             cls: 'tab-pane'
25198         };
25199         
25200         if(this.active){
25201             cfg.cls += ' active';
25202         }
25203         
25204         return cfg;
25205     },
25206     initEvents  : function()
25207     {
25208         //Roo.log('trigger add pane handler');
25209         this.parent().fireEvent('addpane', this)
25210     },
25211     
25212      /**
25213      * Updates the tab title 
25214      * @param {String} html to set the title to.
25215      */
25216     setTitle: function(str)
25217     {
25218         if (!this.tab) {
25219             return;
25220         }
25221         this.title = str;
25222         this.tab.select('a', true).first().dom.innerHTML = str;
25223         
25224     }
25225     
25226     
25227     
25228 });
25229
25230  
25231
25232
25233  /*
25234  * - LGPL
25235  *
25236  * menu
25237  * 
25238  */
25239 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25240
25241 /**
25242  * @class Roo.bootstrap.menu.Menu
25243  * @extends Roo.bootstrap.Component
25244  * Bootstrap Menu class - container for Menu
25245  * @cfg {String} html Text of the menu
25246  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25247  * @cfg {String} icon Font awesome icon
25248  * @cfg {String} pos Menu align to (top | bottom) default bottom
25249  * 
25250  * 
25251  * @constructor
25252  * Create a new Menu
25253  * @param {Object} config The config object
25254  */
25255
25256
25257 Roo.bootstrap.menu.Menu = function(config){
25258     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25259     
25260     this.addEvents({
25261         /**
25262          * @event beforeshow
25263          * Fires before this menu is displayed
25264          * @param {Roo.bootstrap.menu.Menu} this
25265          */
25266         beforeshow : true,
25267         /**
25268          * @event beforehide
25269          * Fires before this menu is hidden
25270          * @param {Roo.bootstrap.menu.Menu} this
25271          */
25272         beforehide : true,
25273         /**
25274          * @event show
25275          * Fires after this menu is displayed
25276          * @param {Roo.bootstrap.menu.Menu} this
25277          */
25278         show : true,
25279         /**
25280          * @event hide
25281          * Fires after this menu is hidden
25282          * @param {Roo.bootstrap.menu.Menu} this
25283          */
25284         hide : true,
25285         /**
25286          * @event click
25287          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25288          * @param {Roo.bootstrap.menu.Menu} this
25289          * @param {Roo.EventObject} e
25290          */
25291         click : true
25292     });
25293     
25294 };
25295
25296 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25297     
25298     submenu : false,
25299     html : '',
25300     weight : 'default',
25301     icon : false,
25302     pos : 'bottom',
25303     
25304     
25305     getChildContainer : function() {
25306         if(this.isSubMenu){
25307             return this.el;
25308         }
25309         
25310         return this.el.select('ul.dropdown-menu', true).first();  
25311     },
25312     
25313     getAutoCreate : function()
25314     {
25315         var text = [
25316             {
25317                 tag : 'span',
25318                 cls : 'roo-menu-text',
25319                 html : this.html
25320             }
25321         ];
25322         
25323         if(this.icon){
25324             text.unshift({
25325                 tag : 'i',
25326                 cls : 'fa ' + this.icon
25327             })
25328         }
25329         
25330         
25331         var cfg = {
25332             tag : 'div',
25333             cls : 'btn-group',
25334             cn : [
25335                 {
25336                     tag : 'button',
25337                     cls : 'dropdown-button btn btn-' + this.weight,
25338                     cn : text
25339                 },
25340                 {
25341                     tag : 'button',
25342                     cls : 'dropdown-toggle btn btn-' + this.weight,
25343                     cn : [
25344                         {
25345                             tag : 'span',
25346                             cls : 'caret'
25347                         }
25348                     ]
25349                 },
25350                 {
25351                     tag : 'ul',
25352                     cls : 'dropdown-menu'
25353                 }
25354             ]
25355             
25356         };
25357         
25358         if(this.pos == 'top'){
25359             cfg.cls += ' dropup';
25360         }
25361         
25362         if(this.isSubMenu){
25363             cfg = {
25364                 tag : 'ul',
25365                 cls : 'dropdown-menu'
25366             }
25367         }
25368         
25369         return cfg;
25370     },
25371     
25372     onRender : function(ct, position)
25373     {
25374         this.isSubMenu = ct.hasClass('dropdown-submenu');
25375         
25376         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25377     },
25378     
25379     initEvents : function() 
25380     {
25381         if(this.isSubMenu){
25382             return;
25383         }
25384         
25385         this.hidden = true;
25386         
25387         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25388         this.triggerEl.on('click', this.onTriggerPress, this);
25389         
25390         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25391         this.buttonEl.on('click', this.onClick, this);
25392         
25393     },
25394     
25395     list : function()
25396     {
25397         if(this.isSubMenu){
25398             return this.el;
25399         }
25400         
25401         return this.el.select('ul.dropdown-menu', true).first();
25402     },
25403     
25404     onClick : function(e)
25405     {
25406         this.fireEvent("click", this, e);
25407     },
25408     
25409     onTriggerPress  : function(e)
25410     {   
25411         if (this.isVisible()) {
25412             this.hide();
25413         } else {
25414             this.show();
25415         }
25416     },
25417     
25418     isVisible : function(){
25419         return !this.hidden;
25420     },
25421     
25422     show : function()
25423     {
25424         this.fireEvent("beforeshow", this);
25425         
25426         this.hidden = false;
25427         this.el.addClass('open');
25428         
25429         Roo.get(document).on("mouseup", this.onMouseUp, this);
25430         
25431         this.fireEvent("show", this);
25432         
25433         
25434     },
25435     
25436     hide : function()
25437     {
25438         this.fireEvent("beforehide", this);
25439         
25440         this.hidden = true;
25441         this.el.removeClass('open');
25442         
25443         Roo.get(document).un("mouseup", this.onMouseUp);
25444         
25445         this.fireEvent("hide", this);
25446     },
25447     
25448     onMouseUp : function()
25449     {
25450         this.hide();
25451     }
25452     
25453 });
25454
25455  
25456  /*
25457  * - LGPL
25458  *
25459  * menu item
25460  * 
25461  */
25462 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25463
25464 /**
25465  * @class Roo.bootstrap.menu.Item
25466  * @extends Roo.bootstrap.Component
25467  * Bootstrap MenuItem class
25468  * @cfg {Boolean} submenu (true | false) default false
25469  * @cfg {String} html text of the item
25470  * @cfg {String} href the link
25471  * @cfg {Boolean} disable (true | false) default false
25472  * @cfg {Boolean} preventDefault (true | false) default true
25473  * @cfg {String} icon Font awesome icon
25474  * @cfg {String} pos Submenu align to (left | right) default right 
25475  * 
25476  * 
25477  * @constructor
25478  * Create a new Item
25479  * @param {Object} config The config object
25480  */
25481
25482
25483 Roo.bootstrap.menu.Item = function(config){
25484     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25485     this.addEvents({
25486         /**
25487          * @event mouseover
25488          * Fires when the mouse is hovering over this menu
25489          * @param {Roo.bootstrap.menu.Item} this
25490          * @param {Roo.EventObject} e
25491          */
25492         mouseover : true,
25493         /**
25494          * @event mouseout
25495          * Fires when the mouse exits this menu
25496          * @param {Roo.bootstrap.menu.Item} this
25497          * @param {Roo.EventObject} e
25498          */
25499         mouseout : true,
25500         // raw events
25501         /**
25502          * @event click
25503          * The raw click event for the entire grid.
25504          * @param {Roo.EventObject} e
25505          */
25506         click : true
25507     });
25508 };
25509
25510 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25511     
25512     submenu : false,
25513     href : '',
25514     html : '',
25515     preventDefault: true,
25516     disable : false,
25517     icon : false,
25518     pos : 'right',
25519     
25520     getAutoCreate : function()
25521     {
25522         var text = [
25523             {
25524                 tag : 'span',
25525                 cls : 'roo-menu-item-text',
25526                 html : this.html
25527             }
25528         ];
25529         
25530         if(this.icon){
25531             text.unshift({
25532                 tag : 'i',
25533                 cls : 'fa ' + this.icon
25534             })
25535         }
25536         
25537         var cfg = {
25538             tag : 'li',
25539             cn : [
25540                 {
25541                     tag : 'a',
25542                     href : this.href || '#',
25543                     cn : text
25544                 }
25545             ]
25546         };
25547         
25548         if(this.disable){
25549             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25550         }
25551         
25552         if(this.submenu){
25553             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25554             
25555             if(this.pos == 'left'){
25556                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25557             }
25558         }
25559         
25560         return cfg;
25561     },
25562     
25563     initEvents : function() 
25564     {
25565         this.el.on('mouseover', this.onMouseOver, this);
25566         this.el.on('mouseout', this.onMouseOut, this);
25567         
25568         this.el.select('a', true).first().on('click', this.onClick, this);
25569         
25570     },
25571     
25572     onClick : function(e)
25573     {
25574         if(this.preventDefault){
25575             e.preventDefault();
25576         }
25577         
25578         this.fireEvent("click", this, e);
25579     },
25580     
25581     onMouseOver : function(e)
25582     {
25583         if(this.submenu && this.pos == 'left'){
25584             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25585         }
25586         
25587         this.fireEvent("mouseover", this, e);
25588     },
25589     
25590     onMouseOut : function(e)
25591     {
25592         this.fireEvent("mouseout", this, e);
25593     }
25594 });
25595
25596  
25597
25598  /*
25599  * - LGPL
25600  *
25601  * menu separator
25602  * 
25603  */
25604 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25605
25606 /**
25607  * @class Roo.bootstrap.menu.Separator
25608  * @extends Roo.bootstrap.Component
25609  * Bootstrap Separator class
25610  * 
25611  * @constructor
25612  * Create a new Separator
25613  * @param {Object} config The config object
25614  */
25615
25616
25617 Roo.bootstrap.menu.Separator = function(config){
25618     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25619 };
25620
25621 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25622     
25623     getAutoCreate : function(){
25624         var cfg = {
25625             tag : 'li',
25626             cls: 'divider'
25627         };
25628         
25629         return cfg;
25630     }
25631    
25632 });
25633
25634  
25635
25636  /*
25637  * - LGPL
25638  *
25639  * Tooltip
25640  * 
25641  */
25642
25643 /**
25644  * @class Roo.bootstrap.Tooltip
25645  * Bootstrap Tooltip class
25646  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25647  * to determine which dom element triggers the tooltip.
25648  * 
25649  * It needs to add support for additional attributes like tooltip-position
25650  * 
25651  * @constructor
25652  * Create a new Toolti
25653  * @param {Object} config The config object
25654  */
25655
25656 Roo.bootstrap.Tooltip = function(config){
25657     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25658     
25659     this.alignment = Roo.bootstrap.Tooltip.alignment;
25660     
25661     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25662         this.alignment = config.alignment;
25663     }
25664     
25665 };
25666
25667 Roo.apply(Roo.bootstrap.Tooltip, {
25668     /**
25669      * @function init initialize tooltip monitoring.
25670      * @static
25671      */
25672     currentEl : false,
25673     currentTip : false,
25674     currentRegion : false,
25675     
25676     //  init : delay?
25677     
25678     init : function()
25679     {
25680         Roo.get(document).on('mouseover', this.enter ,this);
25681         Roo.get(document).on('mouseout', this.leave, this);
25682          
25683         
25684         this.currentTip = new Roo.bootstrap.Tooltip();
25685     },
25686     
25687     enter : function(ev)
25688     {
25689         var dom = ev.getTarget();
25690         
25691         //Roo.log(['enter',dom]);
25692         var el = Roo.fly(dom);
25693         if (this.currentEl) {
25694             //Roo.log(dom);
25695             //Roo.log(this.currentEl);
25696             //Roo.log(this.currentEl.contains(dom));
25697             if (this.currentEl == el) {
25698                 return;
25699             }
25700             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25701                 return;
25702             }
25703
25704         }
25705         
25706         if (this.currentTip.el) {
25707             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25708         }    
25709         //Roo.log(ev);
25710         
25711         if(!el || el.dom == document){
25712             return;
25713         }
25714         
25715         var bindEl = el;
25716         
25717         // you can not look for children, as if el is the body.. then everythign is the child..
25718         if (!el.attr('tooltip')) { //
25719             if (!el.select("[tooltip]").elements.length) {
25720                 return;
25721             }
25722             // is the mouse over this child...?
25723             bindEl = el.select("[tooltip]").first();
25724             var xy = ev.getXY();
25725             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25726                 //Roo.log("not in region.");
25727                 return;
25728             }
25729             //Roo.log("child element over..");
25730             
25731         }
25732         this.currentEl = bindEl;
25733         this.currentTip.bind(bindEl);
25734         this.currentRegion = Roo.lib.Region.getRegion(dom);
25735         this.currentTip.enter();
25736         
25737     },
25738     leave : function(ev)
25739     {
25740         var dom = ev.getTarget();
25741         //Roo.log(['leave',dom]);
25742         if (!this.currentEl) {
25743             return;
25744         }
25745         
25746         
25747         if (dom != this.currentEl.dom) {
25748             return;
25749         }
25750         var xy = ev.getXY();
25751         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25752             return;
25753         }
25754         // only activate leave if mouse cursor is outside... bounding box..
25755         
25756         
25757         
25758         
25759         if (this.currentTip) {
25760             this.currentTip.leave();
25761         }
25762         //Roo.log('clear currentEl');
25763         this.currentEl = false;
25764         
25765         
25766     },
25767     alignment : {
25768         'left' : ['r-l', [-2,0], 'right'],
25769         'right' : ['l-r', [2,0], 'left'],
25770         'bottom' : ['t-b', [0,2], 'top'],
25771         'top' : [ 'b-t', [0,-2], 'bottom']
25772     }
25773     
25774 });
25775
25776
25777 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25778     
25779     
25780     bindEl : false,
25781     
25782     delay : null, // can be { show : 300 , hide: 500}
25783     
25784     timeout : null,
25785     
25786     hoverState : null, //???
25787     
25788     placement : 'bottom', 
25789     
25790     alignment : false,
25791     
25792     getAutoCreate : function(){
25793     
25794         var cfg = {
25795            cls : 'tooltip',
25796            role : 'tooltip',
25797            cn : [
25798                 {
25799                     cls : 'tooltip-arrow'
25800                 },
25801                 {
25802                     cls : 'tooltip-inner'
25803                 }
25804            ]
25805         };
25806         
25807         return cfg;
25808     },
25809     bind : function(el)
25810     {
25811         this.bindEl = el;
25812     },
25813       
25814     
25815     enter : function () {
25816        
25817         if (this.timeout != null) {
25818             clearTimeout(this.timeout);
25819         }
25820         
25821         this.hoverState = 'in';
25822          //Roo.log("enter - show");
25823         if (!this.delay || !this.delay.show) {
25824             this.show();
25825             return;
25826         }
25827         var _t = this;
25828         this.timeout = setTimeout(function () {
25829             if (_t.hoverState == 'in') {
25830                 _t.show();
25831             }
25832         }, this.delay.show);
25833     },
25834     leave : function()
25835     {
25836         clearTimeout(this.timeout);
25837     
25838         this.hoverState = 'out';
25839          if (!this.delay || !this.delay.hide) {
25840             this.hide();
25841             return;
25842         }
25843        
25844         var _t = this;
25845         this.timeout = setTimeout(function () {
25846             //Roo.log("leave - timeout");
25847             
25848             if (_t.hoverState == 'out') {
25849                 _t.hide();
25850                 Roo.bootstrap.Tooltip.currentEl = false;
25851             }
25852         }, delay);
25853     },
25854     
25855     show : function (msg)
25856     {
25857         if (!this.el) {
25858             this.render(document.body);
25859         }
25860         // set content.
25861         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25862         
25863         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25864         
25865         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25866         
25867         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25868         
25869         var placement = typeof this.placement == 'function' ?
25870             this.placement.call(this, this.el, on_el) :
25871             this.placement;
25872             
25873         var autoToken = /\s?auto?\s?/i;
25874         var autoPlace = autoToken.test(placement);
25875         if (autoPlace) {
25876             placement = placement.replace(autoToken, '') || 'top';
25877         }
25878         
25879         //this.el.detach()
25880         //this.el.setXY([0,0]);
25881         this.el.show();
25882         //this.el.dom.style.display='block';
25883         
25884         //this.el.appendTo(on_el);
25885         
25886         var p = this.getPosition();
25887         var box = this.el.getBox();
25888         
25889         if (autoPlace) {
25890             // fixme..
25891         }
25892         
25893         var align = this.alignment[placement];
25894         
25895         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25896         
25897         if(placement == 'top' || placement == 'bottom'){
25898             if(xy[0] < 0){
25899                 placement = 'right';
25900             }
25901             
25902             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25903                 placement = 'left';
25904             }
25905             
25906             var scroll = Roo.select('body', true).first().getScroll();
25907             
25908             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25909                 placement = 'top';
25910             }
25911             
25912         }
25913         
25914         this.el.alignTo(this.bindEl, align[0],align[1]);
25915         //var arrow = this.el.select('.arrow',true).first();
25916         //arrow.set(align[2], 
25917         
25918         this.el.addClass(placement);
25919         
25920         this.el.addClass('in fade');
25921         
25922         this.hoverState = null;
25923         
25924         if (this.el.hasClass('fade')) {
25925             // fade it?
25926         }
25927         
25928     },
25929     hide : function()
25930     {
25931          
25932         if (!this.el) {
25933             return;
25934         }
25935         //this.el.setXY([0,0]);
25936         this.el.removeClass('in');
25937         //this.el.hide();
25938         
25939     }
25940     
25941 });
25942  
25943
25944  /*
25945  * - LGPL
25946  *
25947  * Location Picker
25948  * 
25949  */
25950
25951 /**
25952  * @class Roo.bootstrap.LocationPicker
25953  * @extends Roo.bootstrap.Component
25954  * Bootstrap LocationPicker class
25955  * @cfg {Number} latitude Position when init default 0
25956  * @cfg {Number} longitude Position when init default 0
25957  * @cfg {Number} zoom default 15
25958  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25959  * @cfg {Boolean} mapTypeControl default false
25960  * @cfg {Boolean} disableDoubleClickZoom default false
25961  * @cfg {Boolean} scrollwheel default true
25962  * @cfg {Boolean} streetViewControl default false
25963  * @cfg {Number} radius default 0
25964  * @cfg {String} locationName
25965  * @cfg {Boolean} draggable default true
25966  * @cfg {Boolean} enableAutocomplete default false
25967  * @cfg {Boolean} enableReverseGeocode default true
25968  * @cfg {String} markerTitle
25969  * 
25970  * @constructor
25971  * Create a new LocationPicker
25972  * @param {Object} config The config object
25973  */
25974
25975
25976 Roo.bootstrap.LocationPicker = function(config){
25977     
25978     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25979     
25980     this.addEvents({
25981         /**
25982          * @event initial
25983          * Fires when the picker initialized.
25984          * @param {Roo.bootstrap.LocationPicker} this
25985          * @param {Google Location} location
25986          */
25987         initial : true,
25988         /**
25989          * @event positionchanged
25990          * Fires when the picker position changed.
25991          * @param {Roo.bootstrap.LocationPicker} this
25992          * @param {Google Location} location
25993          */
25994         positionchanged : true,
25995         /**
25996          * @event resize
25997          * Fires when the map resize.
25998          * @param {Roo.bootstrap.LocationPicker} this
25999          */
26000         resize : true,
26001         /**
26002          * @event show
26003          * Fires when the map show.
26004          * @param {Roo.bootstrap.LocationPicker} this
26005          */
26006         show : true,
26007         /**
26008          * @event hide
26009          * Fires when the map hide.
26010          * @param {Roo.bootstrap.LocationPicker} this
26011          */
26012         hide : true,
26013         /**
26014          * @event mapClick
26015          * Fires when click the map.
26016          * @param {Roo.bootstrap.LocationPicker} this
26017          * @param {Map event} e
26018          */
26019         mapClick : true,
26020         /**
26021          * @event mapRightClick
26022          * Fires when right click the map.
26023          * @param {Roo.bootstrap.LocationPicker} this
26024          * @param {Map event} e
26025          */
26026         mapRightClick : true,
26027         /**
26028          * @event markerClick
26029          * Fires when click the marker.
26030          * @param {Roo.bootstrap.LocationPicker} this
26031          * @param {Map event} e
26032          */
26033         markerClick : true,
26034         /**
26035          * @event markerRightClick
26036          * Fires when right click the marker.
26037          * @param {Roo.bootstrap.LocationPicker} this
26038          * @param {Map event} e
26039          */
26040         markerRightClick : true,
26041         /**
26042          * @event OverlayViewDraw
26043          * Fires when OverlayView Draw
26044          * @param {Roo.bootstrap.LocationPicker} this
26045          */
26046         OverlayViewDraw : true,
26047         /**
26048          * @event OverlayViewOnAdd
26049          * Fires when OverlayView Draw
26050          * @param {Roo.bootstrap.LocationPicker} this
26051          */
26052         OverlayViewOnAdd : true,
26053         /**
26054          * @event OverlayViewOnRemove
26055          * Fires when OverlayView Draw
26056          * @param {Roo.bootstrap.LocationPicker} this
26057          */
26058         OverlayViewOnRemove : true,
26059         /**
26060          * @event OverlayViewShow
26061          * Fires when OverlayView Draw
26062          * @param {Roo.bootstrap.LocationPicker} this
26063          * @param {Pixel} cpx
26064          */
26065         OverlayViewShow : true,
26066         /**
26067          * @event OverlayViewHide
26068          * Fires when OverlayView Draw
26069          * @param {Roo.bootstrap.LocationPicker} this
26070          */
26071         OverlayViewHide : true,
26072         /**
26073          * @event loadexception
26074          * Fires when load google lib failed.
26075          * @param {Roo.bootstrap.LocationPicker} this
26076          */
26077         loadexception : true
26078     });
26079         
26080 };
26081
26082 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26083     
26084     gMapContext: false,
26085     
26086     latitude: 0,
26087     longitude: 0,
26088     zoom: 15,
26089     mapTypeId: false,
26090     mapTypeControl: false,
26091     disableDoubleClickZoom: false,
26092     scrollwheel: true,
26093     streetViewControl: false,
26094     radius: 0,
26095     locationName: '',
26096     draggable: true,
26097     enableAutocomplete: false,
26098     enableReverseGeocode: true,
26099     markerTitle: '',
26100     
26101     getAutoCreate: function()
26102     {
26103
26104         var cfg = {
26105             tag: 'div',
26106             cls: 'roo-location-picker'
26107         };
26108         
26109         return cfg
26110     },
26111     
26112     initEvents: function(ct, position)
26113     {       
26114         if(!this.el.getWidth() || this.isApplied()){
26115             return;
26116         }
26117         
26118         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26119         
26120         this.initial();
26121     },
26122     
26123     initial: function()
26124     {
26125         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26126             this.fireEvent('loadexception', this);
26127             return;
26128         }
26129         
26130         if(!this.mapTypeId){
26131             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26132         }
26133         
26134         this.gMapContext = this.GMapContext();
26135         
26136         this.initOverlayView();
26137         
26138         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26139         
26140         var _this = this;
26141                 
26142         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26143             _this.setPosition(_this.gMapContext.marker.position);
26144         });
26145         
26146         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26147             _this.fireEvent('mapClick', this, event);
26148             
26149         });
26150
26151         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26152             _this.fireEvent('mapRightClick', this, event);
26153             
26154         });
26155         
26156         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26157             _this.fireEvent('markerClick', this, event);
26158             
26159         });
26160
26161         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26162             _this.fireEvent('markerRightClick', this, event);
26163             
26164         });
26165         
26166         this.setPosition(this.gMapContext.location);
26167         
26168         this.fireEvent('initial', this, this.gMapContext.location);
26169     },
26170     
26171     initOverlayView: function()
26172     {
26173         var _this = this;
26174         
26175         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26176             
26177             draw: function()
26178             {
26179                 _this.fireEvent('OverlayViewDraw', _this);
26180             },
26181             
26182             onAdd: function()
26183             {
26184                 _this.fireEvent('OverlayViewOnAdd', _this);
26185             },
26186             
26187             onRemove: function()
26188             {
26189                 _this.fireEvent('OverlayViewOnRemove', _this);
26190             },
26191             
26192             show: function(cpx)
26193             {
26194                 _this.fireEvent('OverlayViewShow', _this, cpx);
26195             },
26196             
26197             hide: function()
26198             {
26199                 _this.fireEvent('OverlayViewHide', _this);
26200             }
26201             
26202         });
26203     },
26204     
26205     fromLatLngToContainerPixel: function(event)
26206     {
26207         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26208     },
26209     
26210     isApplied: function() 
26211     {
26212         return this.getGmapContext() == false ? false : true;
26213     },
26214     
26215     getGmapContext: function() 
26216     {
26217         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26218     },
26219     
26220     GMapContext: function() 
26221     {
26222         var position = new google.maps.LatLng(this.latitude, this.longitude);
26223         
26224         var _map = new google.maps.Map(this.el.dom, {
26225             center: position,
26226             zoom: this.zoom,
26227             mapTypeId: this.mapTypeId,
26228             mapTypeControl: this.mapTypeControl,
26229             disableDoubleClickZoom: this.disableDoubleClickZoom,
26230             scrollwheel: this.scrollwheel,
26231             streetViewControl: this.streetViewControl,
26232             locationName: this.locationName,
26233             draggable: this.draggable,
26234             enableAutocomplete: this.enableAutocomplete,
26235             enableReverseGeocode: this.enableReverseGeocode
26236         });
26237         
26238         var _marker = new google.maps.Marker({
26239             position: position,
26240             map: _map,
26241             title: this.markerTitle,
26242             draggable: this.draggable
26243         });
26244         
26245         return {
26246             map: _map,
26247             marker: _marker,
26248             circle: null,
26249             location: position,
26250             radius: this.radius,
26251             locationName: this.locationName,
26252             addressComponents: {
26253                 formatted_address: null,
26254                 addressLine1: null,
26255                 addressLine2: null,
26256                 streetName: null,
26257                 streetNumber: null,
26258                 city: null,
26259                 district: null,
26260                 state: null,
26261                 stateOrProvince: null
26262             },
26263             settings: this,
26264             domContainer: this.el.dom,
26265             geodecoder: new google.maps.Geocoder()
26266         };
26267     },
26268     
26269     drawCircle: function(center, radius, options) 
26270     {
26271         if (this.gMapContext.circle != null) {
26272             this.gMapContext.circle.setMap(null);
26273         }
26274         if (radius > 0) {
26275             radius *= 1;
26276             options = Roo.apply({}, options, {
26277                 strokeColor: "#0000FF",
26278                 strokeOpacity: .35,
26279                 strokeWeight: 2,
26280                 fillColor: "#0000FF",
26281                 fillOpacity: .2
26282             });
26283             
26284             options.map = this.gMapContext.map;
26285             options.radius = radius;
26286             options.center = center;
26287             this.gMapContext.circle = new google.maps.Circle(options);
26288             return this.gMapContext.circle;
26289         }
26290         
26291         return null;
26292     },
26293     
26294     setPosition: function(location) 
26295     {
26296         this.gMapContext.location = location;
26297         this.gMapContext.marker.setPosition(location);
26298         this.gMapContext.map.panTo(location);
26299         this.drawCircle(location, this.gMapContext.radius, {});
26300         
26301         var _this = this;
26302         
26303         if (this.gMapContext.settings.enableReverseGeocode) {
26304             this.gMapContext.geodecoder.geocode({
26305                 latLng: this.gMapContext.location
26306             }, function(results, status) {
26307                 
26308                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26309                     _this.gMapContext.locationName = results[0].formatted_address;
26310                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26311                     
26312                     _this.fireEvent('positionchanged', this, location);
26313                 }
26314             });
26315             
26316             return;
26317         }
26318         
26319         this.fireEvent('positionchanged', this, location);
26320     },
26321     
26322     resize: function()
26323     {
26324         google.maps.event.trigger(this.gMapContext.map, "resize");
26325         
26326         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26327         
26328         this.fireEvent('resize', this);
26329     },
26330     
26331     setPositionByLatLng: function(latitude, longitude)
26332     {
26333         this.setPosition(new google.maps.LatLng(latitude, longitude));
26334     },
26335     
26336     getCurrentPosition: function() 
26337     {
26338         return {
26339             latitude: this.gMapContext.location.lat(),
26340             longitude: this.gMapContext.location.lng()
26341         };
26342     },
26343     
26344     getAddressName: function() 
26345     {
26346         return this.gMapContext.locationName;
26347     },
26348     
26349     getAddressComponents: function() 
26350     {
26351         return this.gMapContext.addressComponents;
26352     },
26353     
26354     address_component_from_google_geocode: function(address_components) 
26355     {
26356         var result = {};
26357         
26358         for (var i = 0; i < address_components.length; i++) {
26359             var component = address_components[i];
26360             if (component.types.indexOf("postal_code") >= 0) {
26361                 result.postalCode = component.short_name;
26362             } else if (component.types.indexOf("street_number") >= 0) {
26363                 result.streetNumber = component.short_name;
26364             } else if (component.types.indexOf("route") >= 0) {
26365                 result.streetName = component.short_name;
26366             } else if (component.types.indexOf("neighborhood") >= 0) {
26367                 result.city = component.short_name;
26368             } else if (component.types.indexOf("locality") >= 0) {
26369                 result.city = component.short_name;
26370             } else if (component.types.indexOf("sublocality") >= 0) {
26371                 result.district = component.short_name;
26372             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26373                 result.stateOrProvince = component.short_name;
26374             } else if (component.types.indexOf("country") >= 0) {
26375                 result.country = component.short_name;
26376             }
26377         }
26378         
26379         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26380         result.addressLine2 = "";
26381         return result;
26382     },
26383     
26384     setZoomLevel: function(zoom)
26385     {
26386         this.gMapContext.map.setZoom(zoom);
26387     },
26388     
26389     show: function()
26390     {
26391         if(!this.el){
26392             return;
26393         }
26394         
26395         this.el.show();
26396         
26397         this.resize();
26398         
26399         this.fireEvent('show', this);
26400     },
26401     
26402     hide: function()
26403     {
26404         if(!this.el){
26405             return;
26406         }
26407         
26408         this.el.hide();
26409         
26410         this.fireEvent('hide', this);
26411     }
26412     
26413 });
26414
26415 Roo.apply(Roo.bootstrap.LocationPicker, {
26416     
26417     OverlayView : function(map, options)
26418     {
26419         options = options || {};
26420         
26421         this.setMap(map);
26422     }
26423     
26424     
26425 });/*
26426  * - LGPL
26427  *
26428  * Alert
26429  * 
26430  */
26431
26432 /**
26433  * @class Roo.bootstrap.Alert
26434  * @extends Roo.bootstrap.Component
26435  * Bootstrap Alert class
26436  * @cfg {String} title The title of alert
26437  * @cfg {String} html The content of alert
26438  * @cfg {String} weight (  success | info | warning | danger )
26439  * @cfg {String} faicon font-awesomeicon
26440  * 
26441  * @constructor
26442  * Create a new alert
26443  * @param {Object} config The config object
26444  */
26445
26446
26447 Roo.bootstrap.Alert = function(config){
26448     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26449     
26450 };
26451
26452 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26453     
26454     title: '',
26455     html: '',
26456     weight: false,
26457     faicon: false,
26458     
26459     getAutoCreate : function()
26460     {
26461         
26462         var cfg = {
26463             tag : 'div',
26464             cls : 'alert',
26465             cn : [
26466                 {
26467                     tag : 'i',
26468                     cls : 'roo-alert-icon'
26469                     
26470                 },
26471                 {
26472                     tag : 'b',
26473                     cls : 'roo-alert-title',
26474                     html : this.title
26475                 },
26476                 {
26477                     tag : 'span',
26478                     cls : 'roo-alert-text',
26479                     html : this.html
26480                 }
26481             ]
26482         };
26483         
26484         if(this.faicon){
26485             cfg.cn[0].cls += ' fa ' + this.faicon;
26486         }
26487         
26488         if(this.weight){
26489             cfg.cls += ' alert-' + this.weight;
26490         }
26491         
26492         return cfg;
26493     },
26494     
26495     initEvents: function() 
26496     {
26497         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26498     },
26499     
26500     setTitle : function(str)
26501     {
26502         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26503     },
26504     
26505     setText : function(str)
26506     {
26507         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26508     },
26509     
26510     setWeight : function(weight)
26511     {
26512         if(this.weight){
26513             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26514         }
26515         
26516         this.weight = weight;
26517         
26518         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26519     },
26520     
26521     setIcon : function(icon)
26522     {
26523         if(this.faicon){
26524             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26525         }
26526         
26527         this.faicon = icon;
26528         
26529         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26530     },
26531     
26532     hide: function() 
26533     {
26534         this.el.hide();   
26535     },
26536     
26537     show: function() 
26538     {  
26539         this.el.show();   
26540     }
26541     
26542 });
26543
26544  
26545 /*
26546 * Licence: LGPL
26547 */
26548
26549 /**
26550  * @class Roo.bootstrap.UploadCropbox
26551  * @extends Roo.bootstrap.Component
26552  * Bootstrap UploadCropbox class
26553  * @cfg {String} emptyText show when image has been loaded
26554  * @cfg {String} rotateNotify show when image too small to rotate
26555  * @cfg {Number} errorTimeout default 3000
26556  * @cfg {Number} minWidth default 300
26557  * @cfg {Number} minHeight default 300
26558  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26559  * @cfg {Boolean} isDocument (true|false) default false
26560  * @cfg {String} url action url
26561  * @cfg {String} paramName default 'imageUpload'
26562  * @cfg {String} method default POST
26563  * @cfg {Boolean} loadMask (true|false) default true
26564  * @cfg {Boolean} loadingText default 'Loading...'
26565  * 
26566  * @constructor
26567  * Create a new UploadCropbox
26568  * @param {Object} config The config object
26569  */
26570
26571 Roo.bootstrap.UploadCropbox = function(config){
26572     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26573     
26574     this.addEvents({
26575         /**
26576          * @event beforeselectfile
26577          * Fire before select file
26578          * @param {Roo.bootstrap.UploadCropbox} this
26579          */
26580         "beforeselectfile" : true,
26581         /**
26582          * @event initial
26583          * Fire after initEvent
26584          * @param {Roo.bootstrap.UploadCropbox} this
26585          */
26586         "initial" : true,
26587         /**
26588          * @event crop
26589          * Fire after initEvent
26590          * @param {Roo.bootstrap.UploadCropbox} this
26591          * @param {String} data
26592          */
26593         "crop" : true,
26594         /**
26595          * @event prepare
26596          * Fire when preparing the file data
26597          * @param {Roo.bootstrap.UploadCropbox} this
26598          * @param {Object} file
26599          */
26600         "prepare" : true,
26601         /**
26602          * @event exception
26603          * Fire when get exception
26604          * @param {Roo.bootstrap.UploadCropbox} this
26605          * @param {XMLHttpRequest} xhr
26606          */
26607         "exception" : true,
26608         /**
26609          * @event beforeloadcanvas
26610          * Fire before load the canvas
26611          * @param {Roo.bootstrap.UploadCropbox} this
26612          * @param {String} src
26613          */
26614         "beforeloadcanvas" : true,
26615         /**
26616          * @event trash
26617          * Fire when trash image
26618          * @param {Roo.bootstrap.UploadCropbox} this
26619          */
26620         "trash" : true,
26621         /**
26622          * @event download
26623          * Fire when download the image
26624          * @param {Roo.bootstrap.UploadCropbox} this
26625          */
26626         "download" : true,
26627         /**
26628          * @event footerbuttonclick
26629          * Fire when footerbuttonclick
26630          * @param {Roo.bootstrap.UploadCropbox} this
26631          * @param {String} type
26632          */
26633         "footerbuttonclick" : true,
26634         /**
26635          * @event resize
26636          * Fire when resize
26637          * @param {Roo.bootstrap.UploadCropbox} this
26638          */
26639         "resize" : true,
26640         /**
26641          * @event rotate
26642          * Fire when rotate the image
26643          * @param {Roo.bootstrap.UploadCropbox} this
26644          * @param {String} pos
26645          */
26646         "rotate" : true,
26647         /**
26648          * @event inspect
26649          * Fire when inspect the file
26650          * @param {Roo.bootstrap.UploadCropbox} this
26651          * @param {Object} file
26652          */
26653         "inspect" : true,
26654         /**
26655          * @event upload
26656          * Fire when xhr upload the file
26657          * @param {Roo.bootstrap.UploadCropbox} this
26658          * @param {Object} data
26659          */
26660         "upload" : true,
26661         /**
26662          * @event arrange
26663          * Fire when arrange the file data
26664          * @param {Roo.bootstrap.UploadCropbox} this
26665          * @param {Object} formData
26666          */
26667         "arrange" : true
26668     });
26669     
26670     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26671 };
26672
26673 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26674     
26675     emptyText : 'Click to upload image',
26676     rotateNotify : 'Image is too small to rotate',
26677     errorTimeout : 3000,
26678     scale : 0,
26679     baseScale : 1,
26680     rotate : 0,
26681     dragable : false,
26682     pinching : false,
26683     mouseX : 0,
26684     mouseY : 0,
26685     cropData : false,
26686     minWidth : 300,
26687     minHeight : 300,
26688     file : false,
26689     exif : {},
26690     baseRotate : 1,
26691     cropType : 'image/jpeg',
26692     buttons : false,
26693     canvasLoaded : false,
26694     isDocument : false,
26695     method : 'POST',
26696     paramName : 'imageUpload',
26697     loadMask : true,
26698     loadingText : 'Loading...',
26699     maskEl : false,
26700     
26701     getAutoCreate : function()
26702     {
26703         var cfg = {
26704             tag : 'div',
26705             cls : 'roo-upload-cropbox',
26706             cn : [
26707                 {
26708                     tag : 'input',
26709                     cls : 'roo-upload-cropbox-selector',
26710                     type : 'file'
26711                 },
26712                 {
26713                     tag : 'div',
26714                     cls : 'roo-upload-cropbox-body',
26715                     style : 'cursor:pointer',
26716                     cn : [
26717                         {
26718                             tag : 'div',
26719                             cls : 'roo-upload-cropbox-preview'
26720                         },
26721                         {
26722                             tag : 'div',
26723                             cls : 'roo-upload-cropbox-thumb'
26724                         },
26725                         {
26726                             tag : 'div',
26727                             cls : 'roo-upload-cropbox-empty-notify',
26728                             html : this.emptyText
26729                         },
26730                         {
26731                             tag : 'div',
26732                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26733                             html : this.rotateNotify
26734                         }
26735                     ]
26736                 },
26737                 {
26738                     tag : 'div',
26739                     cls : 'roo-upload-cropbox-footer',
26740                     cn : {
26741                         tag : 'div',
26742                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26743                         cn : []
26744                     }
26745                 }
26746             ]
26747         };
26748         
26749         return cfg;
26750     },
26751     
26752     onRender : function(ct, position)
26753     {
26754         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26755         
26756         if (this.buttons.length) {
26757             
26758             Roo.each(this.buttons, function(bb) {
26759                 
26760                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26761                 
26762                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26763                 
26764             }, this);
26765         }
26766         
26767         if(this.loadMask){
26768             this.maskEl = this.el;
26769         }
26770     },
26771     
26772     initEvents : function()
26773     {
26774         this.urlAPI = (window.createObjectURL && window) || 
26775                                 (window.URL && URL.revokeObjectURL && URL) || 
26776                                 (window.webkitURL && webkitURL);
26777                         
26778         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26779         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26780         
26781         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26782         this.selectorEl.hide();
26783         
26784         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26785         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26786         
26787         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26788         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26789         this.thumbEl.hide();
26790         
26791         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26792         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26793         
26794         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26795         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26796         this.errorEl.hide();
26797         
26798         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26799         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26800         this.footerEl.hide();
26801         
26802         this.setThumbBoxSize();
26803         
26804         this.bind();
26805         
26806         this.resize();
26807         
26808         this.fireEvent('initial', this);
26809     },
26810
26811     bind : function()
26812     {
26813         var _this = this;
26814         
26815         window.addEventListener("resize", function() { _this.resize(); } );
26816         
26817         this.bodyEl.on('click', this.beforeSelectFile, this);
26818         
26819         if(Roo.isTouch){
26820             this.bodyEl.on('touchstart', this.onTouchStart, this);
26821             this.bodyEl.on('touchmove', this.onTouchMove, this);
26822             this.bodyEl.on('touchend', this.onTouchEnd, this);
26823         }
26824         
26825         if(!Roo.isTouch){
26826             this.bodyEl.on('mousedown', this.onMouseDown, this);
26827             this.bodyEl.on('mousemove', this.onMouseMove, this);
26828             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26829             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26830             Roo.get(document).on('mouseup', this.onMouseUp, this);
26831         }
26832         
26833         this.selectorEl.on('change', this.onFileSelected, this);
26834     },
26835     
26836     reset : function()
26837     {    
26838         this.scale = 0;
26839         this.baseScale = 1;
26840         this.rotate = 0;
26841         this.baseRotate = 1;
26842         this.dragable = false;
26843         this.pinching = false;
26844         this.mouseX = 0;
26845         this.mouseY = 0;
26846         this.cropData = false;
26847         this.notifyEl.dom.innerHTML = this.emptyText;
26848         
26849         this.selectorEl.dom.value = '';
26850         
26851     },
26852     
26853     resize : function()
26854     {
26855         if(this.fireEvent('resize', this) != false){
26856             this.setThumbBoxPosition();
26857             this.setCanvasPosition();
26858         }
26859     },
26860     
26861     onFooterButtonClick : function(e, el, o, type)
26862     {
26863         switch (type) {
26864             case 'rotate-left' :
26865                 this.onRotateLeft(e);
26866                 break;
26867             case 'rotate-right' :
26868                 this.onRotateRight(e);
26869                 break;
26870             case 'picture' :
26871                 this.beforeSelectFile(e);
26872                 break;
26873             case 'trash' :
26874                 this.trash(e);
26875                 break;
26876             case 'crop' :
26877                 this.crop(e);
26878                 break;
26879             case 'download' :
26880                 this.download(e);
26881                 break;
26882             default :
26883                 break;
26884         }
26885         
26886         this.fireEvent('footerbuttonclick', this, type);
26887     },
26888     
26889     beforeSelectFile : function(e)
26890     {
26891         e.preventDefault();
26892         
26893         if(this.fireEvent('beforeselectfile', this) != false){
26894             this.selectorEl.dom.click();
26895         }
26896     },
26897     
26898     onFileSelected : function(e)
26899     {
26900         e.preventDefault();
26901         
26902         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26903             return;
26904         }
26905         
26906         var file = this.selectorEl.dom.files[0];
26907         
26908         if(this.fireEvent('inspect', this, file) != false){
26909             this.prepare(file);
26910         }
26911         
26912     },
26913     
26914     trash : function(e)
26915     {
26916         this.fireEvent('trash', this);
26917     },
26918     
26919     download : function(e)
26920     {
26921         this.fireEvent('download', this);
26922     },
26923     
26924     loadCanvas : function(src)
26925     {   
26926         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26927             
26928             this.reset();
26929             
26930             this.imageEl = document.createElement('img');
26931             
26932             var _this = this;
26933             
26934             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26935             
26936             this.imageEl.src = src;
26937         }
26938     },
26939     
26940     onLoadCanvas : function()
26941     {   
26942         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26943         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26944         
26945         this.bodyEl.un('click', this.beforeSelectFile, this);
26946         
26947         this.notifyEl.hide();
26948         this.thumbEl.show();
26949         this.footerEl.show();
26950         
26951         this.baseRotateLevel();
26952         
26953         if(this.isDocument){
26954             this.setThumbBoxSize();
26955         }
26956         
26957         this.setThumbBoxPosition();
26958         
26959         this.baseScaleLevel();
26960         
26961         this.draw();
26962         
26963         this.resize();
26964         
26965         this.canvasLoaded = true;
26966         
26967         if(this.loadMask){
26968             this.maskEl.unmask();
26969         }
26970         
26971     },
26972     
26973     setCanvasPosition : function()
26974     {   
26975         if(!this.canvasEl){
26976             return;
26977         }
26978         
26979         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26980         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26981         
26982         this.previewEl.setLeft(pw);
26983         this.previewEl.setTop(ph);
26984         
26985     },
26986     
26987     onMouseDown : function(e)
26988     {   
26989         e.stopEvent();
26990         
26991         this.dragable = true;
26992         this.pinching = false;
26993         
26994         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26995             this.dragable = false;
26996             return;
26997         }
26998         
26999         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27000         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27001         
27002     },
27003     
27004     onMouseMove : function(e)
27005     {   
27006         e.stopEvent();
27007         
27008         if(!this.canvasLoaded){
27009             return;
27010         }
27011         
27012         if (!this.dragable){
27013             return;
27014         }
27015         
27016         var minX = Math.ceil(this.thumbEl.getLeft(true));
27017         var minY = Math.ceil(this.thumbEl.getTop(true));
27018         
27019         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27020         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27021         
27022         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27023         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27024         
27025         x = x - this.mouseX;
27026         y = y - this.mouseY;
27027         
27028         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27029         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27030         
27031         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27032         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27033         
27034         this.previewEl.setLeft(bgX);
27035         this.previewEl.setTop(bgY);
27036         
27037         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27038         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27039     },
27040     
27041     onMouseUp : function(e)
27042     {   
27043         e.stopEvent();
27044         
27045         this.dragable = false;
27046     },
27047     
27048     onMouseWheel : function(e)
27049     {   
27050         e.stopEvent();
27051         
27052         this.startScale = this.scale;
27053         
27054         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27055         
27056         if(!this.zoomable()){
27057             this.scale = this.startScale;
27058             return;
27059         }
27060         
27061         this.draw();
27062         
27063         return;
27064     },
27065     
27066     zoomable : function()
27067     {
27068         var minScale = this.thumbEl.getWidth() / this.minWidth;
27069         
27070         if(this.minWidth < this.minHeight){
27071             minScale = this.thumbEl.getHeight() / this.minHeight;
27072         }
27073         
27074         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27075         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27076         
27077         if(
27078                 this.isDocument &&
27079                 (this.rotate == 0 || this.rotate == 180) && 
27080                 (
27081                     width > this.imageEl.OriginWidth || 
27082                     height > this.imageEl.OriginHeight ||
27083                     (width < this.minWidth && height < this.minHeight)
27084                 )
27085         ){
27086             return false;
27087         }
27088         
27089         if(
27090                 this.isDocument &&
27091                 (this.rotate == 90 || this.rotate == 270) && 
27092                 (
27093                     width > this.imageEl.OriginWidth || 
27094                     height > this.imageEl.OriginHeight ||
27095                     (width < this.minHeight && height < this.minWidth)
27096                 )
27097         ){
27098             return false;
27099         }
27100         
27101         if(
27102                 !this.isDocument &&
27103                 (this.rotate == 0 || this.rotate == 180) && 
27104                 (
27105                     width < this.minWidth || 
27106                     width > this.imageEl.OriginWidth || 
27107                     height < this.minHeight || 
27108                     height > this.imageEl.OriginHeight
27109                 )
27110         ){
27111             return false;
27112         }
27113         
27114         if(
27115                 !this.isDocument &&
27116                 (this.rotate == 90 || this.rotate == 270) && 
27117                 (
27118                     width < this.minHeight || 
27119                     width > this.imageEl.OriginWidth || 
27120                     height < this.minWidth || 
27121                     height > this.imageEl.OriginHeight
27122                 )
27123         ){
27124             return false;
27125         }
27126         
27127         return true;
27128         
27129     },
27130     
27131     onRotateLeft : function(e)
27132     {   
27133         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27134             
27135             var minScale = this.thumbEl.getWidth() / this.minWidth;
27136             
27137             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27138             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27139             
27140             this.startScale = this.scale;
27141             
27142             while (this.getScaleLevel() < minScale){
27143             
27144                 this.scale = this.scale + 1;
27145                 
27146                 if(!this.zoomable()){
27147                     break;
27148                 }
27149                 
27150                 if(
27151                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27152                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27153                 ){
27154                     continue;
27155                 }
27156                 
27157                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27158
27159                 this.draw();
27160                 
27161                 return;
27162             }
27163             
27164             this.scale = this.startScale;
27165             
27166             this.onRotateFail();
27167             
27168             return false;
27169         }
27170         
27171         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27172
27173         if(this.isDocument){
27174             this.setThumbBoxSize();
27175             this.setThumbBoxPosition();
27176             this.setCanvasPosition();
27177         }
27178         
27179         this.draw();
27180         
27181         this.fireEvent('rotate', this, 'left');
27182         
27183     },
27184     
27185     onRotateRight : function(e)
27186     {
27187         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27188             
27189             var minScale = this.thumbEl.getWidth() / this.minWidth;
27190         
27191             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27192             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27193             
27194             this.startScale = this.scale;
27195             
27196             while (this.getScaleLevel() < minScale){
27197             
27198                 this.scale = this.scale + 1;
27199                 
27200                 if(!this.zoomable()){
27201                     break;
27202                 }
27203                 
27204                 if(
27205                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27206                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27207                 ){
27208                     continue;
27209                 }
27210                 
27211                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27212
27213                 this.draw();
27214                 
27215                 return;
27216             }
27217             
27218             this.scale = this.startScale;
27219             
27220             this.onRotateFail();
27221             
27222             return false;
27223         }
27224         
27225         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27226
27227         if(this.isDocument){
27228             this.setThumbBoxSize();
27229             this.setThumbBoxPosition();
27230             this.setCanvasPosition();
27231         }
27232         
27233         this.draw();
27234         
27235         this.fireEvent('rotate', this, 'right');
27236     },
27237     
27238     onRotateFail : function()
27239     {
27240         this.errorEl.show(true);
27241         
27242         var _this = this;
27243         
27244         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27245     },
27246     
27247     draw : function()
27248     {
27249         this.previewEl.dom.innerHTML = '';
27250         
27251         var canvasEl = document.createElement("canvas");
27252         
27253         var contextEl = canvasEl.getContext("2d");
27254         
27255         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27256         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27257         var center = this.imageEl.OriginWidth / 2;
27258         
27259         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27260             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27261             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27262             center = this.imageEl.OriginHeight / 2;
27263         }
27264         
27265         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27266         
27267         contextEl.translate(center, center);
27268         contextEl.rotate(this.rotate * Math.PI / 180);
27269
27270         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27271         
27272         this.canvasEl = document.createElement("canvas");
27273         
27274         this.contextEl = this.canvasEl.getContext("2d");
27275         
27276         switch (this.rotate) {
27277             case 0 :
27278                 
27279                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27280                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27281                 
27282                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27283                 
27284                 break;
27285             case 90 : 
27286                 
27287                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27288                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27289                 
27290                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27291                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27292                     break;
27293                 }
27294                 
27295                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27296                 
27297                 break;
27298             case 180 :
27299                 
27300                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27301                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27302                 
27303                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27304                     this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27305                     break;
27306                 }
27307                 
27308                 this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27309                 
27310                 break;
27311             case 270 :
27312                 
27313                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27314                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27315         
27316                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27317                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27318                     break;
27319                 }
27320                 
27321                 this.contextEl.drawImage(canvasEl, 0, Math.abs(this.canvasEl.width - this.canvasEl.height), this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27322                 
27323                 break;
27324             default : 
27325                 break;
27326         }
27327         
27328         this.previewEl.appendChild(this.canvasEl);
27329         
27330         this.setCanvasPosition();
27331     },
27332     
27333     crop : function()
27334     {
27335         if(!this.canvasLoaded){
27336             return;
27337         }
27338         
27339         var imageCanvas = document.createElement("canvas");
27340         
27341         var imageContext = imageCanvas.getContext("2d");
27342         
27343         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27344         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27345         
27346         var center = imageCanvas.width / 2;
27347         
27348         imageContext.translate(center, center);
27349         
27350         imageContext.rotate(this.rotate * Math.PI / 180);
27351         
27352         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27353         
27354         var canvas = document.createElement("canvas");
27355         
27356         var context = canvas.getContext("2d");
27357                 
27358         canvas.width = this.minWidth;
27359         canvas.height = this.minHeight;
27360
27361         switch (this.rotate) {
27362             case 0 :
27363                 
27364                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27365                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27366                 
27367                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27368                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27369                 
27370                 var targetWidth = this.minWidth - 2 * x;
27371                 var targetHeight = this.minHeight - 2 * y;
27372                 
27373                 var scale = 1;
27374                 
27375                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27376                     scale = targetWidth / width;
27377                 }
27378                 
27379                 if(x > 0 && y == 0){
27380                     scale = targetHeight / height;
27381                 }
27382                 
27383                 if(x > 0 && y > 0){
27384                     scale = targetWidth / width;
27385                     
27386                     if(width < height){
27387                         scale = targetHeight / height;
27388                     }
27389                 }
27390                 
27391                 context.scale(scale, scale);
27392                 
27393                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27394                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27395
27396                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27397                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27398
27399                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27400                 
27401                 break;
27402             case 90 : 
27403                 
27404                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27405                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27406                 
27407                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27408                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27409                 
27410                 var targetWidth = this.minWidth - 2 * x;
27411                 var targetHeight = this.minHeight - 2 * y;
27412                 
27413                 var scale = 1;
27414                 
27415                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27416                     scale = targetWidth / width;
27417                 }
27418                 
27419                 if(x > 0 && y == 0){
27420                     scale = targetHeight / height;
27421                 }
27422                 
27423                 if(x > 0 && y > 0){
27424                     scale = targetWidth / width;
27425                     
27426                     if(width < height){
27427                         scale = targetHeight / height;
27428                     }
27429                 }
27430                 
27431                 context.scale(scale, scale);
27432                 
27433                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27434                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27435
27436                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27437                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27438                 
27439                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27440                 
27441                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27442                 
27443                 break;
27444             case 180 :
27445                 
27446                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27447                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27448                 
27449                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27450                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27451                 
27452                 var targetWidth = this.minWidth - 2 * x;
27453                 var targetHeight = this.minHeight - 2 * y;
27454                 
27455                 var scale = 1;
27456                 
27457                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27458                     scale = targetWidth / width;
27459                 }
27460                 
27461                 if(x > 0 && y == 0){
27462                     scale = targetHeight / height;
27463                 }
27464                 
27465                 if(x > 0 && y > 0){
27466                     scale = targetWidth / width;
27467                     
27468                     if(width < height){
27469                         scale = targetHeight / height;
27470                     }
27471                 }
27472                 
27473                 context.scale(scale, scale);
27474                 
27475                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27476                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27477
27478                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27479                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27480
27481                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27482                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27483                 
27484                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27485                 
27486                 break;
27487             case 270 :
27488                 
27489                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27490                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27491                 
27492                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27493                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27494                 
27495                 var targetWidth = this.minWidth - 2 * x;
27496                 var targetHeight = this.minHeight - 2 * y;
27497                 
27498                 var scale = 1;
27499                 
27500                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27501                     scale = targetWidth / width;
27502                 }
27503                 
27504                 if(x > 0 && y == 0){
27505                     scale = targetHeight / height;
27506                 }
27507                 
27508                 if(x > 0 && y > 0){
27509                     scale = targetWidth / width;
27510                     
27511                     if(width < height){
27512                         scale = targetHeight / height;
27513                     }
27514                 }
27515                 
27516                 context.scale(scale, scale);
27517                 
27518                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27519                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27520
27521                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27522                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27523                 
27524                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27525                 
27526                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27527                 
27528                 break;
27529             default : 
27530                 break;
27531         }
27532         
27533         this.cropData = canvas.toDataURL(this.cropType);
27534         
27535         if(this.fireEvent('crop', this, this.cropData) !== false){
27536             this.process(this.file, this.cropData);
27537         }
27538         
27539         return;
27540         
27541     },
27542     
27543     setThumbBoxSize : function()
27544     {
27545         var width, height;
27546         
27547         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27548             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27549             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27550             
27551             this.minWidth = width;
27552             this.minHeight = height;
27553             
27554             if(this.rotate == 90 || this.rotate == 270){
27555                 this.minWidth = height;
27556                 this.minHeight = width;
27557             }
27558         }
27559         
27560         height = 300;
27561         width = Math.ceil(this.minWidth * height / this.minHeight);
27562         
27563         if(this.minWidth > this.minHeight){
27564             width = 300;
27565             height = Math.ceil(this.minHeight * width / this.minWidth);
27566         }
27567         
27568         this.thumbEl.setStyle({
27569             width : width + 'px',
27570             height : height + 'px'
27571         });
27572
27573         return;
27574             
27575     },
27576     
27577     setThumbBoxPosition : function()
27578     {
27579         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27580         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27581         
27582         this.thumbEl.setLeft(x);
27583         this.thumbEl.setTop(y);
27584         
27585     },
27586     
27587     baseRotateLevel : function()
27588     {
27589         this.baseRotate = 1;
27590         
27591         if(
27592                 typeof(this.exif) != 'undefined' &&
27593                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27594                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27595         ){
27596             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27597         }
27598         
27599         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27600         
27601     },
27602     
27603     baseScaleLevel : function()
27604     {
27605         var width, height;
27606         
27607         if(this.isDocument){
27608             
27609             if(this.baseRotate == 6 || this.baseRotate == 8){
27610             
27611                 height = this.thumbEl.getHeight();
27612                 this.baseScale = height / this.imageEl.OriginWidth;
27613
27614                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27615                     width = this.thumbEl.getWidth();
27616                     this.baseScale = width / this.imageEl.OriginHeight;
27617                 }
27618
27619                 return;
27620             }
27621
27622             height = this.thumbEl.getHeight();
27623             this.baseScale = height / this.imageEl.OriginHeight;
27624
27625             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27626                 width = this.thumbEl.getWidth();
27627                 this.baseScale = width / this.imageEl.OriginWidth;
27628             }
27629
27630             return;
27631         }
27632         
27633         if(this.baseRotate == 6 || this.baseRotate == 8){
27634             
27635             width = this.thumbEl.getHeight();
27636             this.baseScale = width / this.imageEl.OriginHeight;
27637             
27638             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27639                 height = this.thumbEl.getWidth();
27640                 this.baseScale = height / this.imageEl.OriginHeight;
27641             }
27642             
27643             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27644                 height = this.thumbEl.getWidth();
27645                 this.baseScale = height / this.imageEl.OriginHeight;
27646                 
27647                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27648                     width = this.thumbEl.getHeight();
27649                     this.baseScale = width / this.imageEl.OriginWidth;
27650                 }
27651             }
27652             
27653             return;
27654         }
27655         
27656         width = this.thumbEl.getWidth();
27657         this.baseScale = width / this.imageEl.OriginWidth;
27658         
27659         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27660             height = this.thumbEl.getHeight();
27661             this.baseScale = height / this.imageEl.OriginHeight;
27662         }
27663         
27664         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27665             
27666             height = this.thumbEl.getHeight();
27667             this.baseScale = height / this.imageEl.OriginHeight;
27668             
27669             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27670                 width = this.thumbEl.getWidth();
27671                 this.baseScale = width / this.imageEl.OriginWidth;
27672             }
27673             
27674         }
27675         
27676         return;
27677     },
27678     
27679     getScaleLevel : function()
27680     {
27681         return this.baseScale * Math.pow(1.1, this.scale);
27682     },
27683     
27684     onTouchStart : function(e)
27685     {
27686         if(!this.canvasLoaded){
27687             this.beforeSelectFile(e);
27688             return;
27689         }
27690         
27691         var touches = e.browserEvent.touches;
27692         
27693         if(!touches){
27694             return;
27695         }
27696         
27697         if(touches.length == 1){
27698             this.onMouseDown(e);
27699             return;
27700         }
27701         
27702         if(touches.length != 2){
27703             return;
27704         }
27705         
27706         var coords = [];
27707         
27708         for(var i = 0, finger; finger = touches[i]; i++){
27709             coords.push(finger.pageX, finger.pageY);
27710         }
27711         
27712         var x = Math.pow(coords[0] - coords[2], 2);
27713         var y = Math.pow(coords[1] - coords[3], 2);
27714         
27715         this.startDistance = Math.sqrt(x + y);
27716         
27717         this.startScale = this.scale;
27718         
27719         this.pinching = true;
27720         this.dragable = false;
27721         
27722     },
27723     
27724     onTouchMove : function(e)
27725     {
27726         if(!this.pinching && !this.dragable){
27727             return;
27728         }
27729         
27730         var touches = e.browserEvent.touches;
27731         
27732         if(!touches){
27733             return;
27734         }
27735         
27736         if(this.dragable){
27737             this.onMouseMove(e);
27738             return;
27739         }
27740         
27741         var coords = [];
27742         
27743         for(var i = 0, finger; finger = touches[i]; i++){
27744             coords.push(finger.pageX, finger.pageY);
27745         }
27746         
27747         var x = Math.pow(coords[0] - coords[2], 2);
27748         var y = Math.pow(coords[1] - coords[3], 2);
27749         
27750         this.endDistance = Math.sqrt(x + y);
27751         
27752         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27753         
27754         if(!this.zoomable()){
27755             this.scale = this.startScale;
27756             return;
27757         }
27758         
27759         this.draw();
27760         
27761     },
27762     
27763     onTouchEnd : function(e)
27764     {
27765         this.pinching = false;
27766         this.dragable = false;
27767         
27768     },
27769     
27770     process : function(file, crop)
27771     {
27772         if(this.loadMask){
27773             this.maskEl.mask(this.loadingText);
27774         }
27775         
27776         this.xhr = new XMLHttpRequest();
27777         
27778         file.xhr = this.xhr;
27779
27780         this.xhr.open(this.method, this.url, true);
27781         
27782         var headers = {
27783             "Accept": "application/json",
27784             "Cache-Control": "no-cache",
27785             "X-Requested-With": "XMLHttpRequest"
27786         };
27787         
27788         for (var headerName in headers) {
27789             var headerValue = headers[headerName];
27790             if (headerValue) {
27791                 this.xhr.setRequestHeader(headerName, headerValue);
27792             }
27793         }
27794         
27795         var _this = this;
27796         
27797         this.xhr.onload = function()
27798         {
27799             _this.xhrOnLoad(_this.xhr);
27800         }
27801         
27802         this.xhr.onerror = function()
27803         {
27804             _this.xhrOnError(_this.xhr);
27805         }
27806         
27807         var formData = new FormData();
27808
27809         formData.append('returnHTML', 'NO');
27810         
27811         if(crop){
27812             formData.append('crop', crop);
27813         }
27814         
27815         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27816             formData.append(this.paramName, file, file.name);
27817         }
27818         
27819         if(typeof(file.filename) != 'undefined'){
27820             formData.append('filename', file.filename);
27821         }
27822         
27823         if(typeof(file.mimetype) != 'undefined'){
27824             formData.append('mimetype', file.mimetype);
27825         }
27826         
27827         if(this.fireEvent('arrange', this, formData) != false){
27828             this.xhr.send(formData);
27829         };
27830     },
27831     
27832     xhrOnLoad : function(xhr)
27833     {
27834         if(this.loadMask){
27835             this.maskEl.unmask();
27836         }
27837         
27838         if (xhr.readyState !== 4) {
27839             this.fireEvent('exception', this, xhr);
27840             return;
27841         }
27842
27843         var response = Roo.decode(xhr.responseText);
27844         
27845         if(!response.success){
27846             this.fireEvent('exception', this, xhr);
27847             return;
27848         }
27849         
27850         var response = Roo.decode(xhr.responseText);
27851         
27852         this.fireEvent('upload', this, response);
27853         
27854     },
27855     
27856     xhrOnError : function()
27857     {
27858         if(this.loadMask){
27859             this.maskEl.unmask();
27860         }
27861         
27862         Roo.log('xhr on error');
27863         
27864         var response = Roo.decode(xhr.responseText);
27865           
27866         Roo.log(response);
27867         
27868     },
27869     
27870     prepare : function(file)
27871     {   
27872         if(this.loadMask){
27873             this.maskEl.mask(this.loadingText);
27874         }
27875         
27876         this.file = false;
27877         this.exif = {};
27878         
27879         if(typeof(file) === 'string'){
27880             this.loadCanvas(file);
27881             return;
27882         }
27883         
27884         if(!file || !this.urlAPI){
27885             return;
27886         }
27887         
27888         this.file = file;
27889         this.cropType = file.type;
27890         
27891         var _this = this;
27892         
27893         if(this.fireEvent('prepare', this, this.file) != false){
27894             
27895             var reader = new FileReader();
27896             
27897             reader.onload = function (e) {
27898                 if (e.target.error) {
27899                     Roo.log(e.target.error);
27900                     return;
27901                 }
27902                 
27903                 var buffer = e.target.result,
27904                     dataView = new DataView(buffer),
27905                     offset = 2,
27906                     maxOffset = dataView.byteLength - 4,
27907                     markerBytes,
27908                     markerLength;
27909                 
27910                 if (dataView.getUint16(0) === 0xffd8) {
27911                     while (offset < maxOffset) {
27912                         markerBytes = dataView.getUint16(offset);
27913                         
27914                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27915                             markerLength = dataView.getUint16(offset + 2) + 2;
27916                             if (offset + markerLength > dataView.byteLength) {
27917                                 Roo.log('Invalid meta data: Invalid segment size.');
27918                                 break;
27919                             }
27920                             
27921                             if(markerBytes == 0xffe1){
27922                                 _this.parseExifData(
27923                                     dataView,
27924                                     offset,
27925                                     markerLength
27926                                 );
27927                             }
27928                             
27929                             offset += markerLength;
27930                             
27931                             continue;
27932                         }
27933                         
27934                         break;
27935                     }
27936                     
27937                 }
27938                 
27939                 var url = _this.urlAPI.createObjectURL(_this.file);
27940                 
27941                 _this.loadCanvas(url);
27942                 
27943                 return;
27944             }
27945             
27946             reader.readAsArrayBuffer(this.file);
27947             
27948         }
27949         
27950     },
27951     
27952     parseExifData : function(dataView, offset, length)
27953     {
27954         var tiffOffset = offset + 10,
27955             littleEndian,
27956             dirOffset;
27957     
27958         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27959             // No Exif data, might be XMP data instead
27960             return;
27961         }
27962         
27963         // Check for the ASCII code for "Exif" (0x45786966):
27964         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27965             // No Exif data, might be XMP data instead
27966             return;
27967         }
27968         if (tiffOffset + 8 > dataView.byteLength) {
27969             Roo.log('Invalid Exif data: Invalid segment size.');
27970             return;
27971         }
27972         // Check for the two null bytes:
27973         if (dataView.getUint16(offset + 8) !== 0x0000) {
27974             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27975             return;
27976         }
27977         // Check the byte alignment:
27978         switch (dataView.getUint16(tiffOffset)) {
27979         case 0x4949:
27980             littleEndian = true;
27981             break;
27982         case 0x4D4D:
27983             littleEndian = false;
27984             break;
27985         default:
27986             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27987             return;
27988         }
27989         // Check for the TIFF tag marker (0x002A):
27990         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27991             Roo.log('Invalid Exif data: Missing TIFF marker.');
27992             return;
27993         }
27994         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27995         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27996         
27997         this.parseExifTags(
27998             dataView,
27999             tiffOffset,
28000             tiffOffset + dirOffset,
28001             littleEndian
28002         );
28003     },
28004     
28005     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28006     {
28007         var tagsNumber,
28008             dirEndOffset,
28009             i;
28010         if (dirOffset + 6 > dataView.byteLength) {
28011             Roo.log('Invalid Exif data: Invalid directory offset.');
28012             return;
28013         }
28014         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28015         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28016         if (dirEndOffset + 4 > dataView.byteLength) {
28017             Roo.log('Invalid Exif data: Invalid directory size.');
28018             return;
28019         }
28020         for (i = 0; i < tagsNumber; i += 1) {
28021             this.parseExifTag(
28022                 dataView,
28023                 tiffOffset,
28024                 dirOffset + 2 + 12 * i, // tag offset
28025                 littleEndian
28026             );
28027         }
28028         // Return the offset to the next directory:
28029         return dataView.getUint32(dirEndOffset, littleEndian);
28030     },
28031     
28032     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28033     {
28034         var tag = dataView.getUint16(offset, littleEndian);
28035         
28036         this.exif[tag] = this.getExifValue(
28037             dataView,
28038             tiffOffset,
28039             offset,
28040             dataView.getUint16(offset + 2, littleEndian), // tag type
28041             dataView.getUint32(offset + 4, littleEndian), // tag length
28042             littleEndian
28043         );
28044     },
28045     
28046     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28047     {
28048         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28049             tagSize,
28050             dataOffset,
28051             values,
28052             i,
28053             str,
28054             c;
28055     
28056         if (!tagType) {
28057             Roo.log('Invalid Exif data: Invalid tag type.');
28058             return;
28059         }
28060         
28061         tagSize = tagType.size * length;
28062         // Determine if the value is contained in the dataOffset bytes,
28063         // or if the value at the dataOffset is a pointer to the actual data:
28064         dataOffset = tagSize > 4 ?
28065                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28066         if (dataOffset + tagSize > dataView.byteLength) {
28067             Roo.log('Invalid Exif data: Invalid data offset.');
28068             return;
28069         }
28070         if (length === 1) {
28071             return tagType.getValue(dataView, dataOffset, littleEndian);
28072         }
28073         values = [];
28074         for (i = 0; i < length; i += 1) {
28075             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28076         }
28077         
28078         if (tagType.ascii) {
28079             str = '';
28080             // Concatenate the chars:
28081             for (i = 0; i < values.length; i += 1) {
28082                 c = values[i];
28083                 // Ignore the terminating NULL byte(s):
28084                 if (c === '\u0000') {
28085                     break;
28086                 }
28087                 str += c;
28088             }
28089             return str;
28090         }
28091         return values;
28092     }
28093     
28094 });
28095
28096 Roo.apply(Roo.bootstrap.UploadCropbox, {
28097     tags : {
28098         'Orientation': 0x0112
28099     },
28100     
28101     Orientation: {
28102             1: 0, //'top-left',
28103 //            2: 'top-right',
28104             3: 180, //'bottom-right',
28105 //            4: 'bottom-left',
28106 //            5: 'left-top',
28107             6: 90, //'right-top',
28108 //            7: 'right-bottom',
28109             8: 270 //'left-bottom'
28110     },
28111     
28112     exifTagTypes : {
28113         // byte, 8-bit unsigned int:
28114         1: {
28115             getValue: function (dataView, dataOffset) {
28116                 return dataView.getUint8(dataOffset);
28117             },
28118             size: 1
28119         },
28120         // ascii, 8-bit byte:
28121         2: {
28122             getValue: function (dataView, dataOffset) {
28123                 return String.fromCharCode(dataView.getUint8(dataOffset));
28124             },
28125             size: 1,
28126             ascii: true
28127         },
28128         // short, 16 bit int:
28129         3: {
28130             getValue: function (dataView, dataOffset, littleEndian) {
28131                 return dataView.getUint16(dataOffset, littleEndian);
28132             },
28133             size: 2
28134         },
28135         // long, 32 bit int:
28136         4: {
28137             getValue: function (dataView, dataOffset, littleEndian) {
28138                 return dataView.getUint32(dataOffset, littleEndian);
28139             },
28140             size: 4
28141         },
28142         // rational = two long values, first is numerator, second is denominator:
28143         5: {
28144             getValue: function (dataView, dataOffset, littleEndian) {
28145                 return dataView.getUint32(dataOffset, littleEndian) /
28146                     dataView.getUint32(dataOffset + 4, littleEndian);
28147             },
28148             size: 8
28149         },
28150         // slong, 32 bit signed int:
28151         9: {
28152             getValue: function (dataView, dataOffset, littleEndian) {
28153                 return dataView.getInt32(dataOffset, littleEndian);
28154             },
28155             size: 4
28156         },
28157         // srational, two slongs, first is numerator, second is denominator:
28158         10: {
28159             getValue: function (dataView, dataOffset, littleEndian) {
28160                 return dataView.getInt32(dataOffset, littleEndian) /
28161                     dataView.getInt32(dataOffset + 4, littleEndian);
28162             },
28163             size: 8
28164         }
28165     },
28166     
28167     footer : {
28168         STANDARD : [
28169             {
28170                 tag : 'div',
28171                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28172                 action : 'rotate-left',
28173                 cn : [
28174                     {
28175                         tag : 'button',
28176                         cls : 'btn btn-default',
28177                         html : '<i class="fa fa-undo"></i>'
28178                     }
28179                 ]
28180             },
28181             {
28182                 tag : 'div',
28183                 cls : 'btn-group roo-upload-cropbox-picture',
28184                 action : 'picture',
28185                 cn : [
28186                     {
28187                         tag : 'button',
28188                         cls : 'btn btn-default',
28189                         html : '<i class="fa fa-picture-o"></i>'
28190                     }
28191                 ]
28192             },
28193             {
28194                 tag : 'div',
28195                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28196                 action : 'rotate-right',
28197                 cn : [
28198                     {
28199                         tag : 'button',
28200                         cls : 'btn btn-default',
28201                         html : '<i class="fa fa-repeat"></i>'
28202                     }
28203                 ]
28204             }
28205         ],
28206         DOCUMENT : [
28207             {
28208                 tag : 'div',
28209                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28210                 action : 'rotate-left',
28211                 cn : [
28212                     {
28213                         tag : 'button',
28214                         cls : 'btn btn-default',
28215                         html : '<i class="fa fa-undo"></i>'
28216                     }
28217                 ]
28218             },
28219             {
28220                 tag : 'div',
28221                 cls : 'btn-group roo-upload-cropbox-download',
28222                 action : 'download',
28223                 cn : [
28224                     {
28225                         tag : 'button',
28226                         cls : 'btn btn-default',
28227                         html : '<i class="fa fa-download"></i>'
28228                     }
28229                 ]
28230             },
28231             {
28232                 tag : 'div',
28233                 cls : 'btn-group roo-upload-cropbox-crop',
28234                 action : 'crop',
28235                 cn : [
28236                     {
28237                         tag : 'button',
28238                         cls : 'btn btn-default',
28239                         html : '<i class="fa fa-crop"></i>'
28240                     }
28241                 ]
28242             },
28243             {
28244                 tag : 'div',
28245                 cls : 'btn-group roo-upload-cropbox-trash',
28246                 action : 'trash',
28247                 cn : [
28248                     {
28249                         tag : 'button',
28250                         cls : 'btn btn-default',
28251                         html : '<i class="fa fa-trash"></i>'
28252                     }
28253                 ]
28254             },
28255             {
28256                 tag : 'div',
28257                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28258                 action : 'rotate-right',
28259                 cn : [
28260                     {
28261                         tag : 'button',
28262                         cls : 'btn btn-default',
28263                         html : '<i class="fa fa-repeat"></i>'
28264                     }
28265                 ]
28266             }
28267         ],
28268         ROTATOR : [
28269             {
28270                 tag : 'div',
28271                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28272                 action : 'rotate-left',
28273                 cn : [
28274                     {
28275                         tag : 'button',
28276                         cls : 'btn btn-default',
28277                         html : '<i class="fa fa-undo"></i>'
28278                     }
28279                 ]
28280             },
28281             {
28282                 tag : 'div',
28283                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28284                 action : 'rotate-right',
28285                 cn : [
28286                     {
28287                         tag : 'button',
28288                         cls : 'btn btn-default',
28289                         html : '<i class="fa fa-repeat"></i>'
28290                     }
28291                 ]
28292             }
28293         ]
28294     }
28295 });
28296
28297 /*
28298 * Licence: LGPL
28299 */
28300
28301 /**
28302  * @class Roo.bootstrap.DocumentManager
28303  * @extends Roo.bootstrap.Component
28304  * Bootstrap DocumentManager class
28305  * @cfg {String} paramName default 'imageUpload'
28306  * @cfg {String} toolTipName default 'filename'
28307  * @cfg {String} method default POST
28308  * @cfg {String} url action url
28309  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28310  * @cfg {Boolean} multiple multiple upload default true
28311  * @cfg {Number} thumbSize default 300
28312  * @cfg {String} fieldLabel
28313  * @cfg {Number} labelWidth default 4
28314  * @cfg {String} labelAlign (left|top) default left
28315  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28316 * @cfg {Number} labellg set the width of label (1-12)
28317  * @cfg {Number} labelmd set the width of label (1-12)
28318  * @cfg {Number} labelsm set the width of label (1-12)
28319  * @cfg {Number} labelxs set the width of label (1-12)
28320  * 
28321  * @constructor
28322  * Create a new DocumentManager
28323  * @param {Object} config The config object
28324  */
28325
28326 Roo.bootstrap.DocumentManager = function(config){
28327     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28328     
28329     this.files = [];
28330     this.delegates = [];
28331     
28332     this.addEvents({
28333         /**
28334          * @event initial
28335          * Fire when initial the DocumentManager
28336          * @param {Roo.bootstrap.DocumentManager} this
28337          */
28338         "initial" : true,
28339         /**
28340          * @event inspect
28341          * inspect selected file
28342          * @param {Roo.bootstrap.DocumentManager} this
28343          * @param {File} file
28344          */
28345         "inspect" : true,
28346         /**
28347          * @event exception
28348          * Fire when xhr load exception
28349          * @param {Roo.bootstrap.DocumentManager} this
28350          * @param {XMLHttpRequest} xhr
28351          */
28352         "exception" : true,
28353         /**
28354          * @event afterupload
28355          * Fire when xhr load exception
28356          * @param {Roo.bootstrap.DocumentManager} this
28357          * @param {XMLHttpRequest} xhr
28358          */
28359         "afterupload" : true,
28360         /**
28361          * @event prepare
28362          * prepare the form data
28363          * @param {Roo.bootstrap.DocumentManager} this
28364          * @param {Object} formData
28365          */
28366         "prepare" : true,
28367         /**
28368          * @event remove
28369          * Fire when remove the file
28370          * @param {Roo.bootstrap.DocumentManager} this
28371          * @param {Object} file
28372          */
28373         "remove" : true,
28374         /**
28375          * @event refresh
28376          * Fire after refresh the file
28377          * @param {Roo.bootstrap.DocumentManager} this
28378          */
28379         "refresh" : true,
28380         /**
28381          * @event click
28382          * Fire after click the image
28383          * @param {Roo.bootstrap.DocumentManager} this
28384          * @param {Object} file
28385          */
28386         "click" : true,
28387         /**
28388          * @event edit
28389          * Fire when upload a image and editable set to true
28390          * @param {Roo.bootstrap.DocumentManager} this
28391          * @param {Object} file
28392          */
28393         "edit" : true,
28394         /**
28395          * @event beforeselectfile
28396          * Fire before select file
28397          * @param {Roo.bootstrap.DocumentManager} this
28398          */
28399         "beforeselectfile" : true,
28400         /**
28401          * @event process
28402          * Fire before process file
28403          * @param {Roo.bootstrap.DocumentManager} this
28404          * @param {Object} file
28405          */
28406         "process" : true
28407         
28408     });
28409 };
28410
28411 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28412     
28413     boxes : 0,
28414     inputName : '',
28415     thumbSize : 300,
28416     multiple : true,
28417     files : false,
28418     method : 'POST',
28419     url : '',
28420     paramName : 'imageUpload',
28421     toolTipName : 'filename',
28422     fieldLabel : '',
28423     labelWidth : 4,
28424     labelAlign : 'left',
28425     editable : true,
28426     delegates : false,
28427     xhr : false, 
28428     
28429     labellg : 0,
28430     labelmd : 0,
28431     labelsm : 0,
28432     labelxs : 0,
28433     
28434     getAutoCreate : function()
28435     {   
28436         var managerWidget = {
28437             tag : 'div',
28438             cls : 'roo-document-manager',
28439             cn : [
28440                 {
28441                     tag : 'input',
28442                     cls : 'roo-document-manager-selector',
28443                     type : 'file'
28444                 },
28445                 {
28446                     tag : 'div',
28447                     cls : 'roo-document-manager-uploader',
28448                     cn : [
28449                         {
28450                             tag : 'div',
28451                             cls : 'roo-document-manager-upload-btn',
28452                             html : '<i class="fa fa-plus"></i>'
28453                         }
28454                     ]
28455                     
28456                 }
28457             ]
28458         };
28459         
28460         var content = [
28461             {
28462                 tag : 'div',
28463                 cls : 'column col-md-12',
28464                 cn : managerWidget
28465             }
28466         ];
28467         
28468         if(this.fieldLabel.length){
28469             
28470             content = [
28471                 {
28472                     tag : 'div',
28473                     cls : 'column col-md-12',
28474                     html : this.fieldLabel
28475                 },
28476                 {
28477                     tag : 'div',
28478                     cls : 'column col-md-12',
28479                     cn : managerWidget
28480                 }
28481             ];
28482
28483             if(this.labelAlign == 'left'){
28484                 content = [
28485                     {
28486                         tag : 'div',
28487                         cls : 'column',
28488                         html : this.fieldLabel
28489                     },
28490                     {
28491                         tag : 'div',
28492                         cls : 'column',
28493                         cn : managerWidget
28494                     }
28495                 ];
28496                 
28497                 if(this.labelWidth > 12){
28498                     content[0].style = "width: " + this.labelWidth + 'px';
28499                 }
28500
28501                 if(this.labelWidth < 13 && this.labelmd == 0){
28502                     this.labelmd = this.labelWidth;
28503                 }
28504
28505                 if(this.labellg > 0){
28506                     content[0].cls += ' col-lg-' + this.labellg;
28507                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28508                 }
28509
28510                 if(this.labelmd > 0){
28511                     content[0].cls += ' col-md-' + this.labelmd;
28512                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28513                 }
28514
28515                 if(this.labelsm > 0){
28516                     content[0].cls += ' col-sm-' + this.labelsm;
28517                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28518                 }
28519
28520                 if(this.labelxs > 0){
28521                     content[0].cls += ' col-xs-' + this.labelxs;
28522                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28523                 }
28524                 
28525             }
28526         }
28527         
28528         var cfg = {
28529             tag : 'div',
28530             cls : 'row clearfix',
28531             cn : content
28532         };
28533         
28534         return cfg;
28535         
28536     },
28537     
28538     initEvents : function()
28539     {
28540         this.managerEl = this.el.select('.roo-document-manager', true).first();
28541         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28542         
28543         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28544         this.selectorEl.hide();
28545         
28546         if(this.multiple){
28547             this.selectorEl.attr('multiple', 'multiple');
28548         }
28549         
28550         this.selectorEl.on('change', this.onFileSelected, this);
28551         
28552         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28553         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28554         
28555         this.uploader.on('click', this.onUploaderClick, this);
28556         
28557         this.renderProgressDialog();
28558         
28559         var _this = this;
28560         
28561         window.addEventListener("resize", function() { _this.refresh(); } );
28562         
28563         this.fireEvent('initial', this);
28564     },
28565     
28566     renderProgressDialog : function()
28567     {
28568         var _this = this;
28569         
28570         this.progressDialog = new Roo.bootstrap.Modal({
28571             cls : 'roo-document-manager-progress-dialog',
28572             allow_close : false,
28573             title : '',
28574             buttons : [
28575                 {
28576                     name  :'cancel',
28577                     weight : 'danger',
28578                     html : 'Cancel'
28579                 }
28580             ], 
28581             listeners : { 
28582                 btnclick : function() {
28583                     _this.uploadCancel();
28584                     this.hide();
28585                 }
28586             }
28587         });
28588          
28589         this.progressDialog.render(Roo.get(document.body));
28590          
28591         this.progress = new Roo.bootstrap.Progress({
28592             cls : 'roo-document-manager-progress',
28593             active : true,
28594             striped : true
28595         });
28596         
28597         this.progress.render(this.progressDialog.getChildContainer());
28598         
28599         this.progressBar = new Roo.bootstrap.ProgressBar({
28600             cls : 'roo-document-manager-progress-bar',
28601             aria_valuenow : 0,
28602             aria_valuemin : 0,
28603             aria_valuemax : 12,
28604             panel : 'success'
28605         });
28606         
28607         this.progressBar.render(this.progress.getChildContainer());
28608     },
28609     
28610     onUploaderClick : function(e)
28611     {
28612         e.preventDefault();
28613      
28614         if(this.fireEvent('beforeselectfile', this) != false){
28615             this.selectorEl.dom.click();
28616         }
28617         
28618     },
28619     
28620     onFileSelected : function(e)
28621     {
28622         e.preventDefault();
28623         
28624         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28625             return;
28626         }
28627         
28628         Roo.each(this.selectorEl.dom.files, function(file){
28629             if(this.fireEvent('inspect', this, file) != false){
28630                 this.files.push(file);
28631             }
28632         }, this);
28633         
28634         this.queue();
28635         
28636     },
28637     
28638     queue : function()
28639     {
28640         this.selectorEl.dom.value = '';
28641         
28642         if(!this.files.length){
28643             return;
28644         }
28645         
28646         if(this.boxes > 0 && this.files.length > this.boxes){
28647             this.files = this.files.slice(0, this.boxes);
28648         }
28649         
28650         this.uploader.show();
28651         
28652         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28653             this.uploader.hide();
28654         }
28655         
28656         var _this = this;
28657         
28658         var files = [];
28659         
28660         var docs = [];
28661         
28662         Roo.each(this.files, function(file){
28663             
28664             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28665                 var f = this.renderPreview(file);
28666                 files.push(f);
28667                 return;
28668             }
28669             
28670             if(file.type.indexOf('image') != -1){
28671                 this.delegates.push(
28672                     (function(){
28673                         _this.process(file);
28674                     }).createDelegate(this)
28675                 );
28676         
28677                 return;
28678             }
28679             
28680             docs.push(
28681                 (function(){
28682                     _this.process(file);
28683                 }).createDelegate(this)
28684             );
28685             
28686         }, this);
28687         
28688         this.files = files;
28689         
28690         this.delegates = this.delegates.concat(docs);
28691         
28692         if(!this.delegates.length){
28693             this.refresh();
28694             return;
28695         }
28696         
28697         this.progressBar.aria_valuemax = this.delegates.length;
28698         
28699         this.arrange();
28700         
28701         return;
28702     },
28703     
28704     arrange : function()
28705     {
28706         if(!this.delegates.length){
28707             this.progressDialog.hide();
28708             this.refresh();
28709             return;
28710         }
28711         
28712         var delegate = this.delegates.shift();
28713         
28714         this.progressDialog.show();
28715         
28716         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28717         
28718         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28719         
28720         delegate();
28721     },
28722     
28723     refresh : function()
28724     {
28725         this.uploader.show();
28726         
28727         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28728             this.uploader.hide();
28729         }
28730         
28731         Roo.isTouch ? this.closable(false) : this.closable(true);
28732         
28733         this.fireEvent('refresh', this);
28734     },
28735     
28736     onRemove : function(e, el, o)
28737     {
28738         e.preventDefault();
28739         
28740         this.fireEvent('remove', this, o);
28741         
28742     },
28743     
28744     remove : function(o)
28745     {
28746         var files = [];
28747         
28748         Roo.each(this.files, function(file){
28749             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28750                 files.push(file);
28751                 return;
28752             }
28753
28754             o.target.remove();
28755
28756         }, this);
28757         
28758         this.files = files;
28759         
28760         this.refresh();
28761     },
28762     
28763     clear : function()
28764     {
28765         Roo.each(this.files, function(file){
28766             if(!file.target){
28767                 return;
28768             }
28769             
28770             file.target.remove();
28771
28772         }, this);
28773         
28774         this.files = [];
28775         
28776         this.refresh();
28777     },
28778     
28779     onClick : function(e, el, o)
28780     {
28781         e.preventDefault();
28782         
28783         this.fireEvent('click', this, o);
28784         
28785     },
28786     
28787     closable : function(closable)
28788     {
28789         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28790             
28791             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28792             
28793             if(closable){
28794                 el.show();
28795                 return;
28796             }
28797             
28798             el.hide();
28799             
28800         }, this);
28801     },
28802     
28803     xhrOnLoad : function(xhr)
28804     {
28805         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28806             el.remove();
28807         }, this);
28808         
28809         if (xhr.readyState !== 4) {
28810             this.arrange();
28811             this.fireEvent('exception', this, xhr);
28812             return;
28813         }
28814
28815         var response = Roo.decode(xhr.responseText);
28816         
28817         if(!response.success){
28818             this.arrange();
28819             this.fireEvent('exception', this, xhr);
28820             return;
28821         }
28822         
28823         var file = this.renderPreview(response.data);
28824         
28825         this.files.push(file);
28826         
28827         this.arrange();
28828         
28829         this.fireEvent('afterupload', this, xhr);
28830         
28831     },
28832     
28833     xhrOnError : function(xhr)
28834     {
28835         Roo.log('xhr on error');
28836         
28837         var response = Roo.decode(xhr.responseText);
28838           
28839         Roo.log(response);
28840         
28841         this.arrange();
28842     },
28843     
28844     process : function(file)
28845     {
28846         if(this.fireEvent('process', this, file) !== false){
28847             if(this.editable && file.type.indexOf('image') != -1){
28848                 this.fireEvent('edit', this, file);
28849                 return;
28850             }
28851
28852             this.uploadStart(file, false);
28853
28854             return;
28855         }
28856         
28857     },
28858     
28859     uploadStart : function(file, crop)
28860     {
28861         this.xhr = new XMLHttpRequest();
28862         
28863         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28864             this.arrange();
28865             return;
28866         }
28867         
28868         file.xhr = this.xhr;
28869             
28870         this.managerEl.createChild({
28871             tag : 'div',
28872             cls : 'roo-document-manager-loading',
28873             cn : [
28874                 {
28875                     tag : 'div',
28876                     tooltip : file.name,
28877                     cls : 'roo-document-manager-thumb',
28878                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28879                 }
28880             ]
28881
28882         });
28883
28884         this.xhr.open(this.method, this.url, true);
28885         
28886         var headers = {
28887             "Accept": "application/json",
28888             "Cache-Control": "no-cache",
28889             "X-Requested-With": "XMLHttpRequest"
28890         };
28891         
28892         for (var headerName in headers) {
28893             var headerValue = headers[headerName];
28894             if (headerValue) {
28895                 this.xhr.setRequestHeader(headerName, headerValue);
28896             }
28897         }
28898         
28899         var _this = this;
28900         
28901         this.xhr.onload = function()
28902         {
28903             _this.xhrOnLoad(_this.xhr);
28904         }
28905         
28906         this.xhr.onerror = function()
28907         {
28908             _this.xhrOnError(_this.xhr);
28909         }
28910         
28911         var formData = new FormData();
28912
28913         formData.append('returnHTML', 'NO');
28914         
28915         if(crop){
28916             formData.append('crop', crop);
28917         }
28918         
28919         formData.append(this.paramName, file, file.name);
28920         
28921         var options = {
28922             file : file, 
28923             manually : false
28924         };
28925         
28926         if(this.fireEvent('prepare', this, formData, options) != false){
28927             
28928             if(options.manually){
28929                 return;
28930             }
28931             
28932             this.xhr.send(formData);
28933             return;
28934         };
28935         
28936         this.uploadCancel();
28937     },
28938     
28939     uploadCancel : function()
28940     {
28941         if (this.xhr) {
28942             this.xhr.abort();
28943         }
28944         
28945         this.delegates = [];
28946         
28947         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28948             el.remove();
28949         }, this);
28950         
28951         this.arrange();
28952     },
28953     
28954     renderPreview : function(file)
28955     {
28956         if(typeof(file.target) != 'undefined' && file.target){
28957             return file;
28958         }
28959         
28960         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28961         
28962         var previewEl = this.managerEl.createChild({
28963             tag : 'div',
28964             cls : 'roo-document-manager-preview',
28965             cn : [
28966                 {
28967                     tag : 'div',
28968                     tooltip : file[this.toolTipName],
28969                     cls : 'roo-document-manager-thumb',
28970                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28971                 },
28972                 {
28973                     tag : 'button',
28974                     cls : 'close',
28975                     html : '<i class="fa fa-times-circle"></i>'
28976                 }
28977             ]
28978         });
28979
28980         var close = previewEl.select('button.close', true).first();
28981
28982         close.on('click', this.onRemove, this, file);
28983
28984         file.target = previewEl;
28985
28986         var image = previewEl.select('img', true).first();
28987         
28988         var _this = this;
28989         
28990         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28991         
28992         image.on('click', this.onClick, this, file);
28993         
28994         return file;
28995         
28996     },
28997     
28998     onPreviewLoad : function(file, image)
28999     {
29000         if(typeof(file.target) == 'undefined' || !file.target){
29001             return;
29002         }
29003         
29004         var width = image.dom.naturalWidth || image.dom.width;
29005         var height = image.dom.naturalHeight || image.dom.height;
29006         
29007         if(width > height){
29008             file.target.addClass('wide');
29009             return;
29010         }
29011         
29012         file.target.addClass('tall');
29013         return;
29014         
29015     },
29016     
29017     uploadFromSource : function(file, crop)
29018     {
29019         this.xhr = new XMLHttpRequest();
29020         
29021         this.managerEl.createChild({
29022             tag : 'div',
29023             cls : 'roo-document-manager-loading',
29024             cn : [
29025                 {
29026                     tag : 'div',
29027                     tooltip : file.name,
29028                     cls : 'roo-document-manager-thumb',
29029                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29030                 }
29031             ]
29032
29033         });
29034
29035         this.xhr.open(this.method, this.url, true);
29036         
29037         var headers = {
29038             "Accept": "application/json",
29039             "Cache-Control": "no-cache",
29040             "X-Requested-With": "XMLHttpRequest"
29041         };
29042         
29043         for (var headerName in headers) {
29044             var headerValue = headers[headerName];
29045             if (headerValue) {
29046                 this.xhr.setRequestHeader(headerName, headerValue);
29047             }
29048         }
29049         
29050         var _this = this;
29051         
29052         this.xhr.onload = function()
29053         {
29054             _this.xhrOnLoad(_this.xhr);
29055         }
29056         
29057         this.xhr.onerror = function()
29058         {
29059             _this.xhrOnError(_this.xhr);
29060         }
29061         
29062         var formData = new FormData();
29063
29064         formData.append('returnHTML', 'NO');
29065         
29066         formData.append('crop', crop);
29067         
29068         if(typeof(file.filename) != 'undefined'){
29069             formData.append('filename', file.filename);
29070         }
29071         
29072         if(typeof(file.mimetype) != 'undefined'){
29073             formData.append('mimetype', file.mimetype);
29074         }
29075         
29076         Roo.log(formData);
29077         
29078         if(this.fireEvent('prepare', this, formData) != false){
29079             this.xhr.send(formData);
29080         };
29081     }
29082 });
29083
29084 /*
29085 * Licence: LGPL
29086 */
29087
29088 /**
29089  * @class Roo.bootstrap.DocumentViewer
29090  * @extends Roo.bootstrap.Component
29091  * Bootstrap DocumentViewer class
29092  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29093  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29094  * 
29095  * @constructor
29096  * Create a new DocumentViewer
29097  * @param {Object} config The config object
29098  */
29099
29100 Roo.bootstrap.DocumentViewer = function(config){
29101     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29102     
29103     this.addEvents({
29104         /**
29105          * @event initial
29106          * Fire after initEvent
29107          * @param {Roo.bootstrap.DocumentViewer} this
29108          */
29109         "initial" : true,
29110         /**
29111          * @event click
29112          * Fire after click
29113          * @param {Roo.bootstrap.DocumentViewer} this
29114          */
29115         "click" : true,
29116         /**
29117          * @event download
29118          * Fire after download button
29119          * @param {Roo.bootstrap.DocumentViewer} this
29120          */
29121         "download" : true,
29122         /**
29123          * @event trash
29124          * Fire after trash button
29125          * @param {Roo.bootstrap.DocumentViewer} this
29126          */
29127         "trash" : true
29128         
29129     });
29130 };
29131
29132 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29133     
29134     showDownload : true,
29135     
29136     showTrash : true,
29137     
29138     getAutoCreate : function()
29139     {
29140         var cfg = {
29141             tag : 'div',
29142             cls : 'roo-document-viewer',
29143             cn : [
29144                 {
29145                     tag : 'div',
29146                     cls : 'roo-document-viewer-body',
29147                     cn : [
29148                         {
29149                             tag : 'div',
29150                             cls : 'roo-document-viewer-thumb',
29151                             cn : [
29152                                 {
29153                                     tag : 'img',
29154                                     cls : 'roo-document-viewer-image'
29155                                 }
29156                             ]
29157                         }
29158                     ]
29159                 },
29160                 {
29161                     tag : 'div',
29162                     cls : 'roo-document-viewer-footer',
29163                     cn : {
29164                         tag : 'div',
29165                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29166                         cn : [
29167                             {
29168                                 tag : 'div',
29169                                 cls : 'btn-group roo-document-viewer-download',
29170                                 cn : [
29171                                     {
29172                                         tag : 'button',
29173                                         cls : 'btn btn-default',
29174                                         html : '<i class="fa fa-download"></i>'
29175                                     }
29176                                 ]
29177                             },
29178                             {
29179                                 tag : 'div',
29180                                 cls : 'btn-group roo-document-viewer-trash',
29181                                 cn : [
29182                                     {
29183                                         tag : 'button',
29184                                         cls : 'btn btn-default',
29185                                         html : '<i class="fa fa-trash"></i>'
29186                                     }
29187                                 ]
29188                             }
29189                         ]
29190                     }
29191                 }
29192             ]
29193         };
29194         
29195         return cfg;
29196     },
29197     
29198     initEvents : function()
29199     {
29200         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29201         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29202         
29203         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29204         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29205         
29206         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29207         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29208         
29209         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29210         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29211         
29212         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29213         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29214         
29215         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29216         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29217         
29218         this.bodyEl.on('click', this.onClick, this);
29219         this.downloadBtn.on('click', this.onDownload, this);
29220         this.trashBtn.on('click', this.onTrash, this);
29221         
29222         this.downloadBtn.hide();
29223         this.trashBtn.hide();
29224         
29225         if(this.showDownload){
29226             this.downloadBtn.show();
29227         }
29228         
29229         if(this.showTrash){
29230             this.trashBtn.show();
29231         }
29232         
29233         if(!this.showDownload && !this.showTrash) {
29234             this.footerEl.hide();
29235         }
29236         
29237     },
29238     
29239     initial : function()
29240     {
29241         this.fireEvent('initial', this);
29242         
29243     },
29244     
29245     onClick : function(e)
29246     {
29247         e.preventDefault();
29248         
29249         this.fireEvent('click', this);
29250     },
29251     
29252     onDownload : function(e)
29253     {
29254         e.preventDefault();
29255         
29256         this.fireEvent('download', this);
29257     },
29258     
29259     onTrash : function(e)
29260     {
29261         e.preventDefault();
29262         
29263         this.fireEvent('trash', this);
29264     }
29265     
29266 });
29267 /*
29268  * - LGPL
29269  *
29270  * nav progress bar
29271  * 
29272  */
29273
29274 /**
29275  * @class Roo.bootstrap.NavProgressBar
29276  * @extends Roo.bootstrap.Component
29277  * Bootstrap NavProgressBar class
29278  * 
29279  * @constructor
29280  * Create a new nav progress bar
29281  * @param {Object} config The config object
29282  */
29283
29284 Roo.bootstrap.NavProgressBar = function(config){
29285     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29286
29287     this.bullets = this.bullets || [];
29288    
29289 //    Roo.bootstrap.NavProgressBar.register(this);
29290      this.addEvents({
29291         /**
29292              * @event changed
29293              * Fires when the active item changes
29294              * @param {Roo.bootstrap.NavProgressBar} this
29295              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29296              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29297          */
29298         'changed': true
29299      });
29300     
29301 };
29302
29303 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29304     
29305     bullets : [],
29306     barItems : [],
29307     
29308     getAutoCreate : function()
29309     {
29310         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29311         
29312         cfg = {
29313             tag : 'div',
29314             cls : 'roo-navigation-bar-group',
29315             cn : [
29316                 {
29317                     tag : 'div',
29318                     cls : 'roo-navigation-top-bar'
29319                 },
29320                 {
29321                     tag : 'div',
29322                     cls : 'roo-navigation-bullets-bar',
29323                     cn : [
29324                         {
29325                             tag : 'ul',
29326                             cls : 'roo-navigation-bar'
29327                         }
29328                     ]
29329                 },
29330                 
29331                 {
29332                     tag : 'div',
29333                     cls : 'roo-navigation-bottom-bar'
29334                 }
29335             ]
29336             
29337         };
29338         
29339         return cfg;
29340         
29341     },
29342     
29343     initEvents: function() 
29344     {
29345         
29346     },
29347     
29348     onRender : function(ct, position) 
29349     {
29350         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29351         
29352         if(this.bullets.length){
29353             Roo.each(this.bullets, function(b){
29354                this.addItem(b);
29355             }, this);
29356         }
29357         
29358         this.format();
29359         
29360     },
29361     
29362     addItem : function(cfg)
29363     {
29364         var item = new Roo.bootstrap.NavProgressItem(cfg);
29365         
29366         item.parentId = this.id;
29367         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29368         
29369         if(cfg.html){
29370             var top = new Roo.bootstrap.Element({
29371                 tag : 'div',
29372                 cls : 'roo-navigation-bar-text'
29373             });
29374             
29375             var bottom = new Roo.bootstrap.Element({
29376                 tag : 'div',
29377                 cls : 'roo-navigation-bar-text'
29378             });
29379             
29380             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29381             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29382             
29383             var topText = new Roo.bootstrap.Element({
29384                 tag : 'span',
29385                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29386             });
29387             
29388             var bottomText = new Roo.bootstrap.Element({
29389                 tag : 'span',
29390                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29391             });
29392             
29393             topText.onRender(top.el, null);
29394             bottomText.onRender(bottom.el, null);
29395             
29396             item.topEl = top;
29397             item.bottomEl = bottom;
29398         }
29399         
29400         this.barItems.push(item);
29401         
29402         return item;
29403     },
29404     
29405     getActive : function()
29406     {
29407         var active = false;
29408         
29409         Roo.each(this.barItems, function(v){
29410             
29411             if (!v.isActive()) {
29412                 return;
29413             }
29414             
29415             active = v;
29416             return false;
29417             
29418         });
29419         
29420         return active;
29421     },
29422     
29423     setActiveItem : function(item)
29424     {
29425         var prev = false;
29426         
29427         Roo.each(this.barItems, function(v){
29428             if (v.rid == item.rid) {
29429                 return ;
29430             }
29431             
29432             if (v.isActive()) {
29433                 v.setActive(false);
29434                 prev = v;
29435             }
29436         });
29437
29438         item.setActive(true);
29439         
29440         this.fireEvent('changed', this, item, prev);
29441     },
29442     
29443     getBarItem: function(rid)
29444     {
29445         var ret = false;
29446         
29447         Roo.each(this.barItems, function(e) {
29448             if (e.rid != rid) {
29449                 return;
29450             }
29451             
29452             ret =  e;
29453             return false;
29454         });
29455         
29456         return ret;
29457     },
29458     
29459     indexOfItem : function(item)
29460     {
29461         var index = false;
29462         
29463         Roo.each(this.barItems, function(v, i){
29464             
29465             if (v.rid != item.rid) {
29466                 return;
29467             }
29468             
29469             index = i;
29470             return false
29471         });
29472         
29473         return index;
29474     },
29475     
29476     setActiveNext : function()
29477     {
29478         var i = this.indexOfItem(this.getActive());
29479         
29480         if (i > this.barItems.length) {
29481             return;
29482         }
29483         
29484         this.setActiveItem(this.barItems[i+1]);
29485     },
29486     
29487     setActivePrev : function()
29488     {
29489         var i = this.indexOfItem(this.getActive());
29490         
29491         if (i  < 1) {
29492             return;
29493         }
29494         
29495         this.setActiveItem(this.barItems[i-1]);
29496     },
29497     
29498     format : function()
29499     {
29500         if(!this.barItems.length){
29501             return;
29502         }
29503      
29504         var width = 100 / this.barItems.length;
29505         
29506         Roo.each(this.barItems, function(i){
29507             i.el.setStyle('width', width + '%');
29508             i.topEl.el.setStyle('width', width + '%');
29509             i.bottomEl.el.setStyle('width', width + '%');
29510         }, this);
29511         
29512     }
29513     
29514 });
29515 /*
29516  * - LGPL
29517  *
29518  * Nav Progress Item
29519  * 
29520  */
29521
29522 /**
29523  * @class Roo.bootstrap.NavProgressItem
29524  * @extends Roo.bootstrap.Component
29525  * Bootstrap NavProgressItem class
29526  * @cfg {String} rid the reference id
29527  * @cfg {Boolean} active (true|false) Is item active default false
29528  * @cfg {Boolean} disabled (true|false) Is item active default false
29529  * @cfg {String} html
29530  * @cfg {String} position (top|bottom) text position default bottom
29531  * @cfg {String} icon show icon instead of number
29532  * 
29533  * @constructor
29534  * Create a new NavProgressItem
29535  * @param {Object} config The config object
29536  */
29537 Roo.bootstrap.NavProgressItem = function(config){
29538     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29539     this.addEvents({
29540         // raw events
29541         /**
29542          * @event click
29543          * The raw click event for the entire grid.
29544          * @param {Roo.bootstrap.NavProgressItem} this
29545          * @param {Roo.EventObject} e
29546          */
29547         "click" : true
29548     });
29549    
29550 };
29551
29552 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29553     
29554     rid : '',
29555     active : false,
29556     disabled : false,
29557     html : '',
29558     position : 'bottom',
29559     icon : false,
29560     
29561     getAutoCreate : function()
29562     {
29563         var iconCls = 'roo-navigation-bar-item-icon';
29564         
29565         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29566         
29567         var cfg = {
29568             tag: 'li',
29569             cls: 'roo-navigation-bar-item',
29570             cn : [
29571                 {
29572                     tag : 'i',
29573                     cls : iconCls
29574                 }
29575             ]
29576         };
29577         
29578         if(this.active){
29579             cfg.cls += ' active';
29580         }
29581         if(this.disabled){
29582             cfg.cls += ' disabled';
29583         }
29584         
29585         return cfg;
29586     },
29587     
29588     disable : function()
29589     {
29590         this.setDisabled(true);
29591     },
29592     
29593     enable : function()
29594     {
29595         this.setDisabled(false);
29596     },
29597     
29598     initEvents: function() 
29599     {
29600         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29601         
29602         this.iconEl.on('click', this.onClick, this);
29603     },
29604     
29605     onClick : function(e)
29606     {
29607         e.preventDefault();
29608         
29609         if(this.disabled){
29610             return;
29611         }
29612         
29613         if(this.fireEvent('click', this, e) === false){
29614             return;
29615         };
29616         
29617         this.parent().setActiveItem(this);
29618     },
29619     
29620     isActive: function () 
29621     {
29622         return this.active;
29623     },
29624     
29625     setActive : function(state)
29626     {
29627         if(this.active == state){
29628             return;
29629         }
29630         
29631         this.active = state;
29632         
29633         if (state) {
29634             this.el.addClass('active');
29635             return;
29636         }
29637         
29638         this.el.removeClass('active');
29639         
29640         return;
29641     },
29642     
29643     setDisabled : function(state)
29644     {
29645         if(this.disabled == state){
29646             return;
29647         }
29648         
29649         this.disabled = state;
29650         
29651         if (state) {
29652             this.el.addClass('disabled');
29653             return;
29654         }
29655         
29656         this.el.removeClass('disabled');
29657     },
29658     
29659     tooltipEl : function()
29660     {
29661         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29662     }
29663 });
29664  
29665
29666  /*
29667  * - LGPL
29668  *
29669  * FieldLabel
29670  * 
29671  */
29672
29673 /**
29674  * @class Roo.bootstrap.FieldLabel
29675  * @extends Roo.bootstrap.Component
29676  * Bootstrap FieldLabel class
29677  * @cfg {String} html contents of the element
29678  * @cfg {String} tag tag of the element default label
29679  * @cfg {String} cls class of the element
29680  * @cfg {String} target label target 
29681  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29682  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29683  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29684  * @cfg {String} iconTooltip default "This field is required"
29685  * 
29686  * @constructor
29687  * Create a new FieldLabel
29688  * @param {Object} config The config object
29689  */
29690
29691 Roo.bootstrap.FieldLabel = function(config){
29692     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29693     
29694     this.addEvents({
29695             /**
29696              * @event invalid
29697              * Fires after the field has been marked as invalid.
29698              * @param {Roo.form.FieldLabel} this
29699              * @param {String} msg The validation message
29700              */
29701             invalid : true,
29702             /**
29703              * @event valid
29704              * Fires after the field has been validated with no errors.
29705              * @param {Roo.form.FieldLabel} this
29706              */
29707             valid : true
29708         });
29709 };
29710
29711 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29712     
29713     tag: 'label',
29714     cls: '',
29715     html: '',
29716     target: '',
29717     allowBlank : true,
29718     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29719     validClass : 'text-success fa fa-lg fa-check',
29720     iconTooltip : 'This field is required',
29721     
29722     getAutoCreate : function(){
29723         
29724         var cfg = {
29725             tag : this.tag,
29726             cls : 'roo-bootstrap-field-label ' + this.cls,
29727             for : this.target,
29728             cn : [
29729                 {
29730                     tag : 'i',
29731                     cls : '',
29732                     tooltip : this.iconTooltip
29733                 },
29734                 {
29735                     tag : 'span',
29736                     html : this.html
29737                 }
29738             ] 
29739         };
29740         
29741         return cfg;
29742     },
29743     
29744     initEvents: function() 
29745     {
29746         Roo.bootstrap.Element.superclass.initEvents.call(this);
29747         
29748         this.iconEl = this.el.select('i', true).first();
29749         
29750         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29751         
29752         Roo.bootstrap.FieldLabel.register(this);
29753     },
29754     
29755     /**
29756      * Mark this field as valid
29757      */
29758     markValid : function()
29759     {
29760         this.iconEl.show();
29761         
29762         this.iconEl.removeClass(this.invalidClass);
29763         
29764         this.iconEl.addClass(this.validClass);
29765         
29766         this.fireEvent('valid', this);
29767     },
29768     
29769     /**
29770      * Mark this field as invalid
29771      * @param {String} msg The validation message
29772      */
29773     markInvalid : function(msg)
29774     {
29775         this.iconEl.show();
29776         
29777         this.iconEl.removeClass(this.validClass);
29778         
29779         this.iconEl.addClass(this.invalidClass);
29780         
29781         this.fireEvent('invalid', this, msg);
29782     }
29783     
29784    
29785 });
29786
29787 Roo.apply(Roo.bootstrap.FieldLabel, {
29788     
29789     groups: {},
29790     
29791      /**
29792     * register a FieldLabel Group
29793     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29794     */
29795     register : function(label)
29796     {
29797         if(this.groups.hasOwnProperty(label.target)){
29798             return;
29799         }
29800      
29801         this.groups[label.target] = label;
29802         
29803     },
29804     /**
29805     * fetch a FieldLabel Group based on the target
29806     * @param {string} target
29807     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29808     */
29809     get: function(target) {
29810         if (typeof(this.groups[target]) == 'undefined') {
29811             return false;
29812         }
29813         
29814         return this.groups[target] ;
29815     }
29816 });
29817
29818  
29819
29820  /*
29821  * - LGPL
29822  *
29823  * page DateSplitField.
29824  * 
29825  */
29826
29827
29828 /**
29829  * @class Roo.bootstrap.DateSplitField
29830  * @extends Roo.bootstrap.Component
29831  * Bootstrap DateSplitField class
29832  * @cfg {string} fieldLabel - the label associated
29833  * @cfg {Number} labelWidth set the width of label (0-12)
29834  * @cfg {String} labelAlign (top|left)
29835  * @cfg {Boolean} dayAllowBlank (true|false) default false
29836  * @cfg {Boolean} monthAllowBlank (true|false) default false
29837  * @cfg {Boolean} yearAllowBlank (true|false) default false
29838  * @cfg {string} dayPlaceholder 
29839  * @cfg {string} monthPlaceholder
29840  * @cfg {string} yearPlaceholder
29841  * @cfg {string} dayFormat default 'd'
29842  * @cfg {string} monthFormat default 'm'
29843  * @cfg {string} yearFormat default 'Y'
29844  * @cfg {Number} labellg set the width of label (1-12)
29845  * @cfg {Number} labelmd set the width of label (1-12)
29846  * @cfg {Number} labelsm set the width of label (1-12)
29847  * @cfg {Number} labelxs set the width of label (1-12)
29848
29849  *     
29850  * @constructor
29851  * Create a new DateSplitField
29852  * @param {Object} config The config object
29853  */
29854
29855 Roo.bootstrap.DateSplitField = function(config){
29856     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29857     
29858     this.addEvents({
29859         // raw events
29860          /**
29861          * @event years
29862          * getting the data of years
29863          * @param {Roo.bootstrap.DateSplitField} this
29864          * @param {Object} years
29865          */
29866         "years" : true,
29867         /**
29868          * @event days
29869          * getting the data of days
29870          * @param {Roo.bootstrap.DateSplitField} this
29871          * @param {Object} days
29872          */
29873         "days" : true,
29874         /**
29875          * @event invalid
29876          * Fires after the field has been marked as invalid.
29877          * @param {Roo.form.Field} this
29878          * @param {String} msg The validation message
29879          */
29880         invalid : true,
29881        /**
29882          * @event valid
29883          * Fires after the field has been validated with no errors.
29884          * @param {Roo.form.Field} this
29885          */
29886         valid : true
29887     });
29888 };
29889
29890 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29891     
29892     fieldLabel : '',
29893     labelAlign : 'top',
29894     labelWidth : 3,
29895     dayAllowBlank : false,
29896     monthAllowBlank : false,
29897     yearAllowBlank : false,
29898     dayPlaceholder : '',
29899     monthPlaceholder : '',
29900     yearPlaceholder : '',
29901     dayFormat : 'd',
29902     monthFormat : 'm',
29903     yearFormat : 'Y',
29904     isFormField : true,
29905     labellg : 0,
29906     labelmd : 0,
29907     labelsm : 0,
29908     labelxs : 0,
29909     
29910     getAutoCreate : function()
29911     {
29912         var cfg = {
29913             tag : 'div',
29914             cls : 'row roo-date-split-field-group',
29915             cn : [
29916                 {
29917                     tag : 'input',
29918                     type : 'hidden',
29919                     cls : 'form-hidden-field roo-date-split-field-group-value',
29920                     name : this.name
29921                 }
29922             ]
29923         };
29924         
29925         var labelCls = 'col-md-12';
29926         var contentCls = 'col-md-4';
29927         
29928         if(this.fieldLabel){
29929             
29930             var label = {
29931                 tag : 'div',
29932                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29933                 cn : [
29934                     {
29935                         tag : 'label',
29936                         html : this.fieldLabel
29937                     }
29938                 ]
29939             };
29940             
29941             if(this.labelAlign == 'left'){
29942             
29943                 if(this.labelWidth > 12){
29944                     label.style = "width: " + this.labelWidth + 'px';
29945                 }
29946
29947                 if(this.labelWidth < 13 && this.labelmd == 0){
29948                     this.labelmd = this.labelWidth;
29949                 }
29950
29951                 if(this.labellg > 0){
29952                     labelCls = ' col-lg-' + this.labellg;
29953                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29954                 }
29955
29956                 if(this.labelmd > 0){
29957                     labelCls = ' col-md-' + this.labelmd;
29958                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29959                 }
29960
29961                 if(this.labelsm > 0){
29962                     labelCls = ' col-sm-' + this.labelsm;
29963                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29964                 }
29965
29966                 if(this.labelxs > 0){
29967                     labelCls = ' col-xs-' + this.labelxs;
29968                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29969                 }
29970             }
29971             
29972             label.cls += ' ' + labelCls;
29973             
29974             cfg.cn.push(label);
29975         }
29976         
29977         Roo.each(['day', 'month', 'year'], function(t){
29978             cfg.cn.push({
29979                 tag : 'div',
29980                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29981             });
29982         }, this);
29983         
29984         return cfg;
29985     },
29986     
29987     inputEl: function ()
29988     {
29989         return this.el.select('.roo-date-split-field-group-value', true).first();
29990     },
29991     
29992     onRender : function(ct, position) 
29993     {
29994         var _this = this;
29995         
29996         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29997         
29998         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29999         
30000         this.dayField = new Roo.bootstrap.ComboBox({
30001             allowBlank : this.dayAllowBlank,
30002             alwaysQuery : true,
30003             displayField : 'value',
30004             editable : false,
30005             fieldLabel : '',
30006             forceSelection : true,
30007             mode : 'local',
30008             placeholder : this.dayPlaceholder,
30009             selectOnFocus : true,
30010             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30011             triggerAction : 'all',
30012             typeAhead : true,
30013             valueField : 'value',
30014             store : new Roo.data.SimpleStore({
30015                 data : (function() {    
30016                     var days = [];
30017                     _this.fireEvent('days', _this, days);
30018                     return days;
30019                 })(),
30020                 fields : [ 'value' ]
30021             }),
30022             listeners : {
30023                 select : function (_self, record, index)
30024                 {
30025                     _this.setValue(_this.getValue());
30026                 }
30027             }
30028         });
30029
30030         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30031         
30032         this.monthField = new Roo.bootstrap.MonthField({
30033             after : '<i class=\"fa fa-calendar\"></i>',
30034             allowBlank : this.monthAllowBlank,
30035             placeholder : this.monthPlaceholder,
30036             readOnly : true,
30037             listeners : {
30038                 render : function (_self)
30039                 {
30040                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30041                         e.preventDefault();
30042                         _self.focus();
30043                     });
30044                 },
30045                 select : function (_self, oldvalue, newvalue)
30046                 {
30047                     _this.setValue(_this.getValue());
30048                 }
30049             }
30050         });
30051         
30052         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30053         
30054         this.yearField = new Roo.bootstrap.ComboBox({
30055             allowBlank : this.yearAllowBlank,
30056             alwaysQuery : true,
30057             displayField : 'value',
30058             editable : false,
30059             fieldLabel : '',
30060             forceSelection : true,
30061             mode : 'local',
30062             placeholder : this.yearPlaceholder,
30063             selectOnFocus : true,
30064             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30065             triggerAction : 'all',
30066             typeAhead : true,
30067             valueField : 'value',
30068             store : new Roo.data.SimpleStore({
30069                 data : (function() {
30070                     var years = [];
30071                     _this.fireEvent('years', _this, years);
30072                     return years;
30073                 })(),
30074                 fields : [ 'value' ]
30075             }),
30076             listeners : {
30077                 select : function (_self, record, index)
30078                 {
30079                     _this.setValue(_this.getValue());
30080                 }
30081             }
30082         });
30083
30084         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30085     },
30086     
30087     setValue : function(v, format)
30088     {
30089         this.inputEl.dom.value = v;
30090         
30091         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30092         
30093         var d = Date.parseDate(v, f);
30094         
30095         if(!d){
30096             this.validate();
30097             return;
30098         }
30099         
30100         this.setDay(d.format(this.dayFormat));
30101         this.setMonth(d.format(this.monthFormat));
30102         this.setYear(d.format(this.yearFormat));
30103         
30104         this.validate();
30105         
30106         return;
30107     },
30108     
30109     setDay : function(v)
30110     {
30111         this.dayField.setValue(v);
30112         this.inputEl.dom.value = this.getValue();
30113         this.validate();
30114         return;
30115     },
30116     
30117     setMonth : function(v)
30118     {
30119         this.monthField.setValue(v, true);
30120         this.inputEl.dom.value = this.getValue();
30121         this.validate();
30122         return;
30123     },
30124     
30125     setYear : function(v)
30126     {
30127         this.yearField.setValue(v);
30128         this.inputEl.dom.value = this.getValue();
30129         this.validate();
30130         return;
30131     },
30132     
30133     getDay : function()
30134     {
30135         return this.dayField.getValue();
30136     },
30137     
30138     getMonth : function()
30139     {
30140         return this.monthField.getValue();
30141     },
30142     
30143     getYear : function()
30144     {
30145         return this.yearField.getValue();
30146     },
30147     
30148     getValue : function()
30149     {
30150         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30151         
30152         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30153         
30154         return date;
30155     },
30156     
30157     reset : function()
30158     {
30159         this.setDay('');
30160         this.setMonth('');
30161         this.setYear('');
30162         this.inputEl.dom.value = '';
30163         this.validate();
30164         return;
30165     },
30166     
30167     validate : function()
30168     {
30169         var d = this.dayField.validate();
30170         var m = this.monthField.validate();
30171         var y = this.yearField.validate();
30172         
30173         var valid = true;
30174         
30175         if(
30176                 (!this.dayAllowBlank && !d) ||
30177                 (!this.monthAllowBlank && !m) ||
30178                 (!this.yearAllowBlank && !y)
30179         ){
30180             valid = false;
30181         }
30182         
30183         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30184             return valid;
30185         }
30186         
30187         if(valid){
30188             this.markValid();
30189             return valid;
30190         }
30191         
30192         this.markInvalid();
30193         
30194         return valid;
30195     },
30196     
30197     markValid : function()
30198     {
30199         
30200         var label = this.el.select('label', true).first();
30201         var icon = this.el.select('i.fa-star', true).first();
30202
30203         if(label && icon){
30204             icon.remove();
30205         }
30206         
30207         this.fireEvent('valid', this);
30208     },
30209     
30210      /**
30211      * Mark this field as invalid
30212      * @param {String} msg The validation message
30213      */
30214     markInvalid : function(msg)
30215     {
30216         
30217         var label = this.el.select('label', true).first();
30218         var icon = this.el.select('i.fa-star', true).first();
30219
30220         if(label && !icon){
30221             this.el.select('.roo-date-split-field-label', true).createChild({
30222                 tag : 'i',
30223                 cls : 'text-danger fa fa-lg fa-star',
30224                 tooltip : 'This field is required',
30225                 style : 'margin-right:5px;'
30226             }, label, true);
30227         }
30228         
30229         this.fireEvent('invalid', this, msg);
30230     },
30231     
30232     clearInvalid : function()
30233     {
30234         var label = this.el.select('label', true).first();
30235         var icon = this.el.select('i.fa-star', true).first();
30236
30237         if(label && icon){
30238             icon.remove();
30239         }
30240         
30241         this.fireEvent('valid', this);
30242     },
30243     
30244     getName: function()
30245     {
30246         return this.name;
30247     }
30248     
30249 });
30250
30251  /**
30252  *
30253  * This is based on 
30254  * http://masonry.desandro.com
30255  *
30256  * The idea is to render all the bricks based on vertical width...
30257  *
30258  * The original code extends 'outlayer' - we might need to use that....
30259  * 
30260  */
30261
30262
30263 /**
30264  * @class Roo.bootstrap.LayoutMasonry
30265  * @extends Roo.bootstrap.Component
30266  * Bootstrap Layout Masonry class
30267  * 
30268  * @constructor
30269  * Create a new Element
30270  * @param {Object} config The config object
30271  */
30272
30273 Roo.bootstrap.LayoutMasonry = function(config){
30274     
30275     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30276     
30277     this.bricks = [];
30278     
30279     Roo.bootstrap.LayoutMasonry.register(this);
30280     
30281     this.addEvents({
30282         // raw events
30283         /**
30284          * @event layout
30285          * Fire after layout the items
30286          * @param {Roo.bootstrap.LayoutMasonry} this
30287          * @param {Roo.EventObject} e
30288          */
30289         "layout" : true
30290     });
30291     
30292 };
30293
30294 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30295     
30296     /**
30297      * @cfg {Boolean} isLayoutInstant = no animation?
30298      */   
30299     isLayoutInstant : false, // needed?
30300    
30301     /**
30302      * @cfg {Number} boxWidth  width of the columns
30303      */   
30304     boxWidth : 450,
30305     
30306       /**
30307      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30308      */   
30309     boxHeight : 0,
30310     
30311     /**
30312      * @cfg {Number} padWidth padding below box..
30313      */   
30314     padWidth : 10, 
30315     
30316     /**
30317      * @cfg {Number} gutter gutter width..
30318      */   
30319     gutter : 10,
30320     
30321      /**
30322      * @cfg {Number} maxCols maximum number of columns
30323      */   
30324     
30325     maxCols: 0,
30326     
30327     /**
30328      * @cfg {Boolean} isAutoInitial defalut true
30329      */   
30330     isAutoInitial : true, 
30331     
30332     containerWidth: 0,
30333     
30334     /**
30335      * @cfg {Boolean} isHorizontal defalut false
30336      */   
30337     isHorizontal : false, 
30338
30339     currentSize : null,
30340     
30341     tag: 'div',
30342     
30343     cls: '',
30344     
30345     bricks: null, //CompositeElement
30346     
30347     cols : 1,
30348     
30349     _isLayoutInited : false,
30350     
30351 //    isAlternative : false, // only use for vertical layout...
30352     
30353     /**
30354      * @cfg {Number} alternativePadWidth padding below box..
30355      */   
30356     alternativePadWidth : 50,
30357     
30358     selectedBrick : [],
30359     
30360     getAutoCreate : function(){
30361         
30362         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30363         
30364         var cfg = {
30365             tag: this.tag,
30366             cls: 'blog-masonary-wrapper ' + this.cls,
30367             cn : {
30368                 cls : 'mas-boxes masonary'
30369             }
30370         };
30371         
30372         return cfg;
30373     },
30374     
30375     getChildContainer: function( )
30376     {
30377         if (this.boxesEl) {
30378             return this.boxesEl;
30379         }
30380         
30381         this.boxesEl = this.el.select('.mas-boxes').first();
30382         
30383         return this.boxesEl;
30384     },
30385     
30386     
30387     initEvents : function()
30388     {
30389         var _this = this;
30390         
30391         if(this.isAutoInitial){
30392             Roo.log('hook children rendered');
30393             this.on('childrenrendered', function() {
30394                 Roo.log('children rendered');
30395                 _this.initial();
30396             } ,this);
30397         }
30398     },
30399     
30400     initial : function()
30401     {
30402         this.selectedBrick = [];
30403         
30404         this.currentSize = this.el.getBox(true);
30405         
30406         Roo.EventManager.onWindowResize(this.resize, this); 
30407
30408         if(!this.isAutoInitial){
30409             this.layout();
30410             return;
30411         }
30412         
30413         this.layout();
30414         
30415         return;
30416         //this.layout.defer(500,this);
30417         
30418     },
30419     
30420     resize : function()
30421     {
30422         var cs = this.el.getBox(true);
30423         
30424         if (
30425                 this.currentSize.width == cs.width && 
30426                 this.currentSize.x == cs.x && 
30427                 this.currentSize.height == cs.height && 
30428                 this.currentSize.y == cs.y 
30429         ) {
30430             Roo.log("no change in with or X or Y");
30431             return;
30432         }
30433         
30434         this.currentSize = cs;
30435         
30436         this.layout();
30437         
30438     },
30439     
30440     layout : function()
30441     {   
30442         this._resetLayout();
30443         
30444         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30445         
30446         this.layoutItems( isInstant );
30447       
30448         this._isLayoutInited = true;
30449         
30450         this.fireEvent('layout', this);
30451         
30452     },
30453     
30454     _resetLayout : function()
30455     {
30456         if(this.isHorizontal){
30457             this.horizontalMeasureColumns();
30458             return;
30459         }
30460         
30461         this.verticalMeasureColumns();
30462         
30463     },
30464     
30465     verticalMeasureColumns : function()
30466     {
30467         this.getContainerWidth();
30468         
30469 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30470 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30471 //            return;
30472 //        }
30473         
30474         var boxWidth = this.boxWidth + this.padWidth;
30475         
30476         if(this.containerWidth < this.boxWidth){
30477             boxWidth = this.containerWidth
30478         }
30479         
30480         var containerWidth = this.containerWidth;
30481         
30482         var cols = Math.floor(containerWidth / boxWidth);
30483         
30484         this.cols = Math.max( cols, 1 );
30485         
30486         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30487         
30488         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30489         
30490         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30491         
30492         this.colWidth = boxWidth + avail - this.padWidth;
30493         
30494         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30495         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30496     },
30497     
30498     horizontalMeasureColumns : function()
30499     {
30500         this.getContainerWidth();
30501         
30502         var boxWidth = this.boxWidth;
30503         
30504         if(this.containerWidth < boxWidth){
30505             boxWidth = this.containerWidth;
30506         }
30507         
30508         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30509         
30510         this.el.setHeight(boxWidth);
30511         
30512     },
30513     
30514     getContainerWidth : function()
30515     {
30516         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30517     },
30518     
30519     layoutItems : function( isInstant )
30520     {
30521         Roo.log(this.bricks);
30522         
30523         var items = Roo.apply([], this.bricks);
30524         
30525         if(this.isHorizontal){
30526             this._horizontalLayoutItems( items , isInstant );
30527             return;
30528         }
30529         
30530 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30531 //            this._verticalAlternativeLayoutItems( items , isInstant );
30532 //            return;
30533 //        }
30534         
30535         this._verticalLayoutItems( items , isInstant );
30536         
30537     },
30538     
30539     _verticalLayoutItems : function ( items , isInstant)
30540     {
30541         if ( !items || !items.length ) {
30542             return;
30543         }
30544         
30545         var standard = [
30546             ['xs', 'xs', 'xs', 'tall'],
30547             ['xs', 'xs', 'tall'],
30548             ['xs', 'xs', 'sm'],
30549             ['xs', 'xs', 'xs'],
30550             ['xs', 'tall'],
30551             ['xs', 'sm'],
30552             ['xs', 'xs'],
30553             ['xs'],
30554             
30555             ['sm', 'xs', 'xs'],
30556             ['sm', 'xs'],
30557             ['sm'],
30558             
30559             ['tall', 'xs', 'xs', 'xs'],
30560             ['tall', 'xs', 'xs'],
30561             ['tall', 'xs'],
30562             ['tall']
30563             
30564         ];
30565         
30566         var queue = [];
30567         
30568         var boxes = [];
30569         
30570         var box = [];
30571         
30572         Roo.each(items, function(item, k){
30573             
30574             switch (item.size) {
30575                 // these layouts take up a full box,
30576                 case 'md' :
30577                 case 'md-left' :
30578                 case 'md-right' :
30579                 case 'wide' :
30580                     
30581                     if(box.length){
30582                         boxes.push(box);
30583                         box = [];
30584                     }
30585                     
30586                     boxes.push([item]);
30587                     
30588                     break;
30589                     
30590                 case 'xs' :
30591                 case 'sm' :
30592                 case 'tall' :
30593                     
30594                     box.push(item);
30595                     
30596                     break;
30597                 default :
30598                     break;
30599                     
30600             }
30601             
30602         }, this);
30603         
30604         if(box.length){
30605             boxes.push(box);
30606             box = [];
30607         }
30608         
30609         var filterPattern = function(box, length)
30610         {
30611             if(!box.length){
30612                 return;
30613             }
30614             
30615             var match = false;
30616             
30617             var pattern = box.slice(0, length);
30618             
30619             var format = [];
30620             
30621             Roo.each(pattern, function(i){
30622                 format.push(i.size);
30623             }, this);
30624             
30625             Roo.each(standard, function(s){
30626                 
30627                 if(String(s) != String(format)){
30628                     return;
30629                 }
30630                 
30631                 match = true;
30632                 return false;
30633                 
30634             }, this);
30635             
30636             if(!match && length == 1){
30637                 return;
30638             }
30639             
30640             if(!match){
30641                 filterPattern(box, length - 1);
30642                 return;
30643             }
30644                 
30645             queue.push(pattern);
30646
30647             box = box.slice(length, box.length);
30648
30649             filterPattern(box, 4);
30650
30651             return;
30652             
30653         }
30654         
30655         Roo.each(boxes, function(box, k){
30656             
30657             if(!box.length){
30658                 return;
30659             }
30660             
30661             if(box.length == 1){
30662                 queue.push(box);
30663                 return;
30664             }
30665             
30666             filterPattern(box, 4);
30667             
30668         }, this);
30669         
30670         this._processVerticalLayoutQueue( queue, isInstant );
30671         
30672     },
30673     
30674 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30675 //    {
30676 //        if ( !items || !items.length ) {
30677 //            return;
30678 //        }
30679 //
30680 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30681 //        
30682 //    },
30683     
30684     _horizontalLayoutItems : function ( items , isInstant)
30685     {
30686         if ( !items || !items.length || items.length < 3) {
30687             return;
30688         }
30689         
30690         items.reverse();
30691         
30692         var eItems = items.slice(0, 3);
30693         
30694         items = items.slice(3, items.length);
30695         
30696         var standard = [
30697             ['xs', 'xs', 'xs', 'wide'],
30698             ['xs', 'xs', 'wide'],
30699             ['xs', 'xs', 'sm'],
30700             ['xs', 'xs', 'xs'],
30701             ['xs', 'wide'],
30702             ['xs', 'sm'],
30703             ['xs', 'xs'],
30704             ['xs'],
30705             
30706             ['sm', 'xs', 'xs'],
30707             ['sm', 'xs'],
30708             ['sm'],
30709             
30710             ['wide', 'xs', 'xs', 'xs'],
30711             ['wide', 'xs', 'xs'],
30712             ['wide', 'xs'],
30713             ['wide'],
30714             
30715             ['wide-thin']
30716         ];
30717         
30718         var queue = [];
30719         
30720         var boxes = [];
30721         
30722         var box = [];
30723         
30724         Roo.each(items, function(item, k){
30725             
30726             switch (item.size) {
30727                 case 'md' :
30728                 case 'md-left' :
30729                 case 'md-right' :
30730                 case 'tall' :
30731                     
30732                     if(box.length){
30733                         boxes.push(box);
30734                         box = [];
30735                     }
30736                     
30737                     boxes.push([item]);
30738                     
30739                     break;
30740                     
30741                 case 'xs' :
30742                 case 'sm' :
30743                 case 'wide' :
30744                 case 'wide-thin' :
30745                     
30746                     box.push(item);
30747                     
30748                     break;
30749                 default :
30750                     break;
30751                     
30752             }
30753             
30754         }, this);
30755         
30756         if(box.length){
30757             boxes.push(box);
30758             box = [];
30759         }
30760         
30761         var filterPattern = function(box, length)
30762         {
30763             if(!box.length){
30764                 return;
30765             }
30766             
30767             var match = false;
30768             
30769             var pattern = box.slice(0, length);
30770             
30771             var format = [];
30772             
30773             Roo.each(pattern, function(i){
30774                 format.push(i.size);
30775             }, this);
30776             
30777             Roo.each(standard, function(s){
30778                 
30779                 if(String(s) != String(format)){
30780                     return;
30781                 }
30782                 
30783                 match = true;
30784                 return false;
30785                 
30786             }, this);
30787             
30788             if(!match && length == 1){
30789                 return;
30790             }
30791             
30792             if(!match){
30793                 filterPattern(box, length - 1);
30794                 return;
30795             }
30796                 
30797             queue.push(pattern);
30798
30799             box = box.slice(length, box.length);
30800
30801             filterPattern(box, 4);
30802
30803             return;
30804             
30805         }
30806         
30807         Roo.each(boxes, function(box, k){
30808             
30809             if(!box.length){
30810                 return;
30811             }
30812             
30813             if(box.length == 1){
30814                 queue.push(box);
30815                 return;
30816             }
30817             
30818             filterPattern(box, 4);
30819             
30820         }, this);
30821         
30822         
30823         var prune = [];
30824         
30825         var pos = this.el.getBox(true);
30826         
30827         var minX = pos.x;
30828         
30829         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30830         
30831         var hit_end = false;
30832         
30833         Roo.each(queue, function(box){
30834             
30835             if(hit_end){
30836                 
30837                 Roo.each(box, function(b){
30838                 
30839                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30840                     b.el.hide();
30841
30842                 }, this);
30843
30844                 return;
30845             }
30846             
30847             var mx = 0;
30848             
30849             Roo.each(box, function(b){
30850                 
30851                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30852                 b.el.show();
30853
30854                 mx = Math.max(mx, b.x);
30855                 
30856             }, this);
30857             
30858             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30859             
30860             if(maxX < minX){
30861                 
30862                 Roo.each(box, function(b){
30863                 
30864                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30865                     b.el.hide();
30866                     
30867                 }, this);
30868                 
30869                 hit_end = true;
30870                 
30871                 return;
30872             }
30873             
30874             prune.push(box);
30875             
30876         }, this);
30877         
30878         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30879     },
30880     
30881     /** Sets position of item in DOM
30882     * @param {Element} item
30883     * @param {Number} x - horizontal position
30884     * @param {Number} y - vertical position
30885     * @param {Boolean} isInstant - disables transitions
30886     */
30887     _processVerticalLayoutQueue : function( queue, isInstant )
30888     {
30889         var pos = this.el.getBox(true);
30890         var x = pos.x;
30891         var y = pos.y;
30892         var maxY = [];
30893         
30894         for (var i = 0; i < this.cols; i++){
30895             maxY[i] = pos.y;
30896         }
30897         
30898         Roo.each(queue, function(box, k){
30899             
30900             var col = k % this.cols;
30901             
30902             Roo.each(box, function(b,kk){
30903                 
30904                 b.el.position('absolute');
30905                 
30906                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30907                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30908                 
30909                 if(b.size == 'md-left' || b.size == 'md-right'){
30910                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30911                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30912                 }
30913                 
30914                 b.el.setWidth(width);
30915                 b.el.setHeight(height);
30916                 // iframe?
30917                 b.el.select('iframe',true).setSize(width,height);
30918                 
30919             }, this);
30920             
30921             for (var i = 0; i < this.cols; i++){
30922                 
30923                 if(maxY[i] < maxY[col]){
30924                     col = i;
30925                     continue;
30926                 }
30927                 
30928                 col = Math.min(col, i);
30929                 
30930             }
30931             
30932             x = pos.x + col * (this.colWidth + this.padWidth);
30933             
30934             y = maxY[col];
30935             
30936             var positions = [];
30937             
30938             switch (box.length){
30939                 case 1 :
30940                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30941                     break;
30942                 case 2 :
30943                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30944                     break;
30945                 case 3 :
30946                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30947                     break;
30948                 case 4 :
30949                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30950                     break;
30951                 default :
30952                     break;
30953             }
30954             
30955             Roo.each(box, function(b,kk){
30956                 
30957                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30958                 
30959                 var sz = b.el.getSize();
30960                 
30961                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30962                 
30963             }, this);
30964             
30965         }, this);
30966         
30967         var mY = 0;
30968         
30969         for (var i = 0; i < this.cols; i++){
30970             mY = Math.max(mY, maxY[i]);
30971         }
30972         
30973         this.el.setHeight(mY - pos.y);
30974         
30975     },
30976     
30977 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30978 //    {
30979 //        var pos = this.el.getBox(true);
30980 //        var x = pos.x;
30981 //        var y = pos.y;
30982 //        var maxX = pos.right;
30983 //        
30984 //        var maxHeight = 0;
30985 //        
30986 //        Roo.each(items, function(item, k){
30987 //            
30988 //            var c = k % 2;
30989 //            
30990 //            item.el.position('absolute');
30991 //                
30992 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30993 //
30994 //            item.el.setWidth(width);
30995 //
30996 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30997 //
30998 //            item.el.setHeight(height);
30999 //            
31000 //            if(c == 0){
31001 //                item.el.setXY([x, y], isInstant ? false : true);
31002 //            } else {
31003 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31004 //            }
31005 //            
31006 //            y = y + height + this.alternativePadWidth;
31007 //            
31008 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31009 //            
31010 //        }, this);
31011 //        
31012 //        this.el.setHeight(maxHeight);
31013 //        
31014 //    },
31015     
31016     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31017     {
31018         var pos = this.el.getBox(true);
31019         
31020         var minX = pos.x;
31021         var minY = pos.y;
31022         
31023         var maxX = pos.right;
31024         
31025         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31026         
31027         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31028         
31029         Roo.each(queue, function(box, k){
31030             
31031             Roo.each(box, function(b, kk){
31032                 
31033                 b.el.position('absolute');
31034                 
31035                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31036                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31037                 
31038                 if(b.size == 'md-left' || b.size == 'md-right'){
31039                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31040                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31041                 }
31042                 
31043                 b.el.setWidth(width);
31044                 b.el.setHeight(height);
31045                 
31046             }, this);
31047             
31048             if(!box.length){
31049                 return;
31050             }
31051             
31052             var positions = [];
31053             
31054             switch (box.length){
31055                 case 1 :
31056                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31057                     break;
31058                 case 2 :
31059                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31060                     break;
31061                 case 3 :
31062                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31063                     break;
31064                 case 4 :
31065                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31066                     break;
31067                 default :
31068                     break;
31069             }
31070             
31071             Roo.each(box, function(b,kk){
31072                 
31073                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31074                 
31075                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31076                 
31077             }, this);
31078             
31079         }, this);
31080         
31081     },
31082     
31083     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31084     {
31085         Roo.each(eItems, function(b,k){
31086             
31087             b.size = (k == 0) ? 'sm' : 'xs';
31088             b.x = (k == 0) ? 2 : 1;
31089             b.y = (k == 0) ? 2 : 1;
31090             
31091             b.el.position('absolute');
31092             
31093             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31094                 
31095             b.el.setWidth(width);
31096             
31097             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31098             
31099             b.el.setHeight(height);
31100             
31101         }, this);
31102
31103         var positions = [];
31104         
31105         positions.push({
31106             x : maxX - this.unitWidth * 2 - this.gutter,
31107             y : minY
31108         });
31109         
31110         positions.push({
31111             x : maxX - this.unitWidth,
31112             y : minY + (this.unitWidth + this.gutter) * 2
31113         });
31114         
31115         positions.push({
31116             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31117             y : minY
31118         });
31119         
31120         Roo.each(eItems, function(b,k){
31121             
31122             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31123
31124         }, this);
31125         
31126     },
31127     
31128     getVerticalOneBoxColPositions : function(x, y, box)
31129     {
31130         var pos = [];
31131         
31132         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31133         
31134         if(box[0].size == 'md-left'){
31135             rand = 0;
31136         }
31137         
31138         if(box[0].size == 'md-right'){
31139             rand = 1;
31140         }
31141         
31142         pos.push({
31143             x : x + (this.unitWidth + this.gutter) * rand,
31144             y : y
31145         });
31146         
31147         return pos;
31148     },
31149     
31150     getVerticalTwoBoxColPositions : function(x, y, box)
31151     {
31152         var pos = [];
31153         
31154         if(box[0].size == 'xs'){
31155             
31156             pos.push({
31157                 x : x,
31158                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31159             });
31160
31161             pos.push({
31162                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31163                 y : y
31164             });
31165             
31166             return pos;
31167             
31168         }
31169         
31170         pos.push({
31171             x : x,
31172             y : y
31173         });
31174
31175         pos.push({
31176             x : x + (this.unitWidth + this.gutter) * 2,
31177             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31178         });
31179         
31180         return pos;
31181         
31182     },
31183     
31184     getVerticalThreeBoxColPositions : function(x, y, box)
31185     {
31186         var pos = [];
31187         
31188         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31189             
31190             pos.push({
31191                 x : x,
31192                 y : y
31193             });
31194
31195             pos.push({
31196                 x : x + (this.unitWidth + this.gutter) * 1,
31197                 y : y
31198             });
31199             
31200             pos.push({
31201                 x : x + (this.unitWidth + this.gutter) * 2,
31202                 y : y
31203             });
31204             
31205             return pos;
31206             
31207         }
31208         
31209         if(box[0].size == 'xs' && box[1].size == 'xs'){
31210             
31211             pos.push({
31212                 x : x,
31213                 y : y
31214             });
31215
31216             pos.push({
31217                 x : x,
31218                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31219             });
31220             
31221             pos.push({
31222                 x : x + (this.unitWidth + this.gutter) * 1,
31223                 y : y
31224             });
31225             
31226             return pos;
31227             
31228         }
31229         
31230         pos.push({
31231             x : x,
31232             y : y
31233         });
31234
31235         pos.push({
31236             x : x + (this.unitWidth + this.gutter) * 2,
31237             y : y
31238         });
31239
31240         pos.push({
31241             x : x + (this.unitWidth + this.gutter) * 2,
31242             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31243         });
31244             
31245         return pos;
31246         
31247     },
31248     
31249     getVerticalFourBoxColPositions : function(x, y, box)
31250     {
31251         var pos = [];
31252         
31253         if(box[0].size == 'xs'){
31254             
31255             pos.push({
31256                 x : x,
31257                 y : y
31258             });
31259
31260             pos.push({
31261                 x : x,
31262                 y : y + (this.unitHeight + this.gutter) * 1
31263             });
31264             
31265             pos.push({
31266                 x : x,
31267                 y : y + (this.unitHeight + this.gutter) * 2
31268             });
31269             
31270             pos.push({
31271                 x : x + (this.unitWidth + this.gutter) * 1,
31272                 y : y
31273             });
31274             
31275             return pos;
31276             
31277         }
31278         
31279         pos.push({
31280             x : x,
31281             y : y
31282         });
31283
31284         pos.push({
31285             x : x + (this.unitWidth + this.gutter) * 2,
31286             y : y
31287         });
31288
31289         pos.push({
31290             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31291             y : y + (this.unitHeight + this.gutter) * 1
31292         });
31293
31294         pos.push({
31295             x : x + (this.unitWidth + this.gutter) * 2,
31296             y : y + (this.unitWidth + this.gutter) * 2
31297         });
31298
31299         return pos;
31300         
31301     },
31302     
31303     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31304     {
31305         var pos = [];
31306         
31307         if(box[0].size == 'md-left'){
31308             pos.push({
31309                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31310                 y : minY
31311             });
31312             
31313             return pos;
31314         }
31315         
31316         if(box[0].size == 'md-right'){
31317             pos.push({
31318                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31319                 y : minY + (this.unitWidth + this.gutter) * 1
31320             });
31321             
31322             return pos;
31323         }
31324         
31325         var rand = Math.floor(Math.random() * (4 - box[0].y));
31326         
31327         pos.push({
31328             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31329             y : minY + (this.unitWidth + this.gutter) * rand
31330         });
31331         
31332         return pos;
31333         
31334     },
31335     
31336     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31337     {
31338         var pos = [];
31339         
31340         if(box[0].size == 'xs'){
31341             
31342             pos.push({
31343                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31344                 y : minY
31345             });
31346
31347             pos.push({
31348                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31349                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31350             });
31351             
31352             return pos;
31353             
31354         }
31355         
31356         pos.push({
31357             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31358             y : minY
31359         });
31360
31361         pos.push({
31362             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31363             y : minY + (this.unitWidth + this.gutter) * 2
31364         });
31365         
31366         return pos;
31367         
31368     },
31369     
31370     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31371     {
31372         var pos = [];
31373         
31374         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31375             
31376             pos.push({
31377                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31378                 y : minY
31379             });
31380
31381             pos.push({
31382                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31383                 y : minY + (this.unitWidth + this.gutter) * 1
31384             });
31385             
31386             pos.push({
31387                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31388                 y : minY + (this.unitWidth + this.gutter) * 2
31389             });
31390             
31391             return pos;
31392             
31393         }
31394         
31395         if(box[0].size == 'xs' && box[1].size == 'xs'){
31396             
31397             pos.push({
31398                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31399                 y : minY
31400             });
31401
31402             pos.push({
31403                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31404                 y : minY
31405             });
31406             
31407             pos.push({
31408                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31409                 y : minY + (this.unitWidth + this.gutter) * 1
31410             });
31411             
31412             return pos;
31413             
31414         }
31415         
31416         pos.push({
31417             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31418             y : minY
31419         });
31420
31421         pos.push({
31422             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31423             y : minY + (this.unitWidth + this.gutter) * 2
31424         });
31425
31426         pos.push({
31427             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31428             y : minY + (this.unitWidth + this.gutter) * 2
31429         });
31430             
31431         return pos;
31432         
31433     },
31434     
31435     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31436     {
31437         var pos = [];
31438         
31439         if(box[0].size == 'xs'){
31440             
31441             pos.push({
31442                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31443                 y : minY
31444             });
31445
31446             pos.push({
31447                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31448                 y : minY
31449             });
31450             
31451             pos.push({
31452                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31453                 y : minY
31454             });
31455             
31456             pos.push({
31457                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31458                 y : minY + (this.unitWidth + this.gutter) * 1
31459             });
31460             
31461             return pos;
31462             
31463         }
31464         
31465         pos.push({
31466             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31467             y : minY
31468         });
31469         
31470         pos.push({
31471             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31472             y : minY + (this.unitWidth + this.gutter) * 2
31473         });
31474         
31475         pos.push({
31476             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31477             y : minY + (this.unitWidth + this.gutter) * 2
31478         });
31479         
31480         pos.push({
31481             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31482             y : minY + (this.unitWidth + this.gutter) * 2
31483         });
31484
31485         return pos;
31486         
31487     },
31488     
31489     /**
31490     * remove a Masonry Brick
31491     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31492     */
31493     removeBrick : function(brick_id)
31494     {
31495         if (!brick_id) {
31496             return;
31497         }
31498         
31499         for (var i = 0; i<this.bricks.length; i++) {
31500             if (this.bricks[i].id == brick_id) {
31501                 this.bricks.splice(i,1);
31502                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31503                 this.initial();
31504             }
31505         }
31506     },
31507     
31508     /**
31509     * adds a Masonry Brick
31510     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31511     */
31512     addBrick : function(cfg)
31513     {
31514         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31515         //this.register(cn);
31516         cn.parentId = this.id;
31517         cn.onRender(this.el, null);
31518         return cn;
31519     },
31520     
31521     /**
31522     * register a Masonry Brick
31523     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31524     */
31525     
31526     register : function(brick)
31527     {
31528         this.bricks.push(brick);
31529         brick.masonryId = this.id;
31530     },
31531     
31532     /**
31533     * clear all the Masonry Brick
31534     */
31535     clearAll : function()
31536     {
31537         this.bricks = [];
31538         //this.getChildContainer().dom.innerHTML = "";
31539         this.el.dom.innerHTML = '';
31540     },
31541     
31542     getSelected : function()
31543     {
31544         if (!this.selectedBrick) {
31545             return false;
31546         }
31547         
31548         return this.selectedBrick;
31549     }
31550 });
31551
31552 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31553     
31554     groups: {},
31555      /**
31556     * register a Masonry Layout
31557     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31558     */
31559     
31560     register : function(layout)
31561     {
31562         this.groups[layout.id] = layout;
31563     },
31564     /**
31565     * fetch a  Masonry Layout based on the masonry layout ID
31566     * @param {string} the masonry layout to add
31567     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31568     */
31569     
31570     get: function(layout_id) {
31571         if (typeof(this.groups[layout_id]) == 'undefined') {
31572             return false;
31573         }
31574         return this.groups[layout_id] ;
31575     }
31576     
31577     
31578     
31579 });
31580
31581  
31582
31583  /**
31584  *
31585  * This is based on 
31586  * http://masonry.desandro.com
31587  *
31588  * The idea is to render all the bricks based on vertical width...
31589  *
31590  * The original code extends 'outlayer' - we might need to use that....
31591  * 
31592  */
31593
31594
31595 /**
31596  * @class Roo.bootstrap.LayoutMasonryAuto
31597  * @extends Roo.bootstrap.Component
31598  * Bootstrap Layout Masonry class
31599  * 
31600  * @constructor
31601  * Create a new Element
31602  * @param {Object} config The config object
31603  */
31604
31605 Roo.bootstrap.LayoutMasonryAuto = function(config){
31606     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31607 };
31608
31609 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31610     
31611       /**
31612      * @cfg {Boolean} isFitWidth  - resize the width..
31613      */   
31614     isFitWidth : false,  // options..
31615     /**
31616      * @cfg {Boolean} isOriginLeft = left align?
31617      */   
31618     isOriginLeft : true,
31619     /**
31620      * @cfg {Boolean} isOriginTop = top align?
31621      */   
31622     isOriginTop : false,
31623     /**
31624      * @cfg {Boolean} isLayoutInstant = no animation?
31625      */   
31626     isLayoutInstant : false, // needed?
31627     /**
31628      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31629      */   
31630     isResizingContainer : true,
31631     /**
31632      * @cfg {Number} columnWidth  width of the columns 
31633      */   
31634     
31635     columnWidth : 0,
31636     
31637     /**
31638      * @cfg {Number} maxCols maximum number of columns
31639      */   
31640     
31641     maxCols: 0,
31642     /**
31643      * @cfg {Number} padHeight padding below box..
31644      */   
31645     
31646     padHeight : 10, 
31647     
31648     /**
31649      * @cfg {Boolean} isAutoInitial defalut true
31650      */   
31651     
31652     isAutoInitial : true, 
31653     
31654     // private?
31655     gutter : 0,
31656     
31657     containerWidth: 0,
31658     initialColumnWidth : 0,
31659     currentSize : null,
31660     
31661     colYs : null, // array.
31662     maxY : 0,
31663     padWidth: 10,
31664     
31665     
31666     tag: 'div',
31667     cls: '',
31668     bricks: null, //CompositeElement
31669     cols : 0, // array?
31670     // element : null, // wrapped now this.el
31671     _isLayoutInited : null, 
31672     
31673     
31674     getAutoCreate : function(){
31675         
31676         var cfg = {
31677             tag: this.tag,
31678             cls: 'blog-masonary-wrapper ' + this.cls,
31679             cn : {
31680                 cls : 'mas-boxes masonary'
31681             }
31682         };
31683         
31684         return cfg;
31685     },
31686     
31687     getChildContainer: function( )
31688     {
31689         if (this.boxesEl) {
31690             return this.boxesEl;
31691         }
31692         
31693         this.boxesEl = this.el.select('.mas-boxes').first();
31694         
31695         return this.boxesEl;
31696     },
31697     
31698     
31699     initEvents : function()
31700     {
31701         var _this = this;
31702         
31703         if(this.isAutoInitial){
31704             Roo.log('hook children rendered');
31705             this.on('childrenrendered', function() {
31706                 Roo.log('children rendered');
31707                 _this.initial();
31708             } ,this);
31709         }
31710         
31711     },
31712     
31713     initial : function()
31714     {
31715         this.reloadItems();
31716
31717         this.currentSize = this.el.getBox(true);
31718
31719         /// was window resize... - let's see if this works..
31720         Roo.EventManager.onWindowResize(this.resize, this); 
31721
31722         if(!this.isAutoInitial){
31723             this.layout();
31724             return;
31725         }
31726         
31727         this.layout.defer(500,this);
31728     },
31729     
31730     reloadItems: function()
31731     {
31732         this.bricks = this.el.select('.masonry-brick', true);
31733         
31734         this.bricks.each(function(b) {
31735             //Roo.log(b.getSize());
31736             if (!b.attr('originalwidth')) {
31737                 b.attr('originalwidth',  b.getSize().width);
31738             }
31739             
31740         });
31741         
31742         Roo.log(this.bricks.elements.length);
31743     },
31744     
31745     resize : function()
31746     {
31747         Roo.log('resize');
31748         var cs = this.el.getBox(true);
31749         
31750         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31751             Roo.log("no change in with or X");
31752             return;
31753         }
31754         this.currentSize = cs;
31755         this.layout();
31756     },
31757     
31758     layout : function()
31759     {
31760          Roo.log('layout');
31761         this._resetLayout();
31762         //this._manageStamps();
31763       
31764         // don't animate first layout
31765         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31766         this.layoutItems( isInstant );
31767       
31768         // flag for initalized
31769         this._isLayoutInited = true;
31770     },
31771     
31772     layoutItems : function( isInstant )
31773     {
31774         //var items = this._getItemsForLayout( this.items );
31775         // original code supports filtering layout items.. we just ignore it..
31776         
31777         this._layoutItems( this.bricks , isInstant );
31778       
31779         this._postLayout();
31780     },
31781     _layoutItems : function ( items , isInstant)
31782     {
31783        //this.fireEvent( 'layout', this, items );
31784     
31785
31786         if ( !items || !items.elements.length ) {
31787           // no items, emit event with empty array
31788             return;
31789         }
31790
31791         var queue = [];
31792         items.each(function(item) {
31793             Roo.log("layout item");
31794             Roo.log(item);
31795             // get x/y object from method
31796             var position = this._getItemLayoutPosition( item );
31797             // enqueue
31798             position.item = item;
31799             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31800             queue.push( position );
31801         }, this);
31802       
31803         this._processLayoutQueue( queue );
31804     },
31805     /** Sets position of item in DOM
31806     * @param {Element} item
31807     * @param {Number} x - horizontal position
31808     * @param {Number} y - vertical position
31809     * @param {Boolean} isInstant - disables transitions
31810     */
31811     _processLayoutQueue : function( queue )
31812     {
31813         for ( var i=0, len = queue.length; i < len; i++ ) {
31814             var obj = queue[i];
31815             obj.item.position('absolute');
31816             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31817         }
31818     },
31819       
31820     
31821     /**
31822     * Any logic you want to do after each layout,
31823     * i.e. size the container
31824     */
31825     _postLayout : function()
31826     {
31827         this.resizeContainer();
31828     },
31829     
31830     resizeContainer : function()
31831     {
31832         if ( !this.isResizingContainer ) {
31833             return;
31834         }
31835         var size = this._getContainerSize();
31836         if ( size ) {
31837             this.el.setSize(size.width,size.height);
31838             this.boxesEl.setSize(size.width,size.height);
31839         }
31840     },
31841     
31842     
31843     
31844     _resetLayout : function()
31845     {
31846         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31847         this.colWidth = this.el.getWidth();
31848         //this.gutter = this.el.getWidth(); 
31849         
31850         this.measureColumns();
31851
31852         // reset column Y
31853         var i = this.cols;
31854         this.colYs = [];
31855         while (i--) {
31856             this.colYs.push( 0 );
31857         }
31858     
31859         this.maxY = 0;
31860     },
31861
31862     measureColumns : function()
31863     {
31864         this.getContainerWidth();
31865       // if columnWidth is 0, default to outerWidth of first item
31866         if ( !this.columnWidth ) {
31867             var firstItem = this.bricks.first();
31868             Roo.log(firstItem);
31869             this.columnWidth  = this.containerWidth;
31870             if (firstItem && firstItem.attr('originalwidth') ) {
31871                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31872             }
31873             // columnWidth fall back to item of first element
31874             Roo.log("set column width?");
31875                         this.initialColumnWidth = this.columnWidth  ;
31876
31877             // if first elem has no width, default to size of container
31878             
31879         }
31880         
31881         
31882         if (this.initialColumnWidth) {
31883             this.columnWidth = this.initialColumnWidth;
31884         }
31885         
31886         
31887             
31888         // column width is fixed at the top - however if container width get's smaller we should
31889         // reduce it...
31890         
31891         // this bit calcs how man columns..
31892             
31893         var columnWidth = this.columnWidth += this.gutter;
31894       
31895         // calculate columns
31896         var containerWidth = this.containerWidth + this.gutter;
31897         
31898         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31899         // fix rounding errors, typically with gutters
31900         var excess = columnWidth - containerWidth % columnWidth;
31901         
31902         
31903         // if overshoot is less than a pixel, round up, otherwise floor it
31904         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31905         cols = Math[ mathMethod ]( cols );
31906         this.cols = Math.max( cols, 1 );
31907         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31908         
31909          // padding positioning..
31910         var totalColWidth = this.cols * this.columnWidth;
31911         var padavail = this.containerWidth - totalColWidth;
31912         // so for 2 columns - we need 3 'pads'
31913         
31914         var padNeeded = (1+this.cols) * this.padWidth;
31915         
31916         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31917         
31918         this.columnWidth += padExtra
31919         //this.padWidth = Math.floor(padavail /  ( this.cols));
31920         
31921         // adjust colum width so that padding is fixed??
31922         
31923         // we have 3 columns ... total = width * 3
31924         // we have X left over... that should be used by 
31925         
31926         //if (this.expandC) {
31927             
31928         //}
31929         
31930         
31931         
31932     },
31933     
31934     getContainerWidth : function()
31935     {
31936        /* // container is parent if fit width
31937         var container = this.isFitWidth ? this.element.parentNode : this.element;
31938         // check that this.size and size are there
31939         // IE8 triggers resize on body size change, so they might not be
31940         
31941         var size = getSize( container );  //FIXME
31942         this.containerWidth = size && size.innerWidth; //FIXME
31943         */
31944          
31945         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31946         
31947     },
31948     
31949     _getItemLayoutPosition : function( item )  // what is item?
31950     {
31951         // we resize the item to our columnWidth..
31952       
31953         item.setWidth(this.columnWidth);
31954         item.autoBoxAdjust  = false;
31955         
31956         var sz = item.getSize();
31957  
31958         // how many columns does this brick span
31959         var remainder = this.containerWidth % this.columnWidth;
31960         
31961         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31962         // round if off by 1 pixel, otherwise use ceil
31963         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31964         colSpan = Math.min( colSpan, this.cols );
31965         
31966         // normally this should be '1' as we dont' currently allow multi width columns..
31967         
31968         var colGroup = this._getColGroup( colSpan );
31969         // get the minimum Y value from the columns
31970         var minimumY = Math.min.apply( Math, colGroup );
31971         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31972         
31973         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31974          
31975         // position the brick
31976         var position = {
31977             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31978             y: this.currentSize.y + minimumY + this.padHeight
31979         };
31980         
31981         Roo.log(position);
31982         // apply setHeight to necessary columns
31983         var setHeight = minimumY + sz.height + this.padHeight;
31984         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31985         
31986         var setSpan = this.cols + 1 - colGroup.length;
31987         for ( var i = 0; i < setSpan; i++ ) {
31988           this.colYs[ shortColIndex + i ] = setHeight ;
31989         }
31990       
31991         return position;
31992     },
31993     
31994     /**
31995      * @param {Number} colSpan - number of columns the element spans
31996      * @returns {Array} colGroup
31997      */
31998     _getColGroup : function( colSpan )
31999     {
32000         if ( colSpan < 2 ) {
32001           // if brick spans only one column, use all the column Ys
32002           return this.colYs;
32003         }
32004       
32005         var colGroup = [];
32006         // how many different places could this brick fit horizontally
32007         var groupCount = this.cols + 1 - colSpan;
32008         // for each group potential horizontal position
32009         for ( var i = 0; i < groupCount; i++ ) {
32010           // make an array of colY values for that one group
32011           var groupColYs = this.colYs.slice( i, i + colSpan );
32012           // and get the max value of the array
32013           colGroup[i] = Math.max.apply( Math, groupColYs );
32014         }
32015         return colGroup;
32016     },
32017     /*
32018     _manageStamp : function( stamp )
32019     {
32020         var stampSize =  stamp.getSize();
32021         var offset = stamp.getBox();
32022         // get the columns that this stamp affects
32023         var firstX = this.isOriginLeft ? offset.x : offset.right;
32024         var lastX = firstX + stampSize.width;
32025         var firstCol = Math.floor( firstX / this.columnWidth );
32026         firstCol = Math.max( 0, firstCol );
32027         
32028         var lastCol = Math.floor( lastX / this.columnWidth );
32029         // lastCol should not go over if multiple of columnWidth #425
32030         lastCol -= lastX % this.columnWidth ? 0 : 1;
32031         lastCol = Math.min( this.cols - 1, lastCol );
32032         
32033         // set colYs to bottom of the stamp
32034         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32035             stampSize.height;
32036             
32037         for ( var i = firstCol; i <= lastCol; i++ ) {
32038           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32039         }
32040     },
32041     */
32042     
32043     _getContainerSize : function()
32044     {
32045         this.maxY = Math.max.apply( Math, this.colYs );
32046         var size = {
32047             height: this.maxY
32048         };
32049       
32050         if ( this.isFitWidth ) {
32051             size.width = this._getContainerFitWidth();
32052         }
32053       
32054         return size;
32055     },
32056     
32057     _getContainerFitWidth : function()
32058     {
32059         var unusedCols = 0;
32060         // count unused columns
32061         var i = this.cols;
32062         while ( --i ) {
32063           if ( this.colYs[i] !== 0 ) {
32064             break;
32065           }
32066           unusedCols++;
32067         }
32068         // fit container to columns that have been used
32069         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32070     },
32071     
32072     needsResizeLayout : function()
32073     {
32074         var previousWidth = this.containerWidth;
32075         this.getContainerWidth();
32076         return previousWidth !== this.containerWidth;
32077     }
32078  
32079 });
32080
32081  
32082
32083  /*
32084  * - LGPL
32085  *
32086  * element
32087  * 
32088  */
32089
32090 /**
32091  * @class Roo.bootstrap.MasonryBrick
32092  * @extends Roo.bootstrap.Component
32093  * Bootstrap MasonryBrick class
32094  * 
32095  * @constructor
32096  * Create a new MasonryBrick
32097  * @param {Object} config The config object
32098  */
32099
32100 Roo.bootstrap.MasonryBrick = function(config){
32101     
32102     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32103     
32104     Roo.bootstrap.MasonryBrick.register(this);
32105     
32106     this.addEvents({
32107         // raw events
32108         /**
32109          * @event click
32110          * When a MasonryBrick is clcik
32111          * @param {Roo.bootstrap.MasonryBrick} this
32112          * @param {Roo.EventObject} e
32113          */
32114         "click" : true
32115     });
32116 };
32117
32118 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32119     
32120     /**
32121      * @cfg {String} title
32122      */   
32123     title : '',
32124     /**
32125      * @cfg {String} html
32126      */   
32127     html : '',
32128     /**
32129      * @cfg {String} bgimage
32130      */   
32131     bgimage : '',
32132     /**
32133      * @cfg {String} videourl
32134      */   
32135     videourl : '',
32136     /**
32137      * @cfg {String} cls
32138      */   
32139     cls : '',
32140     /**
32141      * @cfg {String} href
32142      */   
32143     href : '',
32144     /**
32145      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32146      */   
32147     size : 'xs',
32148     
32149     /**
32150      * @cfg {String} placetitle (center|bottom)
32151      */   
32152     placetitle : '',
32153     
32154     /**
32155      * @cfg {Boolean} isFitContainer defalut true
32156      */   
32157     isFitContainer : true, 
32158     
32159     /**
32160      * @cfg {Boolean} preventDefault defalut false
32161      */   
32162     preventDefault : false, 
32163     
32164     /**
32165      * @cfg {Boolean} inverse defalut false
32166      */   
32167     maskInverse : false, 
32168     
32169     getAutoCreate : function()
32170     {
32171         if(!this.isFitContainer){
32172             return this.getSplitAutoCreate();
32173         }
32174         
32175         var cls = 'masonry-brick masonry-brick-full';
32176         
32177         if(this.href.length){
32178             cls += ' masonry-brick-link';
32179         }
32180         
32181         if(this.bgimage.length){
32182             cls += ' masonry-brick-image';
32183         }
32184         
32185         if(this.maskInverse){
32186             cls += ' mask-inverse';
32187         }
32188         
32189         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32190             cls += ' enable-mask';
32191         }
32192         
32193         if(this.size){
32194             cls += ' masonry-' + this.size + '-brick';
32195         }
32196         
32197         if(this.placetitle.length){
32198             
32199             switch (this.placetitle) {
32200                 case 'center' :
32201                     cls += ' masonry-center-title';
32202                     break;
32203                 case 'bottom' :
32204                     cls += ' masonry-bottom-title';
32205                     break;
32206                 default:
32207                     break;
32208             }
32209             
32210         } else {
32211             if(!this.html.length && !this.bgimage.length){
32212                 cls += ' masonry-center-title';
32213             }
32214
32215             if(!this.html.length && this.bgimage.length){
32216                 cls += ' masonry-bottom-title';
32217             }
32218         }
32219         
32220         if(this.cls){
32221             cls += ' ' + this.cls;
32222         }
32223         
32224         var cfg = {
32225             tag: (this.href.length) ? 'a' : 'div',
32226             cls: cls,
32227             cn: [
32228                 {
32229                     tag: 'div',
32230                     cls: 'masonry-brick-mask'
32231                 },
32232                 {
32233                     tag: 'div',
32234                     cls: 'masonry-brick-paragraph',
32235                     cn: []
32236                 }
32237             ]
32238         };
32239         
32240         if(this.href.length){
32241             cfg.href = this.href;
32242         }
32243         
32244         var cn = cfg.cn[1].cn;
32245         
32246         if(this.title.length){
32247             cn.push({
32248                 tag: 'h4',
32249                 cls: 'masonry-brick-title',
32250                 html: this.title
32251             });
32252         }
32253         
32254         if(this.html.length){
32255             cn.push({
32256                 tag: 'p',
32257                 cls: 'masonry-brick-text',
32258                 html: this.html
32259             });
32260         }
32261         
32262         if (!this.title.length && !this.html.length) {
32263             cfg.cn[1].cls += ' hide';
32264         }
32265         
32266         if(this.bgimage.length){
32267             cfg.cn.push({
32268                 tag: 'img',
32269                 cls: 'masonry-brick-image-view',
32270                 src: this.bgimage
32271             });
32272         }
32273         
32274         if(this.videourl.length){
32275             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32276             // youtube support only?
32277             cfg.cn.push({
32278                 tag: 'iframe',
32279                 cls: 'masonry-brick-image-view',
32280                 src: vurl,
32281                 frameborder : 0,
32282                 allowfullscreen : true
32283             });
32284         }
32285         
32286         return cfg;
32287         
32288     },
32289     
32290     getSplitAutoCreate : function()
32291     {
32292         var cls = 'masonry-brick masonry-brick-split';
32293         
32294         if(this.href.length){
32295             cls += ' masonry-brick-link';
32296         }
32297         
32298         if(this.bgimage.length){
32299             cls += ' masonry-brick-image';
32300         }
32301         
32302         if(this.size){
32303             cls += ' masonry-' + this.size + '-brick';
32304         }
32305         
32306         switch (this.placetitle) {
32307             case 'center' :
32308                 cls += ' masonry-center-title';
32309                 break;
32310             case 'bottom' :
32311                 cls += ' masonry-bottom-title';
32312                 break;
32313             default:
32314                 if(!this.bgimage.length){
32315                     cls += ' masonry-center-title';
32316                 }
32317
32318                 if(this.bgimage.length){
32319                     cls += ' masonry-bottom-title';
32320                 }
32321                 break;
32322         }
32323         
32324         if(this.cls){
32325             cls += ' ' + this.cls;
32326         }
32327         
32328         var cfg = {
32329             tag: (this.href.length) ? 'a' : 'div',
32330             cls: cls,
32331             cn: [
32332                 {
32333                     tag: 'div',
32334                     cls: 'masonry-brick-split-head',
32335                     cn: [
32336                         {
32337                             tag: 'div',
32338                             cls: 'masonry-brick-paragraph',
32339                             cn: []
32340                         }
32341                     ]
32342                 },
32343                 {
32344                     tag: 'div',
32345                     cls: 'masonry-brick-split-body',
32346                     cn: []
32347                 }
32348             ]
32349         };
32350         
32351         if(this.href.length){
32352             cfg.href = this.href;
32353         }
32354         
32355         if(this.title.length){
32356             cfg.cn[0].cn[0].cn.push({
32357                 tag: 'h4',
32358                 cls: 'masonry-brick-title',
32359                 html: this.title
32360             });
32361         }
32362         
32363         if(this.html.length){
32364             cfg.cn[1].cn.push({
32365                 tag: 'p',
32366                 cls: 'masonry-brick-text',
32367                 html: this.html
32368             });
32369         }
32370
32371         if(this.bgimage.length){
32372             cfg.cn[0].cn.push({
32373                 tag: 'img',
32374                 cls: 'masonry-brick-image-view',
32375                 src: this.bgimage
32376             });
32377         }
32378         
32379         if(this.videourl.length){
32380             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32381             // youtube support only?
32382             cfg.cn[0].cn.cn.push({
32383                 tag: 'iframe',
32384                 cls: 'masonry-brick-image-view',
32385                 src: vurl,
32386                 frameborder : 0,
32387                 allowfullscreen : true
32388             });
32389         }
32390         
32391         return cfg;
32392     },
32393     
32394     initEvents: function() 
32395     {
32396         switch (this.size) {
32397             case 'xs' :
32398                 this.x = 1;
32399                 this.y = 1;
32400                 break;
32401             case 'sm' :
32402                 this.x = 2;
32403                 this.y = 2;
32404                 break;
32405             case 'md' :
32406             case 'md-left' :
32407             case 'md-right' :
32408                 this.x = 3;
32409                 this.y = 3;
32410                 break;
32411             case 'tall' :
32412                 this.x = 2;
32413                 this.y = 3;
32414                 break;
32415             case 'wide' :
32416                 this.x = 3;
32417                 this.y = 2;
32418                 break;
32419             case 'wide-thin' :
32420                 this.x = 3;
32421                 this.y = 1;
32422                 break;
32423                         
32424             default :
32425                 break;
32426         }
32427         
32428         if(Roo.isTouch){
32429             this.el.on('touchstart', this.onTouchStart, this);
32430             this.el.on('touchmove', this.onTouchMove, this);
32431             this.el.on('touchend', this.onTouchEnd, this);
32432             this.el.on('contextmenu', this.onContextMenu, this);
32433         } else {
32434             this.el.on('mouseenter'  ,this.enter, this);
32435             this.el.on('mouseleave', this.leave, this);
32436             this.el.on('click', this.onClick, this);
32437         }
32438         
32439         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32440             this.parent().bricks.push(this);   
32441         }
32442         
32443     },
32444     
32445     onClick: function(e, el)
32446     {
32447         var time = this.endTimer - this.startTimer;
32448         // Roo.log(e.preventDefault());
32449         if(Roo.isTouch){
32450             if(time > 1000){
32451                 e.preventDefault();
32452                 return;
32453             }
32454         }
32455         
32456         if(!this.preventDefault){
32457             return;
32458         }
32459         
32460         e.preventDefault();
32461         
32462         if (this.activcClass != '') {
32463             this.selectBrick();
32464         }
32465         
32466         this.fireEvent('click', this);
32467     },
32468     
32469     enter: function(e, el)
32470     {
32471         e.preventDefault();
32472         
32473         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32474             return;
32475         }
32476         
32477         if(this.bgimage.length && this.html.length){
32478             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32479         }
32480     },
32481     
32482     leave: function(e, el)
32483     {
32484         e.preventDefault();
32485         
32486         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32487             return;
32488         }
32489         
32490         if(this.bgimage.length && this.html.length){
32491             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32492         }
32493     },
32494     
32495     onTouchStart: function(e, el)
32496     {
32497 //        e.preventDefault();
32498         
32499         this.touchmoved = false;
32500         
32501         if(!this.isFitContainer){
32502             return;
32503         }
32504         
32505         if(!this.bgimage.length || !this.html.length){
32506             return;
32507         }
32508         
32509         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32510         
32511         this.timer = new Date().getTime();
32512         
32513     },
32514     
32515     onTouchMove: function(e, el)
32516     {
32517         this.touchmoved = true;
32518     },
32519     
32520     onContextMenu : function(e,el)
32521     {
32522         e.preventDefault();
32523         e.stopPropagation();
32524         return false;
32525     },
32526     
32527     onTouchEnd: function(e, el)
32528     {
32529 //        e.preventDefault();
32530         
32531         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32532         
32533             this.leave(e,el);
32534             
32535             return;
32536         }
32537         
32538         if(!this.bgimage.length || !this.html.length){
32539             
32540             if(this.href.length){
32541                 window.location.href = this.href;
32542             }
32543             
32544             return;
32545         }
32546         
32547         if(!this.isFitContainer){
32548             return;
32549         }
32550         
32551         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32552         
32553         window.location.href = this.href;
32554     },
32555     
32556     //selection on single brick only
32557     selectBrick : function() {
32558         
32559         if (!this.parentId) {
32560             return;
32561         }
32562         
32563         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32564         var index = m.selectedBrick.indexOf(this.id);
32565         
32566         if ( index > -1) {
32567             m.selectedBrick.splice(index,1);
32568             this.el.removeClass(this.activeClass);
32569             return;
32570         }
32571         
32572         for(var i = 0; i < m.selectedBrick.length; i++) {
32573             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32574             b.el.removeClass(b.activeClass);
32575         }
32576         
32577         m.selectedBrick = [];
32578         
32579         m.selectedBrick.push(this.id);
32580         this.el.addClass(this.activeClass);
32581         return;
32582     }
32583     
32584 });
32585
32586 Roo.apply(Roo.bootstrap.MasonryBrick, {
32587     
32588     //groups: {},
32589     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32590      /**
32591     * register a Masonry Brick
32592     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32593     */
32594     
32595     register : function(brick)
32596     {
32597         //this.groups[brick.id] = brick;
32598         this.groups.add(brick.id, brick);
32599     },
32600     /**
32601     * fetch a  masonry brick based on the masonry brick ID
32602     * @param {string} the masonry brick to add
32603     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32604     */
32605     
32606     get: function(brick_id) 
32607     {
32608         // if (typeof(this.groups[brick_id]) == 'undefined') {
32609         //     return false;
32610         // }
32611         // return this.groups[brick_id] ;
32612         
32613         if(this.groups.key(brick_id)) {
32614             return this.groups.key(brick_id);
32615         }
32616         
32617         return false;
32618     }
32619     
32620     
32621     
32622 });
32623
32624  /*
32625  * - LGPL
32626  *
32627  * element
32628  * 
32629  */
32630
32631 /**
32632  * @class Roo.bootstrap.Brick
32633  * @extends Roo.bootstrap.Component
32634  * Bootstrap Brick class
32635  * 
32636  * @constructor
32637  * Create a new Brick
32638  * @param {Object} config The config object
32639  */
32640
32641 Roo.bootstrap.Brick = function(config){
32642     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32643     
32644     this.addEvents({
32645         // raw events
32646         /**
32647          * @event click
32648          * When a Brick is click
32649          * @param {Roo.bootstrap.Brick} this
32650          * @param {Roo.EventObject} e
32651          */
32652         "click" : true
32653     });
32654 };
32655
32656 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32657     
32658     /**
32659      * @cfg {String} title
32660      */   
32661     title : '',
32662     /**
32663      * @cfg {String} html
32664      */   
32665     html : '',
32666     /**
32667      * @cfg {String} bgimage
32668      */   
32669     bgimage : '',
32670     /**
32671      * @cfg {String} cls
32672      */   
32673     cls : '',
32674     /**
32675      * @cfg {String} href
32676      */   
32677     href : '',
32678     /**
32679      * @cfg {String} video
32680      */   
32681     video : '',
32682     /**
32683      * @cfg {Boolean} square
32684      */   
32685     square : true,
32686     
32687     getAutoCreate : function()
32688     {
32689         var cls = 'roo-brick';
32690         
32691         if(this.href.length){
32692             cls += ' roo-brick-link';
32693         }
32694         
32695         if(this.bgimage.length){
32696             cls += ' roo-brick-image';
32697         }
32698         
32699         if(!this.html.length && !this.bgimage.length){
32700             cls += ' roo-brick-center-title';
32701         }
32702         
32703         if(!this.html.length && this.bgimage.length){
32704             cls += ' roo-brick-bottom-title';
32705         }
32706         
32707         if(this.cls){
32708             cls += ' ' + this.cls;
32709         }
32710         
32711         var cfg = {
32712             tag: (this.href.length) ? 'a' : 'div',
32713             cls: cls,
32714             cn: [
32715                 {
32716                     tag: 'div',
32717                     cls: 'roo-brick-paragraph',
32718                     cn: []
32719                 }
32720             ]
32721         };
32722         
32723         if(this.href.length){
32724             cfg.href = this.href;
32725         }
32726         
32727         var cn = cfg.cn[0].cn;
32728         
32729         if(this.title.length){
32730             cn.push({
32731                 tag: 'h4',
32732                 cls: 'roo-brick-title',
32733                 html: this.title
32734             });
32735         }
32736         
32737         if(this.html.length){
32738             cn.push({
32739                 tag: 'p',
32740                 cls: 'roo-brick-text',
32741                 html: this.html
32742             });
32743         } else {
32744             cn.cls += ' hide';
32745         }
32746         
32747         if(this.bgimage.length){
32748             cfg.cn.push({
32749                 tag: 'img',
32750                 cls: 'roo-brick-image-view',
32751                 src: this.bgimage
32752             });
32753         }
32754         
32755         return cfg;
32756     },
32757     
32758     initEvents: function() 
32759     {
32760         if(this.title.length || this.html.length){
32761             this.el.on('mouseenter'  ,this.enter, this);
32762             this.el.on('mouseleave', this.leave, this);
32763         }
32764         
32765         Roo.EventManager.onWindowResize(this.resize, this); 
32766         
32767         if(this.bgimage.length){
32768             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32769             this.imageEl.on('load', this.onImageLoad, this);
32770             return;
32771         }
32772         
32773         this.resize();
32774     },
32775     
32776     onImageLoad : function()
32777     {
32778         this.resize();
32779     },
32780     
32781     resize : function()
32782     {
32783         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32784         
32785         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32786         
32787         if(this.bgimage.length){
32788             var image = this.el.select('.roo-brick-image-view', true).first();
32789             
32790             image.setWidth(paragraph.getWidth());
32791             
32792             if(this.square){
32793                 image.setHeight(paragraph.getWidth());
32794             }
32795             
32796             this.el.setHeight(image.getHeight());
32797             paragraph.setHeight(image.getHeight());
32798             
32799         }
32800         
32801     },
32802     
32803     enter: function(e, el)
32804     {
32805         e.preventDefault();
32806         
32807         if(this.bgimage.length){
32808             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32809             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32810         }
32811     },
32812     
32813     leave: function(e, el)
32814     {
32815         e.preventDefault();
32816         
32817         if(this.bgimage.length){
32818             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32819             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32820         }
32821     }
32822     
32823 });
32824
32825  
32826
32827  /*
32828  * - LGPL
32829  *
32830  * Input
32831  * 
32832  */
32833
32834 /**
32835  * @class Roo.bootstrap.NumberField
32836  * @extends Roo.bootstrap.Input
32837  * Bootstrap NumberField class
32838  * 
32839  * 
32840  * 
32841  * 
32842  * @constructor
32843  * Create a new NumberField
32844  * @param {Object} config The config object
32845  */
32846
32847 Roo.bootstrap.NumberField = function(config){
32848     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32849 };
32850
32851 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32852     
32853     /**
32854      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32855      */
32856     allowDecimals : true,
32857     /**
32858      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32859      */
32860     decimalSeparator : ".",
32861     /**
32862      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32863      */
32864     decimalPrecision : 2,
32865     /**
32866      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32867      */
32868     allowNegative : true,
32869     /**
32870      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32871      */
32872     minValue : Number.NEGATIVE_INFINITY,
32873     /**
32874      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32875      */
32876     maxValue : Number.MAX_VALUE,
32877     /**
32878      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32879      */
32880     minText : "The minimum value for this field is {0}",
32881     /**
32882      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32883      */
32884     maxText : "The maximum value for this field is {0}",
32885     /**
32886      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32887      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32888      */
32889     nanText : "{0} is not a valid number",
32890     /**
32891      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32892      */
32893     castInt : true,
32894
32895     // private
32896     initEvents : function()
32897     {   
32898         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32899         
32900         var allowed = "0123456789";
32901         
32902         if(this.allowDecimals){
32903             allowed += this.decimalSeparator;
32904         }
32905         
32906         if(this.allowNegative){
32907             allowed += "-";
32908         }
32909         
32910         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32911         
32912         var keyPress = function(e){
32913             
32914             var k = e.getKey();
32915             
32916             var c = e.getCharCode();
32917             
32918             if(
32919                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32920                     allowed.indexOf(String.fromCharCode(c)) === -1
32921             ){
32922                 e.stopEvent();
32923                 return;
32924             }
32925             
32926             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32927                 return;
32928             }
32929             
32930             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32931                 e.stopEvent();
32932             }
32933         };
32934         
32935         this.el.on("keypress", keyPress, this);
32936     },
32937     
32938     validateValue : function(value)
32939     {
32940         
32941         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32942             return false;
32943         }
32944         
32945         var num = this.parseValue(value);
32946         
32947         if(isNaN(num)){
32948             this.markInvalid(String.format(this.nanText, value));
32949             return false;
32950         }
32951         
32952         if(num < this.minValue){
32953             this.markInvalid(String.format(this.minText, this.minValue));
32954             return false;
32955         }
32956         
32957         if(num > this.maxValue){
32958             this.markInvalid(String.format(this.maxText, this.maxValue));
32959             return false;
32960         }
32961         
32962         return true;
32963     },
32964
32965     getValue : function()
32966     {
32967         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32968     },
32969
32970     parseValue : function(value)
32971     {
32972         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32973         return isNaN(value) ? '' : value;
32974     },
32975
32976     fixPrecision : function(value)
32977     {
32978         var nan = isNaN(value);
32979         
32980         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32981             return nan ? '' : value;
32982         }
32983         return parseFloat(value).toFixed(this.decimalPrecision);
32984     },
32985
32986     setValue : function(v)
32987     {
32988         v = this.fixPrecision(v);
32989         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32990     },
32991
32992     decimalPrecisionFcn : function(v)
32993     {
32994         return Math.floor(v);
32995     },
32996
32997     beforeBlur : function()
32998     {
32999         if(!this.castInt){
33000             return;
33001         }
33002         
33003         var v = this.parseValue(this.getRawValue());
33004         if(v){
33005             this.setValue(v);
33006         }
33007     }
33008     
33009 });
33010
33011  
33012
33013 /*
33014 * Licence: LGPL
33015 */
33016
33017 /**
33018  * @class Roo.bootstrap.DocumentSlider
33019  * @extends Roo.bootstrap.Component
33020  * Bootstrap DocumentSlider class
33021  * 
33022  * @constructor
33023  * Create a new DocumentViewer
33024  * @param {Object} config The config object
33025  */
33026
33027 Roo.bootstrap.DocumentSlider = function(config){
33028     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33029     
33030     this.files = [];
33031     
33032     this.addEvents({
33033         /**
33034          * @event initial
33035          * Fire after initEvent
33036          * @param {Roo.bootstrap.DocumentSlider} this
33037          */
33038         "initial" : true,
33039         /**
33040          * @event update
33041          * Fire after update
33042          * @param {Roo.bootstrap.DocumentSlider} this
33043          */
33044         "update" : true,
33045         /**
33046          * @event click
33047          * Fire after click
33048          * @param {Roo.bootstrap.DocumentSlider} this
33049          */
33050         "click" : true
33051     });
33052 };
33053
33054 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33055     
33056     files : false,
33057     
33058     indicator : 0,
33059     
33060     getAutoCreate : function()
33061     {
33062         var cfg = {
33063             tag : 'div',
33064             cls : 'roo-document-slider',
33065             cn : [
33066                 {
33067                     tag : 'div',
33068                     cls : 'roo-document-slider-header',
33069                     cn : [
33070                         {
33071                             tag : 'div',
33072                             cls : 'roo-document-slider-header-title'
33073                         }
33074                     ]
33075                 },
33076                 {
33077                     tag : 'div',
33078                     cls : 'roo-document-slider-body',
33079                     cn : [
33080                         {
33081                             tag : 'div',
33082                             cls : 'roo-document-slider-prev',
33083                             cn : [
33084                                 {
33085                                     tag : 'i',
33086                                     cls : 'fa fa-chevron-left'
33087                                 }
33088                             ]
33089                         },
33090                         {
33091                             tag : 'div',
33092                             cls : 'roo-document-slider-thumb',
33093                             cn : [
33094                                 {
33095                                     tag : 'img',
33096                                     cls : 'roo-document-slider-image'
33097                                 }
33098                             ]
33099                         },
33100                         {
33101                             tag : 'div',
33102                             cls : 'roo-document-slider-next',
33103                             cn : [
33104                                 {
33105                                     tag : 'i',
33106                                     cls : 'fa fa-chevron-right'
33107                                 }
33108                             ]
33109                         }
33110                     ]
33111                 }
33112             ]
33113         };
33114         
33115         return cfg;
33116     },
33117     
33118     initEvents : function()
33119     {
33120         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33121         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33122         
33123         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33124         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33125         
33126         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33127         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33128         
33129         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33130         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33131         
33132         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33133         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33134         
33135         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33136         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33137         
33138         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33139         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33140         
33141         this.thumbEl.on('click', this.onClick, this);
33142         
33143         this.prevIndicator.on('click', this.prev, this);
33144         
33145         this.nextIndicator.on('click', this.next, this);
33146         
33147     },
33148     
33149     initial : function()
33150     {
33151         if(this.files.length){
33152             this.indicator = 1;
33153             this.update()
33154         }
33155         
33156         this.fireEvent('initial', this);
33157     },
33158     
33159     update : function()
33160     {
33161         this.imageEl.attr('src', this.files[this.indicator - 1]);
33162         
33163         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33164         
33165         this.prevIndicator.show();
33166         
33167         if(this.indicator == 1){
33168             this.prevIndicator.hide();
33169         }
33170         
33171         this.nextIndicator.show();
33172         
33173         if(this.indicator == this.files.length){
33174             this.nextIndicator.hide();
33175         }
33176         
33177         this.thumbEl.scrollTo('top');
33178         
33179         this.fireEvent('update', this);
33180     },
33181     
33182     onClick : function(e)
33183     {
33184         e.preventDefault();
33185         
33186         this.fireEvent('click', this);
33187     },
33188     
33189     prev : function(e)
33190     {
33191         e.preventDefault();
33192         
33193         this.indicator = Math.max(1, this.indicator - 1);
33194         
33195         this.update();
33196     },
33197     
33198     next : function(e)
33199     {
33200         e.preventDefault();
33201         
33202         this.indicator = Math.min(this.files.length, this.indicator + 1);
33203         
33204         this.update();
33205     }
33206 });
33207 /*
33208  * - LGPL
33209  *
33210  * RadioSet
33211  *
33212  *
33213  */
33214
33215 /**
33216  * @class Roo.bootstrap.RadioSet
33217  * @extends Roo.bootstrap.Input
33218  * Bootstrap RadioSet class
33219  * @cfg {String} indicatorpos (left|right) default left
33220  * @cfg {Boolean} inline (true|false) inline the element (default true)
33221  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33222  * @constructor
33223  * Create a new RadioSet
33224  * @param {Object} config The config object
33225  */
33226
33227 Roo.bootstrap.RadioSet = function(config){
33228     
33229     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33230     
33231     this.radioes = [];
33232     
33233     Roo.bootstrap.RadioSet.register(this);
33234     
33235     this.addEvents({
33236         /**
33237         * @event check
33238         * Fires when the element is checked or unchecked.
33239         * @param {Roo.bootstrap.RadioSet} this This radio
33240         * @param {Roo.bootstrap.Radio} item The checked item
33241         */
33242        check : true
33243     });
33244     
33245 };
33246
33247 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33248
33249     radioes : false,
33250     
33251     inline : true,
33252     
33253     weight : '',
33254     
33255     indicatorpos : 'left',
33256     
33257     getAutoCreate : function()
33258     {
33259         var label = {
33260             tag : 'label',
33261             cls : 'roo-radio-set-label',
33262             cn : [
33263                 {
33264                     tag : 'span',
33265                     html : this.fieldLabel
33266                 }
33267             ]
33268         };
33269         
33270         if(this.indicatorpos == 'left'){
33271             label.cn.unshift({
33272                 tag : 'i',
33273                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33274                 tooltip : 'This field is required'
33275             });
33276         } else {
33277             label.cn.push({
33278                 tag : 'i',
33279                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33280                 tooltip : 'This field is required'
33281             });
33282         }
33283         
33284         var items = {
33285             tag : 'div',
33286             cls : 'roo-radio-set-items'
33287         };
33288         
33289         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33290         
33291         if (align === 'left' && this.fieldLabel.length) {
33292             
33293             items = {
33294                 cls : "roo-radio-set-right", 
33295                 cn: [
33296                     items
33297                 ]
33298             };
33299             
33300             if(this.labelWidth > 12){
33301                 label.style = "width: " + this.labelWidth + 'px';
33302             }
33303             
33304             if(this.labelWidth < 13 && this.labelmd == 0){
33305                 this.labelmd = this.labelWidth;
33306             }
33307             
33308             if(this.labellg > 0){
33309                 label.cls += ' col-lg-' + this.labellg;
33310                 items.cls += ' col-lg-' + (12 - this.labellg);
33311             }
33312             
33313             if(this.labelmd > 0){
33314                 label.cls += ' col-md-' + this.labelmd;
33315                 items.cls += ' col-md-' + (12 - this.labelmd);
33316             }
33317             
33318             if(this.labelsm > 0){
33319                 label.cls += ' col-sm-' + this.labelsm;
33320                 items.cls += ' col-sm-' + (12 - this.labelsm);
33321             }
33322             
33323             if(this.labelxs > 0){
33324                 label.cls += ' col-xs-' + this.labelxs;
33325                 items.cls += ' col-xs-' + (12 - this.labelxs);
33326             }
33327         }
33328         
33329         var cfg = {
33330             tag : 'div',
33331             cls : 'roo-radio-set',
33332             cn : [
33333                 {
33334                     tag : 'input',
33335                     cls : 'roo-radio-set-input',
33336                     type : 'hidden',
33337                     name : this.name,
33338                     value : this.value ? this.value :  ''
33339                 },
33340                 label,
33341                 items
33342             ]
33343         };
33344         
33345         if(this.weight.length){
33346             cfg.cls += ' roo-radio-' + this.weight;
33347         }
33348         
33349         if(this.inline) {
33350             cfg.cls += ' roo-radio-set-inline';
33351         }
33352         
33353         var settings=this;
33354         ['xs','sm','md','lg'].map(function(size){
33355             if (settings[size]) {
33356                 cfg.cls += ' col-' + size + '-' + settings[size];
33357             }
33358         });
33359         
33360         return cfg;
33361         
33362     },
33363
33364     initEvents : function()
33365     {
33366         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33367         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33368         
33369         if(!this.fieldLabel.length){
33370             this.labelEl.hide();
33371         }
33372         
33373         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33374         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33375         
33376         this.indicatorEl().addClass('invisible');
33377         
33378         this.originalValue = this.getValue();
33379         
33380     },
33381     
33382     inputEl: function ()
33383     {
33384         return this.el.select('.roo-radio-set-input', true).first();
33385     },
33386     
33387     getChildContainer : function()
33388     {
33389         return this.itemsEl;
33390     },
33391     
33392     register : function(item)
33393     {
33394         this.radioes.push(item);
33395         
33396     },
33397     
33398     validate : function()
33399     {   
33400         var valid = false;
33401         
33402         Roo.each(this.radioes, function(i){
33403             if(!i.checked){
33404                 return;
33405             }
33406             
33407             valid = true;
33408             return false;
33409         });
33410         
33411         if(this.allowBlank) {
33412             return true;
33413         }
33414         
33415         if(this.disabled || valid){
33416             this.markValid();
33417             return true;
33418         }
33419         
33420         this.markInvalid();
33421         return false;
33422         
33423     },
33424     
33425     markValid : function()
33426     {
33427         if(this.labelEl.isVisible(true)){
33428             this.indicatorEl().removeClass('visible');
33429             this.indicatorEl().addClass('invisible');
33430         }
33431         
33432         this.el.removeClass([this.invalidClass, this.validClass]);
33433         this.el.addClass(this.validClass);
33434         
33435         this.fireEvent('valid', this);
33436     },
33437     
33438     markInvalid : function(msg)
33439     {
33440         if(this.allowBlank || this.disabled){
33441             return;
33442         }
33443         
33444         if(this.labelEl.isVisible(true)){
33445             this.indicatorEl().removeClass('invisible');
33446             this.indicatorEl().addClass('visible');
33447         }
33448         
33449         this.el.removeClass([this.invalidClass, this.validClass]);
33450         this.el.addClass(this.invalidClass);
33451         
33452         this.fireEvent('invalid', this, msg);
33453         
33454     },
33455     
33456     setValue : function(v, suppressEvent)
33457     {   
33458         this.value = v;
33459         if(this.rendered){
33460             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33461         }
33462         
33463         Roo.each(this.radioes, function(i){
33464             
33465             i.checked = false;
33466             i.el.removeClass('checked');
33467             
33468             if(i.value === v || i.value.toString() === v.toString()){
33469                 i.checked = true;
33470                 i.el.addClass('checked');
33471                 
33472                 if(suppressEvent !== true){
33473                     this.fireEvent('check', this, i);
33474                 }
33475             }
33476             
33477         }, this);
33478         
33479         this.validate();
33480     },
33481     
33482     clearInvalid : function(){
33483         
33484         if(!this.el || this.preventMark){
33485             return;
33486         }
33487         
33488         this.el.removeClass([this.invalidClass]);
33489         
33490         this.fireEvent('valid', this);
33491     }
33492     
33493 });
33494
33495 Roo.apply(Roo.bootstrap.RadioSet, {
33496     
33497     groups: {},
33498     
33499     register : function(set)
33500     {
33501         this.groups[set.name] = set;
33502     },
33503     
33504     get: function(name) 
33505     {
33506         if (typeof(this.groups[name]) == 'undefined') {
33507             return false;
33508         }
33509         
33510         return this.groups[name] ;
33511     }
33512     
33513 });
33514 /*
33515  * Based on:
33516  * Ext JS Library 1.1.1
33517  * Copyright(c) 2006-2007, Ext JS, LLC.
33518  *
33519  * Originally Released Under LGPL - original licence link has changed is not relivant.
33520  *
33521  * Fork - LGPL
33522  * <script type="text/javascript">
33523  */
33524
33525
33526 /**
33527  * @class Roo.bootstrap.SplitBar
33528  * @extends Roo.util.Observable
33529  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33530  * <br><br>
33531  * Usage:
33532  * <pre><code>
33533 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33534                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33535 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33536 split.minSize = 100;
33537 split.maxSize = 600;
33538 split.animate = true;
33539 split.on('moved', splitterMoved);
33540 </code></pre>
33541  * @constructor
33542  * Create a new SplitBar
33543  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33544  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33545  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33546  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33547                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33548                         position of the SplitBar).
33549  */
33550 Roo.bootstrap.SplitBar = function(cfg){
33551     
33552     /** @private */
33553     
33554     //{
33555     //  dragElement : elm
33556     //  resizingElement: el,
33557         // optional..
33558     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33559     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33560         // existingProxy ???
33561     //}
33562     
33563     this.el = Roo.get(cfg.dragElement, true);
33564     this.el.dom.unselectable = "on";
33565     /** @private */
33566     this.resizingEl = Roo.get(cfg.resizingElement, true);
33567
33568     /**
33569      * @private
33570      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33571      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33572      * @type Number
33573      */
33574     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33575     
33576     /**
33577      * The minimum size of the resizing element. (Defaults to 0)
33578      * @type Number
33579      */
33580     this.minSize = 0;
33581     
33582     /**
33583      * The maximum size of the resizing element. (Defaults to 2000)
33584      * @type Number
33585      */
33586     this.maxSize = 2000;
33587     
33588     /**
33589      * Whether to animate the transition to the new size
33590      * @type Boolean
33591      */
33592     this.animate = false;
33593     
33594     /**
33595      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33596      * @type Boolean
33597      */
33598     this.useShim = false;
33599     
33600     /** @private */
33601     this.shim = null;
33602     
33603     if(!cfg.existingProxy){
33604         /** @private */
33605         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33606     }else{
33607         this.proxy = Roo.get(cfg.existingProxy).dom;
33608     }
33609     /** @private */
33610     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33611     
33612     /** @private */
33613     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33614     
33615     /** @private */
33616     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33617     
33618     /** @private */
33619     this.dragSpecs = {};
33620     
33621     /**
33622      * @private The adapter to use to positon and resize elements
33623      */
33624     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33625     this.adapter.init(this);
33626     
33627     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33628         /** @private */
33629         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33630         this.el.addClass("roo-splitbar-h");
33631     }else{
33632         /** @private */
33633         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33634         this.el.addClass("roo-splitbar-v");
33635     }
33636     
33637     this.addEvents({
33638         /**
33639          * @event resize
33640          * Fires when the splitter is moved (alias for {@link #event-moved})
33641          * @param {Roo.bootstrap.SplitBar} this
33642          * @param {Number} newSize the new width or height
33643          */
33644         "resize" : true,
33645         /**
33646          * @event moved
33647          * Fires when the splitter is moved
33648          * @param {Roo.bootstrap.SplitBar} this
33649          * @param {Number} newSize the new width or height
33650          */
33651         "moved" : true,
33652         /**
33653          * @event beforeresize
33654          * Fires before the splitter is dragged
33655          * @param {Roo.bootstrap.SplitBar} this
33656          */
33657         "beforeresize" : true,
33658
33659         "beforeapply" : true
33660     });
33661
33662     Roo.util.Observable.call(this);
33663 };
33664
33665 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33666     onStartProxyDrag : function(x, y){
33667         this.fireEvent("beforeresize", this);
33668         if(!this.overlay){
33669             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33670             o.unselectable();
33671             o.enableDisplayMode("block");
33672             // all splitbars share the same overlay
33673             Roo.bootstrap.SplitBar.prototype.overlay = o;
33674         }
33675         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33676         this.overlay.show();
33677         Roo.get(this.proxy).setDisplayed("block");
33678         var size = this.adapter.getElementSize(this);
33679         this.activeMinSize = this.getMinimumSize();;
33680         this.activeMaxSize = this.getMaximumSize();;
33681         var c1 = size - this.activeMinSize;
33682         var c2 = Math.max(this.activeMaxSize - size, 0);
33683         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33684             this.dd.resetConstraints();
33685             this.dd.setXConstraint(
33686                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33687                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33688             );
33689             this.dd.setYConstraint(0, 0);
33690         }else{
33691             this.dd.resetConstraints();
33692             this.dd.setXConstraint(0, 0);
33693             this.dd.setYConstraint(
33694                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33695                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33696             );
33697          }
33698         this.dragSpecs.startSize = size;
33699         this.dragSpecs.startPoint = [x, y];
33700         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33701     },
33702     
33703     /** 
33704      * @private Called after the drag operation by the DDProxy
33705      */
33706     onEndProxyDrag : function(e){
33707         Roo.get(this.proxy).setDisplayed(false);
33708         var endPoint = Roo.lib.Event.getXY(e);
33709         if(this.overlay){
33710             this.overlay.hide();
33711         }
33712         var newSize;
33713         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33714             newSize = this.dragSpecs.startSize + 
33715                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33716                     endPoint[0] - this.dragSpecs.startPoint[0] :
33717                     this.dragSpecs.startPoint[0] - endPoint[0]
33718                 );
33719         }else{
33720             newSize = this.dragSpecs.startSize + 
33721                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33722                     endPoint[1] - this.dragSpecs.startPoint[1] :
33723                     this.dragSpecs.startPoint[1] - endPoint[1]
33724                 );
33725         }
33726         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33727         if(newSize != this.dragSpecs.startSize){
33728             if(this.fireEvent('beforeapply', this, newSize) !== false){
33729                 this.adapter.setElementSize(this, newSize);
33730                 this.fireEvent("moved", this, newSize);
33731                 this.fireEvent("resize", this, newSize);
33732             }
33733         }
33734     },
33735     
33736     /**
33737      * Get the adapter this SplitBar uses
33738      * @return The adapter object
33739      */
33740     getAdapter : function(){
33741         return this.adapter;
33742     },
33743     
33744     /**
33745      * Set the adapter this SplitBar uses
33746      * @param {Object} adapter A SplitBar adapter object
33747      */
33748     setAdapter : function(adapter){
33749         this.adapter = adapter;
33750         this.adapter.init(this);
33751     },
33752     
33753     /**
33754      * Gets the minimum size for the resizing element
33755      * @return {Number} The minimum size
33756      */
33757     getMinimumSize : function(){
33758         return this.minSize;
33759     },
33760     
33761     /**
33762      * Sets the minimum size for the resizing element
33763      * @param {Number} minSize The minimum size
33764      */
33765     setMinimumSize : function(minSize){
33766         this.minSize = minSize;
33767     },
33768     
33769     /**
33770      * Gets the maximum size for the resizing element
33771      * @return {Number} The maximum size
33772      */
33773     getMaximumSize : function(){
33774         return this.maxSize;
33775     },
33776     
33777     /**
33778      * Sets the maximum size for the resizing element
33779      * @param {Number} maxSize The maximum size
33780      */
33781     setMaximumSize : function(maxSize){
33782         this.maxSize = maxSize;
33783     },
33784     
33785     /**
33786      * Sets the initialize size for the resizing element
33787      * @param {Number} size The initial size
33788      */
33789     setCurrentSize : function(size){
33790         var oldAnimate = this.animate;
33791         this.animate = false;
33792         this.adapter.setElementSize(this, size);
33793         this.animate = oldAnimate;
33794     },
33795     
33796     /**
33797      * Destroy this splitbar. 
33798      * @param {Boolean} removeEl True to remove the element
33799      */
33800     destroy : function(removeEl){
33801         if(this.shim){
33802             this.shim.remove();
33803         }
33804         this.dd.unreg();
33805         this.proxy.parentNode.removeChild(this.proxy);
33806         if(removeEl){
33807             this.el.remove();
33808         }
33809     }
33810 });
33811
33812 /**
33813  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
33814  */
33815 Roo.bootstrap.SplitBar.createProxy = function(dir){
33816     var proxy = new Roo.Element(document.createElement("div"));
33817     proxy.unselectable();
33818     var cls = 'roo-splitbar-proxy';
33819     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33820     document.body.appendChild(proxy.dom);
33821     return proxy.dom;
33822 };
33823
33824 /** 
33825  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33826  * Default Adapter. It assumes the splitter and resizing element are not positioned
33827  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33828  */
33829 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33830 };
33831
33832 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33833     // do nothing for now
33834     init : function(s){
33835     
33836     },
33837     /**
33838      * Called before drag operations to get the current size of the resizing element. 
33839      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33840      */
33841      getElementSize : function(s){
33842         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33843             return s.resizingEl.getWidth();
33844         }else{
33845             return s.resizingEl.getHeight();
33846         }
33847     },
33848     
33849     /**
33850      * Called after drag operations to set the size of the resizing element.
33851      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33852      * @param {Number} newSize The new size to set
33853      * @param {Function} onComplete A function to be invoked when resizing is complete
33854      */
33855     setElementSize : function(s, newSize, onComplete){
33856         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33857             if(!s.animate){
33858                 s.resizingEl.setWidth(newSize);
33859                 if(onComplete){
33860                     onComplete(s, newSize);
33861                 }
33862             }else{
33863                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33864             }
33865         }else{
33866             
33867             if(!s.animate){
33868                 s.resizingEl.setHeight(newSize);
33869                 if(onComplete){
33870                     onComplete(s, newSize);
33871                 }
33872             }else{
33873                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33874             }
33875         }
33876     }
33877 };
33878
33879 /** 
33880  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33881  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33882  * Adapter that  moves the splitter element to align with the resized sizing element. 
33883  * Used with an absolute positioned SplitBar.
33884  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33885  * document.body, make sure you assign an id to the body element.
33886  */
33887 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33888     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33889     this.container = Roo.get(container);
33890 };
33891
33892 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33893     init : function(s){
33894         this.basic.init(s);
33895     },
33896     
33897     getElementSize : function(s){
33898         return this.basic.getElementSize(s);
33899     },
33900     
33901     setElementSize : function(s, newSize, onComplete){
33902         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33903     },
33904     
33905     moveSplitter : function(s){
33906         var yes = Roo.bootstrap.SplitBar;
33907         switch(s.placement){
33908             case yes.LEFT:
33909                 s.el.setX(s.resizingEl.getRight());
33910                 break;
33911             case yes.RIGHT:
33912                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33913                 break;
33914             case yes.TOP:
33915                 s.el.setY(s.resizingEl.getBottom());
33916                 break;
33917             case yes.BOTTOM:
33918                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33919                 break;
33920         }
33921     }
33922 };
33923
33924 /**
33925  * Orientation constant - Create a vertical SplitBar
33926  * @static
33927  * @type Number
33928  */
33929 Roo.bootstrap.SplitBar.VERTICAL = 1;
33930
33931 /**
33932  * Orientation constant - Create a horizontal SplitBar
33933  * @static
33934  * @type Number
33935  */
33936 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33937
33938 /**
33939  * Placement constant - The resizing element is to the left of the splitter element
33940  * @static
33941  * @type Number
33942  */
33943 Roo.bootstrap.SplitBar.LEFT = 1;
33944
33945 /**
33946  * Placement constant - The resizing element is to the right of the splitter element
33947  * @static
33948  * @type Number
33949  */
33950 Roo.bootstrap.SplitBar.RIGHT = 2;
33951
33952 /**
33953  * Placement constant - The resizing element is positioned above the splitter element
33954  * @static
33955  * @type Number
33956  */
33957 Roo.bootstrap.SplitBar.TOP = 3;
33958
33959 /**
33960  * Placement constant - The resizing element is positioned under splitter element
33961  * @static
33962  * @type Number
33963  */
33964 Roo.bootstrap.SplitBar.BOTTOM = 4;
33965 Roo.namespace("Roo.bootstrap.layout");/*
33966  * Based on:
33967  * Ext JS Library 1.1.1
33968  * Copyright(c) 2006-2007, Ext JS, LLC.
33969  *
33970  * Originally Released Under LGPL - original licence link has changed is not relivant.
33971  *
33972  * Fork - LGPL
33973  * <script type="text/javascript">
33974  */
33975
33976 /**
33977  * @class Roo.bootstrap.layout.Manager
33978  * @extends Roo.bootstrap.Component
33979  * Base class for layout managers.
33980  */
33981 Roo.bootstrap.layout.Manager = function(config)
33982 {
33983     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33984
33985
33986
33987
33988
33989     /** false to disable window resize monitoring @type Boolean */
33990     this.monitorWindowResize = true;
33991     this.regions = {};
33992     this.addEvents({
33993         /**
33994          * @event layout
33995          * Fires when a layout is performed.
33996          * @param {Roo.LayoutManager} this
33997          */
33998         "layout" : true,
33999         /**
34000          * @event regionresized
34001          * Fires when the user resizes a region.
34002          * @param {Roo.LayoutRegion} region The resized region
34003          * @param {Number} newSize The new size (width for east/west, height for north/south)
34004          */
34005         "regionresized" : true,
34006         /**
34007          * @event regioncollapsed
34008          * Fires when a region is collapsed.
34009          * @param {Roo.LayoutRegion} region The collapsed region
34010          */
34011         "regioncollapsed" : true,
34012         /**
34013          * @event regionexpanded
34014          * Fires when a region is expanded.
34015          * @param {Roo.LayoutRegion} region The expanded region
34016          */
34017         "regionexpanded" : true
34018     });
34019     this.updating = false;
34020
34021     if (config.el) {
34022         this.el = Roo.get(config.el);
34023         this.initEvents();
34024     }
34025
34026 };
34027
34028 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34029
34030
34031     regions : null,
34032
34033     monitorWindowResize : true,
34034
34035
34036     updating : false,
34037
34038
34039     onRender : function(ct, position)
34040     {
34041         if(!this.el){
34042             this.el = Roo.get(ct);
34043             this.initEvents();
34044         }
34045         //this.fireEvent('render',this);
34046     },
34047
34048
34049     initEvents: function()
34050     {
34051
34052
34053         // ie scrollbar fix
34054         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34055             document.body.scroll = "no";
34056         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34057             this.el.position('relative');
34058         }
34059         this.id = this.el.id;
34060         this.el.addClass("roo-layout-container");
34061         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34062         if(this.el.dom != document.body ) {
34063             this.el.on('resize', this.layout,this);
34064             this.el.on('show', this.layout,this);
34065         }
34066
34067     },
34068
34069     /**
34070      * Returns true if this layout is currently being updated
34071      * @return {Boolean}
34072      */
34073     isUpdating : function(){
34074         return this.updating;
34075     },
34076
34077     /**
34078      * Suspend the LayoutManager from doing auto-layouts while
34079      * making multiple add or remove calls
34080      */
34081     beginUpdate : function(){
34082         this.updating = true;
34083     },
34084
34085     /**
34086      * Restore auto-layouts and optionally disable the manager from performing a layout
34087      * @param {Boolean} noLayout true to disable a layout update
34088      */
34089     endUpdate : function(noLayout){
34090         this.updating = false;
34091         if(!noLayout){
34092             this.layout();
34093         }
34094     },
34095
34096     layout: function(){
34097         // abstract...
34098     },
34099
34100     onRegionResized : function(region, newSize){
34101         this.fireEvent("regionresized", region, newSize);
34102         this.layout();
34103     },
34104
34105     onRegionCollapsed : function(region){
34106         this.fireEvent("regioncollapsed", region);
34107     },
34108
34109     onRegionExpanded : function(region){
34110         this.fireEvent("regionexpanded", region);
34111     },
34112
34113     /**
34114      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34115      * performs box-model adjustments.
34116      * @return {Object} The size as an object {width: (the width), height: (the height)}
34117      */
34118     getViewSize : function()
34119     {
34120         var size;
34121         if(this.el.dom != document.body){
34122             size = this.el.getSize();
34123         }else{
34124             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34125         }
34126         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34127         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34128         return size;
34129     },
34130
34131     /**
34132      * Returns the Element this layout is bound to.
34133      * @return {Roo.Element}
34134      */
34135     getEl : function(){
34136         return this.el;
34137     },
34138
34139     /**
34140      * Returns the specified region.
34141      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34142      * @return {Roo.LayoutRegion}
34143      */
34144     getRegion : function(target){
34145         return this.regions[target.toLowerCase()];
34146     },
34147
34148     onWindowResize : function(){
34149         if(this.monitorWindowResize){
34150             this.layout();
34151         }
34152     }
34153 });
34154 /*
34155  * Based on:
34156  * Ext JS Library 1.1.1
34157  * Copyright(c) 2006-2007, Ext JS, LLC.
34158  *
34159  * Originally Released Under LGPL - original licence link has changed is not relivant.
34160  *
34161  * Fork - LGPL
34162  * <script type="text/javascript">
34163  */
34164 /**
34165  * @class Roo.bootstrap.layout.Border
34166  * @extends Roo.bootstrap.layout.Manager
34167  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34168  * please see: examples/bootstrap/nested.html<br><br>
34169  
34170 <b>The container the layout is rendered into can be either the body element or any other element.
34171 If it is not the body element, the container needs to either be an absolute positioned element,
34172 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34173 the container size if it is not the body element.</b>
34174
34175 * @constructor
34176 * Create a new Border
34177 * @param {Object} config Configuration options
34178  */
34179 Roo.bootstrap.layout.Border = function(config){
34180     config = config || {};
34181     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34182     
34183     
34184     
34185     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34186         if(config[region]){
34187             config[region].region = region;
34188             this.addRegion(config[region]);
34189         }
34190     },this);
34191     
34192 };
34193
34194 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34195
34196 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34197     /**
34198      * Creates and adds a new region if it doesn't already exist.
34199      * @param {String} target The target region key (north, south, east, west or center).
34200      * @param {Object} config The regions config object
34201      * @return {BorderLayoutRegion} The new region
34202      */
34203     addRegion : function(config)
34204     {
34205         if(!this.regions[config.region]){
34206             var r = this.factory(config);
34207             this.bindRegion(r);
34208         }
34209         return this.regions[config.region];
34210     },
34211
34212     // private (kinda)
34213     bindRegion : function(r){
34214         this.regions[r.config.region] = r;
34215         
34216         r.on("visibilitychange",    this.layout, this);
34217         r.on("paneladded",          this.layout, this);
34218         r.on("panelremoved",        this.layout, this);
34219         r.on("invalidated",         this.layout, this);
34220         r.on("resized",             this.onRegionResized, this);
34221         r.on("collapsed",           this.onRegionCollapsed, this);
34222         r.on("expanded",            this.onRegionExpanded, this);
34223     },
34224
34225     /**
34226      * Performs a layout update.
34227      */
34228     layout : function()
34229     {
34230         if(this.updating) {
34231             return;
34232         }
34233         
34234         // render all the rebions if they have not been done alreayd?
34235         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34236             if(this.regions[region] && !this.regions[region].bodyEl){
34237                 this.regions[region].onRender(this.el)
34238             }
34239         },this);
34240         
34241         var size = this.getViewSize();
34242         var w = size.width;
34243         var h = size.height;
34244         var centerW = w;
34245         var centerH = h;
34246         var centerY = 0;
34247         var centerX = 0;
34248         //var x = 0, y = 0;
34249
34250         var rs = this.regions;
34251         var north = rs["north"];
34252         var south = rs["south"]; 
34253         var west = rs["west"];
34254         var east = rs["east"];
34255         var center = rs["center"];
34256         //if(this.hideOnLayout){ // not supported anymore
34257             //c.el.setStyle("display", "none");
34258         //}
34259         if(north && north.isVisible()){
34260             var b = north.getBox();
34261             var m = north.getMargins();
34262             b.width = w - (m.left+m.right);
34263             b.x = m.left;
34264             b.y = m.top;
34265             centerY = b.height + b.y + m.bottom;
34266             centerH -= centerY;
34267             north.updateBox(this.safeBox(b));
34268         }
34269         if(south && south.isVisible()){
34270             var b = south.getBox();
34271             var m = south.getMargins();
34272             b.width = w - (m.left+m.right);
34273             b.x = m.left;
34274             var totalHeight = (b.height + m.top + m.bottom);
34275             b.y = h - totalHeight + m.top;
34276             centerH -= totalHeight;
34277             south.updateBox(this.safeBox(b));
34278         }
34279         if(west && west.isVisible()){
34280             var b = west.getBox();
34281             var m = west.getMargins();
34282             b.height = centerH - (m.top+m.bottom);
34283             b.x = m.left;
34284             b.y = centerY + m.top;
34285             var totalWidth = (b.width + m.left + m.right);
34286             centerX += totalWidth;
34287             centerW -= totalWidth;
34288             west.updateBox(this.safeBox(b));
34289         }
34290         if(east && east.isVisible()){
34291             var b = east.getBox();
34292             var m = east.getMargins();
34293             b.height = centerH - (m.top+m.bottom);
34294             var totalWidth = (b.width + m.left + m.right);
34295             b.x = w - totalWidth + m.left;
34296             b.y = centerY + m.top;
34297             centerW -= totalWidth;
34298             east.updateBox(this.safeBox(b));
34299         }
34300         if(center){
34301             var m = center.getMargins();
34302             var centerBox = {
34303                 x: centerX + m.left,
34304                 y: centerY + m.top,
34305                 width: centerW - (m.left+m.right),
34306                 height: centerH - (m.top+m.bottom)
34307             };
34308             //if(this.hideOnLayout){
34309                 //center.el.setStyle("display", "block");
34310             //}
34311             center.updateBox(this.safeBox(centerBox));
34312         }
34313         this.el.repaint();
34314         this.fireEvent("layout", this);
34315     },
34316
34317     // private
34318     safeBox : function(box){
34319         box.width = Math.max(0, box.width);
34320         box.height = Math.max(0, box.height);
34321         return box;
34322     },
34323
34324     /**
34325      * Adds a ContentPanel (or subclass) to this layout.
34326      * @param {String} target The target region key (north, south, east, west or center).
34327      * @param {Roo.ContentPanel} panel The panel to add
34328      * @return {Roo.ContentPanel} The added panel
34329      */
34330     add : function(target, panel){
34331          
34332         target = target.toLowerCase();
34333         return this.regions[target].add(panel);
34334     },
34335
34336     /**
34337      * Remove a ContentPanel (or subclass) to this layout.
34338      * @param {String} target The target region key (north, south, east, west or center).
34339      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34340      * @return {Roo.ContentPanel} The removed panel
34341      */
34342     remove : function(target, panel){
34343         target = target.toLowerCase();
34344         return this.regions[target].remove(panel);
34345     },
34346
34347     /**
34348      * Searches all regions for a panel with the specified id
34349      * @param {String} panelId
34350      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34351      */
34352     findPanel : function(panelId){
34353         var rs = this.regions;
34354         for(var target in rs){
34355             if(typeof rs[target] != "function"){
34356                 var p = rs[target].getPanel(panelId);
34357                 if(p){
34358                     return p;
34359                 }
34360             }
34361         }
34362         return null;
34363     },
34364
34365     /**
34366      * Searches all regions for a panel with the specified id and activates (shows) it.
34367      * @param {String/ContentPanel} panelId The panels id or the panel itself
34368      * @return {Roo.ContentPanel} The shown panel or null
34369      */
34370     showPanel : function(panelId) {
34371       var rs = this.regions;
34372       for(var target in rs){
34373          var r = rs[target];
34374          if(typeof r != "function"){
34375             if(r.hasPanel(panelId)){
34376                return r.showPanel(panelId);
34377             }
34378          }
34379       }
34380       return null;
34381    },
34382
34383    /**
34384      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34385      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34386      */
34387    /*
34388     restoreState : function(provider){
34389         if(!provider){
34390             provider = Roo.state.Manager;
34391         }
34392         var sm = new Roo.LayoutStateManager();
34393         sm.init(this, provider);
34394     },
34395 */
34396  
34397  
34398     /**
34399      * Adds a xtype elements to the layout.
34400      * <pre><code>
34401
34402 layout.addxtype({
34403        xtype : 'ContentPanel',
34404        region: 'west',
34405        items: [ .... ]
34406    }
34407 );
34408
34409 layout.addxtype({
34410         xtype : 'NestedLayoutPanel',
34411         region: 'west',
34412         layout: {
34413            center: { },
34414            west: { }   
34415         },
34416         items : [ ... list of content panels or nested layout panels.. ]
34417    }
34418 );
34419 </code></pre>
34420      * @param {Object} cfg Xtype definition of item to add.
34421      */
34422     addxtype : function(cfg)
34423     {
34424         // basically accepts a pannel...
34425         // can accept a layout region..!?!?
34426         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34427         
34428         
34429         // theory?  children can only be panels??
34430         
34431         //if (!cfg.xtype.match(/Panel$/)) {
34432         //    return false;
34433         //}
34434         var ret = false;
34435         
34436         if (typeof(cfg.region) == 'undefined') {
34437             Roo.log("Failed to add Panel, region was not set");
34438             Roo.log(cfg);
34439             return false;
34440         }
34441         var region = cfg.region;
34442         delete cfg.region;
34443         
34444           
34445         var xitems = [];
34446         if (cfg.items) {
34447             xitems = cfg.items;
34448             delete cfg.items;
34449         }
34450         var nb = false;
34451         
34452         switch(cfg.xtype) 
34453         {
34454             case 'Content':  // ContentPanel (el, cfg)
34455             case 'Scroll':  // ContentPanel (el, cfg)
34456             case 'View': 
34457                 cfg.autoCreate = true;
34458                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34459                 //} else {
34460                 //    var el = this.el.createChild();
34461                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34462                 //}
34463                 
34464                 this.add(region, ret);
34465                 break;
34466             
34467             /*
34468             case 'TreePanel': // our new panel!
34469                 cfg.el = this.el.createChild();
34470                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34471                 this.add(region, ret);
34472                 break;
34473             */
34474             
34475             case 'Nest': 
34476                 // create a new Layout (which is  a Border Layout...
34477                 
34478                 var clayout = cfg.layout;
34479                 clayout.el  = this.el.createChild();
34480                 clayout.items   = clayout.items  || [];
34481                 
34482                 delete cfg.layout;
34483                 
34484                 // replace this exitems with the clayout ones..
34485                 xitems = clayout.items;
34486                  
34487                 // force background off if it's in center...
34488                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34489                     cfg.background = false;
34490                 }
34491                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34492                 
34493                 
34494                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34495                 //console.log('adding nested layout panel '  + cfg.toSource());
34496                 this.add(region, ret);
34497                 nb = {}; /// find first...
34498                 break;
34499             
34500             case 'Grid':
34501                 
34502                 // needs grid and region
34503                 
34504                 //var el = this.getRegion(region).el.createChild();
34505                 /*
34506                  *var el = this.el.createChild();
34507                 // create the grid first...
34508                 cfg.grid.container = el;
34509                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34510                 */
34511                 
34512                 if (region == 'center' && this.active ) {
34513                     cfg.background = false;
34514                 }
34515                 
34516                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34517                 
34518                 this.add(region, ret);
34519                 /*
34520                 if (cfg.background) {
34521                     // render grid on panel activation (if panel background)
34522                     ret.on('activate', function(gp) {
34523                         if (!gp.grid.rendered) {
34524                     //        gp.grid.render(el);
34525                         }
34526                     });
34527                 } else {
34528                   //  cfg.grid.render(el);
34529                 }
34530                 */
34531                 break;
34532            
34533            
34534             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34535                 // it was the old xcomponent building that caused this before.
34536                 // espeically if border is the top element in the tree.
34537                 ret = this;
34538                 break; 
34539                 
34540                     
34541                 
34542                 
34543                 
34544             default:
34545                 /*
34546                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34547                     
34548                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34549                     this.add(region, ret);
34550                 } else {
34551                 */
34552                     Roo.log(cfg);
34553                     throw "Can not add '" + cfg.xtype + "' to Border";
34554                     return null;
34555              
34556                                 
34557              
34558         }
34559         this.beginUpdate();
34560         // add children..
34561         var region = '';
34562         var abn = {};
34563         Roo.each(xitems, function(i)  {
34564             region = nb && i.region ? i.region : false;
34565             
34566             var add = ret.addxtype(i);
34567            
34568             if (region) {
34569                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34570                 if (!i.background) {
34571                     abn[region] = nb[region] ;
34572                 }
34573             }
34574             
34575         });
34576         this.endUpdate();
34577
34578         // make the last non-background panel active..
34579         //if (nb) { Roo.log(abn); }
34580         if (nb) {
34581             
34582             for(var r in abn) {
34583                 region = this.getRegion(r);
34584                 if (region) {
34585                     // tried using nb[r], but it does not work..
34586                      
34587                     region.showPanel(abn[r]);
34588                    
34589                 }
34590             }
34591         }
34592         return ret;
34593         
34594     },
34595     
34596     
34597 // private
34598     factory : function(cfg)
34599     {
34600         
34601         var validRegions = Roo.bootstrap.layout.Border.regions;
34602
34603         var target = cfg.region;
34604         cfg.mgr = this;
34605         
34606         var r = Roo.bootstrap.layout;
34607         Roo.log(target);
34608         switch(target){
34609             case "north":
34610                 return new r.North(cfg);
34611             case "south":
34612                 return new r.South(cfg);
34613             case "east":
34614                 return new r.East(cfg);
34615             case "west":
34616                 return new r.West(cfg);
34617             case "center":
34618                 return new r.Center(cfg);
34619         }
34620         throw 'Layout region "'+target+'" not supported.';
34621     }
34622     
34623     
34624 });
34625  /*
34626  * Based on:
34627  * Ext JS Library 1.1.1
34628  * Copyright(c) 2006-2007, Ext JS, LLC.
34629  *
34630  * Originally Released Under LGPL - original licence link has changed is not relivant.
34631  *
34632  * Fork - LGPL
34633  * <script type="text/javascript">
34634  */
34635  
34636 /**
34637  * @class Roo.bootstrap.layout.Basic
34638  * @extends Roo.util.Observable
34639  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34640  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34641  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34642  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34643  * @cfg {string}   region  the region that it inhabits..
34644  * @cfg {bool}   skipConfig skip config?
34645  * 
34646
34647  */
34648 Roo.bootstrap.layout.Basic = function(config){
34649     
34650     this.mgr = config.mgr;
34651     
34652     this.position = config.region;
34653     
34654     var skipConfig = config.skipConfig;
34655     
34656     this.events = {
34657         /**
34658          * @scope Roo.BasicLayoutRegion
34659          */
34660         
34661         /**
34662          * @event beforeremove
34663          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34664          * @param {Roo.LayoutRegion} this
34665          * @param {Roo.ContentPanel} panel The panel
34666          * @param {Object} e The cancel event object
34667          */
34668         "beforeremove" : true,
34669         /**
34670          * @event invalidated
34671          * Fires when the layout for this region is changed.
34672          * @param {Roo.LayoutRegion} this
34673          */
34674         "invalidated" : true,
34675         /**
34676          * @event visibilitychange
34677          * Fires when this region is shown or hidden 
34678          * @param {Roo.LayoutRegion} this
34679          * @param {Boolean} visibility true or false
34680          */
34681         "visibilitychange" : true,
34682         /**
34683          * @event paneladded
34684          * Fires when a panel is added. 
34685          * @param {Roo.LayoutRegion} this
34686          * @param {Roo.ContentPanel} panel The panel
34687          */
34688         "paneladded" : true,
34689         /**
34690          * @event panelremoved
34691          * Fires when a panel is removed. 
34692          * @param {Roo.LayoutRegion} this
34693          * @param {Roo.ContentPanel} panel The panel
34694          */
34695         "panelremoved" : true,
34696         /**
34697          * @event beforecollapse
34698          * Fires when this region before collapse.
34699          * @param {Roo.LayoutRegion} this
34700          */
34701         "beforecollapse" : true,
34702         /**
34703          * @event collapsed
34704          * Fires when this region is collapsed.
34705          * @param {Roo.LayoutRegion} this
34706          */
34707         "collapsed" : true,
34708         /**
34709          * @event expanded
34710          * Fires when this region is expanded.
34711          * @param {Roo.LayoutRegion} this
34712          */
34713         "expanded" : true,
34714         /**
34715          * @event slideshow
34716          * Fires when this region is slid into view.
34717          * @param {Roo.LayoutRegion} this
34718          */
34719         "slideshow" : true,
34720         /**
34721          * @event slidehide
34722          * Fires when this region slides out of view. 
34723          * @param {Roo.LayoutRegion} this
34724          */
34725         "slidehide" : true,
34726         /**
34727          * @event panelactivated
34728          * Fires when a panel is activated. 
34729          * @param {Roo.LayoutRegion} this
34730          * @param {Roo.ContentPanel} panel The activated panel
34731          */
34732         "panelactivated" : true,
34733         /**
34734          * @event resized
34735          * Fires when the user resizes this region. 
34736          * @param {Roo.LayoutRegion} this
34737          * @param {Number} newSize The new size (width for east/west, height for north/south)
34738          */
34739         "resized" : true
34740     };
34741     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34742     this.panels = new Roo.util.MixedCollection();
34743     this.panels.getKey = this.getPanelId.createDelegate(this);
34744     this.box = null;
34745     this.activePanel = null;
34746     // ensure listeners are added...
34747     
34748     if (config.listeners || config.events) {
34749         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34750             listeners : config.listeners || {},
34751             events : config.events || {}
34752         });
34753     }
34754     
34755     if(skipConfig !== true){
34756         this.applyConfig(config);
34757     }
34758 };
34759
34760 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34761 {
34762     getPanelId : function(p){
34763         return p.getId();
34764     },
34765     
34766     applyConfig : function(config){
34767         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34768         this.config = config;
34769         
34770     },
34771     
34772     /**
34773      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34774      * the width, for horizontal (north, south) the height.
34775      * @param {Number} newSize The new width or height
34776      */
34777     resizeTo : function(newSize){
34778         var el = this.el ? this.el :
34779                  (this.activePanel ? this.activePanel.getEl() : null);
34780         if(el){
34781             switch(this.position){
34782                 case "east":
34783                 case "west":
34784                     el.setWidth(newSize);
34785                     this.fireEvent("resized", this, newSize);
34786                 break;
34787                 case "north":
34788                 case "south":
34789                     el.setHeight(newSize);
34790                     this.fireEvent("resized", this, newSize);
34791                 break;                
34792             }
34793         }
34794     },
34795     
34796     getBox : function(){
34797         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34798     },
34799     
34800     getMargins : function(){
34801         return this.margins;
34802     },
34803     
34804     updateBox : function(box){
34805         this.box = box;
34806         var el = this.activePanel.getEl();
34807         el.dom.style.left = box.x + "px";
34808         el.dom.style.top = box.y + "px";
34809         this.activePanel.setSize(box.width, box.height);
34810     },
34811     
34812     /**
34813      * Returns the container element for this region.
34814      * @return {Roo.Element}
34815      */
34816     getEl : function(){
34817         return this.activePanel;
34818     },
34819     
34820     /**
34821      * Returns true if this region is currently visible.
34822      * @return {Boolean}
34823      */
34824     isVisible : function(){
34825         return this.activePanel ? true : false;
34826     },
34827     
34828     setActivePanel : function(panel){
34829         panel = this.getPanel(panel);
34830         if(this.activePanel && this.activePanel != panel){
34831             this.activePanel.setActiveState(false);
34832             this.activePanel.getEl().setLeftTop(-10000,-10000);
34833         }
34834         this.activePanel = panel;
34835         panel.setActiveState(true);
34836         if(this.box){
34837             panel.setSize(this.box.width, this.box.height);
34838         }
34839         this.fireEvent("panelactivated", this, panel);
34840         this.fireEvent("invalidated");
34841     },
34842     
34843     /**
34844      * Show the specified panel.
34845      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34846      * @return {Roo.ContentPanel} The shown panel or null
34847      */
34848     showPanel : function(panel){
34849         panel = this.getPanel(panel);
34850         if(panel){
34851             this.setActivePanel(panel);
34852         }
34853         return panel;
34854     },
34855     
34856     /**
34857      * Get the active panel for this region.
34858      * @return {Roo.ContentPanel} The active panel or null
34859      */
34860     getActivePanel : function(){
34861         return this.activePanel;
34862     },
34863     
34864     /**
34865      * Add the passed ContentPanel(s)
34866      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34867      * @return {Roo.ContentPanel} The panel added (if only one was added)
34868      */
34869     add : function(panel){
34870         if(arguments.length > 1){
34871             for(var i = 0, len = arguments.length; i < len; i++) {
34872                 this.add(arguments[i]);
34873             }
34874             return null;
34875         }
34876         if(this.hasPanel(panel)){
34877             this.showPanel(panel);
34878             return panel;
34879         }
34880         var el = panel.getEl();
34881         if(el.dom.parentNode != this.mgr.el.dom){
34882             this.mgr.el.dom.appendChild(el.dom);
34883         }
34884         if(panel.setRegion){
34885             panel.setRegion(this);
34886         }
34887         this.panels.add(panel);
34888         el.setStyle("position", "absolute");
34889         if(!panel.background){
34890             this.setActivePanel(panel);
34891             if(this.config.initialSize && this.panels.getCount()==1){
34892                 this.resizeTo(this.config.initialSize);
34893             }
34894         }
34895         this.fireEvent("paneladded", this, panel);
34896         return panel;
34897     },
34898     
34899     /**
34900      * Returns true if the panel is in this region.
34901      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34902      * @return {Boolean}
34903      */
34904     hasPanel : function(panel){
34905         if(typeof panel == "object"){ // must be panel obj
34906             panel = panel.getId();
34907         }
34908         return this.getPanel(panel) ? true : false;
34909     },
34910     
34911     /**
34912      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34913      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34914      * @param {Boolean} preservePanel Overrides the config preservePanel option
34915      * @return {Roo.ContentPanel} The panel that was removed
34916      */
34917     remove : function(panel, preservePanel){
34918         panel = this.getPanel(panel);
34919         if(!panel){
34920             return null;
34921         }
34922         var e = {};
34923         this.fireEvent("beforeremove", this, panel, e);
34924         if(e.cancel === true){
34925             return null;
34926         }
34927         var panelId = panel.getId();
34928         this.panels.removeKey(panelId);
34929         return panel;
34930     },
34931     
34932     /**
34933      * Returns the panel specified or null if it's not in this region.
34934      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34935      * @return {Roo.ContentPanel}
34936      */
34937     getPanel : function(id){
34938         if(typeof id == "object"){ // must be panel obj
34939             return id;
34940         }
34941         return this.panels.get(id);
34942     },
34943     
34944     /**
34945      * Returns this regions position (north/south/east/west/center).
34946      * @return {String} 
34947      */
34948     getPosition: function(){
34949         return this.position;    
34950     }
34951 });/*
34952  * Based on:
34953  * Ext JS Library 1.1.1
34954  * Copyright(c) 2006-2007, Ext JS, LLC.
34955  *
34956  * Originally Released Under LGPL - original licence link has changed is not relivant.
34957  *
34958  * Fork - LGPL
34959  * <script type="text/javascript">
34960  */
34961  
34962 /**
34963  * @class Roo.bootstrap.layout.Region
34964  * @extends Roo.bootstrap.layout.Basic
34965  * This class represents a region in a layout manager.
34966  
34967  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34968  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
34969  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34970  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34971  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34972  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34973  * @cfg {String}    title           The title for the region (overrides panel titles)
34974  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34975  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34976  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34977  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34978  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34979  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34980  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34981  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34982  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34983  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34984
34985  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34986  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34987  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34988  * @cfg {Number}    width           For East/West panels
34989  * @cfg {Number}    height          For North/South panels
34990  * @cfg {Boolean}   split           To show the splitter
34991  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34992  * 
34993  * @cfg {string}   cls             Extra CSS classes to add to region
34994  * 
34995  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34996  * @cfg {string}   region  the region that it inhabits..
34997  *
34998
34999  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35000  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35001
35002  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35003  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35004  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35005  */
35006 Roo.bootstrap.layout.Region = function(config)
35007 {
35008     this.applyConfig(config);
35009
35010     var mgr = config.mgr;
35011     var pos = config.region;
35012     config.skipConfig = true;
35013     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35014     
35015     if (mgr.el) {
35016         this.onRender(mgr.el);   
35017     }
35018      
35019     this.visible = true;
35020     this.collapsed = false;
35021     this.unrendered_panels = [];
35022 };
35023
35024 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35025
35026     position: '', // set by wrapper (eg. north/south etc..)
35027     unrendered_panels : null,  // unrendered panels.
35028     createBody : function(){
35029         /** This region's body element 
35030         * @type Roo.Element */
35031         this.bodyEl = this.el.createChild({
35032                 tag: "div",
35033                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35034         });
35035     },
35036
35037     onRender: function(ctr, pos)
35038     {
35039         var dh = Roo.DomHelper;
35040         /** This region's container element 
35041         * @type Roo.Element */
35042         this.el = dh.append(ctr.dom, {
35043                 tag: "div",
35044                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35045             }, true);
35046         /** This region's title element 
35047         * @type Roo.Element */
35048     
35049         this.titleEl = dh.append(this.el.dom,
35050             {
35051                     tag: "div",
35052                     unselectable: "on",
35053                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35054                     children:[
35055                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35056                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35057                     ]}, true);
35058         
35059         this.titleEl.enableDisplayMode();
35060         /** This region's title text element 
35061         * @type HTMLElement */
35062         this.titleTextEl = this.titleEl.dom.firstChild;
35063         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35064         /*
35065         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35066         this.closeBtn.enableDisplayMode();
35067         this.closeBtn.on("click", this.closeClicked, this);
35068         this.closeBtn.hide();
35069     */
35070         this.createBody(this.config);
35071         if(this.config.hideWhenEmpty){
35072             this.hide();
35073             this.on("paneladded", this.validateVisibility, this);
35074             this.on("panelremoved", this.validateVisibility, this);
35075         }
35076         if(this.autoScroll){
35077             this.bodyEl.setStyle("overflow", "auto");
35078         }else{
35079             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35080         }
35081         //if(c.titlebar !== false){
35082             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35083                 this.titleEl.hide();
35084             }else{
35085                 this.titleEl.show();
35086                 if(this.config.title){
35087                     this.titleTextEl.innerHTML = this.config.title;
35088                 }
35089             }
35090         //}
35091         if(this.config.collapsed){
35092             this.collapse(true);
35093         }
35094         if(this.config.hidden){
35095             this.hide();
35096         }
35097         
35098         if (this.unrendered_panels && this.unrendered_panels.length) {
35099             for (var i =0;i< this.unrendered_panels.length; i++) {
35100                 this.add(this.unrendered_panels[i]);
35101             }
35102             this.unrendered_panels = null;
35103             
35104         }
35105         
35106     },
35107     
35108     applyConfig : function(c)
35109     {
35110         /*
35111          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35112             var dh = Roo.DomHelper;
35113             if(c.titlebar !== false){
35114                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35115                 this.collapseBtn.on("click", this.collapse, this);
35116                 this.collapseBtn.enableDisplayMode();
35117                 /*
35118                 if(c.showPin === true || this.showPin){
35119                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35120                     this.stickBtn.enableDisplayMode();
35121                     this.stickBtn.on("click", this.expand, this);
35122                     this.stickBtn.hide();
35123                 }
35124                 
35125             }
35126             */
35127             /** This region's collapsed element
35128             * @type Roo.Element */
35129             /*
35130              *
35131             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35132                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35133             ]}, true);
35134             
35135             if(c.floatable !== false){
35136                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35137                this.collapsedEl.on("click", this.collapseClick, this);
35138             }
35139
35140             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35141                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35142                    id: "message", unselectable: "on", style:{"float":"left"}});
35143                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35144              }
35145             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35146             this.expandBtn.on("click", this.expand, this);
35147             
35148         }
35149         
35150         if(this.collapseBtn){
35151             this.collapseBtn.setVisible(c.collapsible == true);
35152         }
35153         
35154         this.cmargins = c.cmargins || this.cmargins ||
35155                          (this.position == "west" || this.position == "east" ?
35156                              {top: 0, left: 2, right:2, bottom: 0} :
35157                              {top: 2, left: 0, right:0, bottom: 2});
35158         */
35159         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35160         
35161         
35162         this.bottomTabs = c.tabPosition != "top";
35163         
35164         this.autoScroll = c.autoScroll || false;
35165         
35166         
35167        
35168         
35169         this.duration = c.duration || .30;
35170         this.slideDuration = c.slideDuration || .45;
35171         this.config = c;
35172        
35173     },
35174     /**
35175      * Returns true if this region is currently visible.
35176      * @return {Boolean}
35177      */
35178     isVisible : function(){
35179         return this.visible;
35180     },
35181
35182     /**
35183      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35184      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35185      */
35186     //setCollapsedTitle : function(title){
35187     //    title = title || "&#160;";
35188      //   if(this.collapsedTitleTextEl){
35189       //      this.collapsedTitleTextEl.innerHTML = title;
35190        // }
35191     //},
35192
35193     getBox : function(){
35194         var b;
35195       //  if(!this.collapsed){
35196             b = this.el.getBox(false, true);
35197        // }else{
35198           //  b = this.collapsedEl.getBox(false, true);
35199         //}
35200         return b;
35201     },
35202
35203     getMargins : function(){
35204         return this.margins;
35205         //return this.collapsed ? this.cmargins : this.margins;
35206     },
35207 /*
35208     highlight : function(){
35209         this.el.addClass("x-layout-panel-dragover");
35210     },
35211
35212     unhighlight : function(){
35213         this.el.removeClass("x-layout-panel-dragover");
35214     },
35215 */
35216     updateBox : function(box)
35217     {
35218         if (!this.bodyEl) {
35219             return; // not rendered yet..
35220         }
35221         
35222         this.box = box;
35223         if(!this.collapsed){
35224             this.el.dom.style.left = box.x + "px";
35225             this.el.dom.style.top = box.y + "px";
35226             this.updateBody(box.width, box.height);
35227         }else{
35228             this.collapsedEl.dom.style.left = box.x + "px";
35229             this.collapsedEl.dom.style.top = box.y + "px";
35230             this.collapsedEl.setSize(box.width, box.height);
35231         }
35232         if(this.tabs){
35233             this.tabs.autoSizeTabs();
35234         }
35235     },
35236
35237     updateBody : function(w, h)
35238     {
35239         if(w !== null){
35240             this.el.setWidth(w);
35241             w -= this.el.getBorderWidth("rl");
35242             if(this.config.adjustments){
35243                 w += this.config.adjustments[0];
35244             }
35245         }
35246         if(h !== null && h > 0){
35247             this.el.setHeight(h);
35248             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35249             h -= this.el.getBorderWidth("tb");
35250             if(this.config.adjustments){
35251                 h += this.config.adjustments[1];
35252             }
35253             this.bodyEl.setHeight(h);
35254             if(this.tabs){
35255                 h = this.tabs.syncHeight(h);
35256             }
35257         }
35258         if(this.panelSize){
35259             w = w !== null ? w : this.panelSize.width;
35260             h = h !== null ? h : this.panelSize.height;
35261         }
35262         if(this.activePanel){
35263             var el = this.activePanel.getEl();
35264             w = w !== null ? w : el.getWidth();
35265             h = h !== null ? h : el.getHeight();
35266             this.panelSize = {width: w, height: h};
35267             this.activePanel.setSize(w, h);
35268         }
35269         if(Roo.isIE && this.tabs){
35270             this.tabs.el.repaint();
35271         }
35272     },
35273
35274     /**
35275      * Returns the container element for this region.
35276      * @return {Roo.Element}
35277      */
35278     getEl : function(){
35279         return this.el;
35280     },
35281
35282     /**
35283      * Hides this region.
35284      */
35285     hide : function(){
35286         //if(!this.collapsed){
35287             this.el.dom.style.left = "-2000px";
35288             this.el.hide();
35289         //}else{
35290          //   this.collapsedEl.dom.style.left = "-2000px";
35291          //   this.collapsedEl.hide();
35292        // }
35293         this.visible = false;
35294         this.fireEvent("visibilitychange", this, false);
35295     },
35296
35297     /**
35298      * Shows this region if it was previously hidden.
35299      */
35300     show : function(){
35301         //if(!this.collapsed){
35302             this.el.show();
35303         //}else{
35304         //    this.collapsedEl.show();
35305        // }
35306         this.visible = true;
35307         this.fireEvent("visibilitychange", this, true);
35308     },
35309 /*
35310     closeClicked : function(){
35311         if(this.activePanel){
35312             this.remove(this.activePanel);
35313         }
35314     },
35315
35316     collapseClick : function(e){
35317         if(this.isSlid){
35318            e.stopPropagation();
35319            this.slideIn();
35320         }else{
35321            e.stopPropagation();
35322            this.slideOut();
35323         }
35324     },
35325 */
35326     /**
35327      * Collapses this region.
35328      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35329      */
35330     /*
35331     collapse : function(skipAnim, skipCheck = false){
35332         if(this.collapsed) {
35333             return;
35334         }
35335         
35336         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35337             
35338             this.collapsed = true;
35339             if(this.split){
35340                 this.split.el.hide();
35341             }
35342             if(this.config.animate && skipAnim !== true){
35343                 this.fireEvent("invalidated", this);
35344                 this.animateCollapse();
35345             }else{
35346                 this.el.setLocation(-20000,-20000);
35347                 this.el.hide();
35348                 this.collapsedEl.show();
35349                 this.fireEvent("collapsed", this);
35350                 this.fireEvent("invalidated", this);
35351             }
35352         }
35353         
35354     },
35355 */
35356     animateCollapse : function(){
35357         // overridden
35358     },
35359
35360     /**
35361      * Expands this region if it was previously collapsed.
35362      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35363      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35364      */
35365     /*
35366     expand : function(e, skipAnim){
35367         if(e) {
35368             e.stopPropagation();
35369         }
35370         if(!this.collapsed || this.el.hasActiveFx()) {
35371             return;
35372         }
35373         if(this.isSlid){
35374             this.afterSlideIn();
35375             skipAnim = true;
35376         }
35377         this.collapsed = false;
35378         if(this.config.animate && skipAnim !== true){
35379             this.animateExpand();
35380         }else{
35381             this.el.show();
35382             if(this.split){
35383                 this.split.el.show();
35384             }
35385             this.collapsedEl.setLocation(-2000,-2000);
35386             this.collapsedEl.hide();
35387             this.fireEvent("invalidated", this);
35388             this.fireEvent("expanded", this);
35389         }
35390     },
35391 */
35392     animateExpand : function(){
35393         // overridden
35394     },
35395
35396     initTabs : function()
35397     {
35398         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35399         
35400         var ts = new Roo.bootstrap.panel.Tabs({
35401                 el: this.bodyEl.dom,
35402                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35403                 disableTooltips: this.config.disableTabTips,
35404                 toolbar : this.config.toolbar
35405             });
35406         
35407         if(this.config.hideTabs){
35408             ts.stripWrap.setDisplayed(false);
35409         }
35410         this.tabs = ts;
35411         ts.resizeTabs = this.config.resizeTabs === true;
35412         ts.minTabWidth = this.config.minTabWidth || 40;
35413         ts.maxTabWidth = this.config.maxTabWidth || 250;
35414         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35415         ts.monitorResize = false;
35416         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35417         ts.bodyEl.addClass('roo-layout-tabs-body');
35418         this.panels.each(this.initPanelAsTab, this);
35419     },
35420
35421     initPanelAsTab : function(panel){
35422         var ti = this.tabs.addTab(
35423             panel.getEl().id,
35424             panel.getTitle(),
35425             null,
35426             this.config.closeOnTab && panel.isClosable(),
35427             panel.tpl
35428         );
35429         if(panel.tabTip !== undefined){
35430             ti.setTooltip(panel.tabTip);
35431         }
35432         ti.on("activate", function(){
35433               this.setActivePanel(panel);
35434         }, this);
35435         
35436         if(this.config.closeOnTab){
35437             ti.on("beforeclose", function(t, e){
35438                 e.cancel = true;
35439                 this.remove(panel);
35440             }, this);
35441         }
35442         
35443         panel.tabItem = ti;
35444         
35445         return ti;
35446     },
35447
35448     updatePanelTitle : function(panel, title)
35449     {
35450         if(this.activePanel == panel){
35451             this.updateTitle(title);
35452         }
35453         if(this.tabs){
35454             var ti = this.tabs.getTab(panel.getEl().id);
35455             ti.setText(title);
35456             if(panel.tabTip !== undefined){
35457                 ti.setTooltip(panel.tabTip);
35458             }
35459         }
35460     },
35461
35462     updateTitle : function(title){
35463         if(this.titleTextEl && !this.config.title){
35464             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35465         }
35466     },
35467
35468     setActivePanel : function(panel)
35469     {
35470         panel = this.getPanel(panel);
35471         if(this.activePanel && this.activePanel != panel){
35472             if(this.activePanel.setActiveState(false) === false){
35473                 return;
35474             }
35475         }
35476         this.activePanel = panel;
35477         panel.setActiveState(true);
35478         if(this.panelSize){
35479             panel.setSize(this.panelSize.width, this.panelSize.height);
35480         }
35481         if(this.closeBtn){
35482             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35483         }
35484         this.updateTitle(panel.getTitle());
35485         if(this.tabs){
35486             this.fireEvent("invalidated", this);
35487         }
35488         this.fireEvent("panelactivated", this, panel);
35489     },
35490
35491     /**
35492      * Shows the specified panel.
35493      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35494      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35495      */
35496     showPanel : function(panel)
35497     {
35498         panel = this.getPanel(panel);
35499         if(panel){
35500             if(this.tabs){
35501                 var tab = this.tabs.getTab(panel.getEl().id);
35502                 if(tab.isHidden()){
35503                     this.tabs.unhideTab(tab.id);
35504                 }
35505                 tab.activate();
35506             }else{
35507                 this.setActivePanel(panel);
35508             }
35509         }
35510         return panel;
35511     },
35512
35513     /**
35514      * Get the active panel for this region.
35515      * @return {Roo.ContentPanel} The active panel or null
35516      */
35517     getActivePanel : function(){
35518         return this.activePanel;
35519     },
35520
35521     validateVisibility : function(){
35522         if(this.panels.getCount() < 1){
35523             this.updateTitle("&#160;");
35524             this.closeBtn.hide();
35525             this.hide();
35526         }else{
35527             if(!this.isVisible()){
35528                 this.show();
35529             }
35530         }
35531     },
35532
35533     /**
35534      * Adds the passed ContentPanel(s) to this region.
35535      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35536      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35537      */
35538     add : function(panel)
35539     {
35540         if(arguments.length > 1){
35541             for(var i = 0, len = arguments.length; i < len; i++) {
35542                 this.add(arguments[i]);
35543             }
35544             return null;
35545         }
35546         
35547         // if we have not been rendered yet, then we can not really do much of this..
35548         if (!this.bodyEl) {
35549             this.unrendered_panels.push(panel);
35550             return panel;
35551         }
35552         
35553         
35554         
35555         
35556         if(this.hasPanel(panel)){
35557             this.showPanel(panel);
35558             return panel;
35559         }
35560         panel.setRegion(this);
35561         this.panels.add(panel);
35562        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35563             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35564             // and hide them... ???
35565             this.bodyEl.dom.appendChild(panel.getEl().dom);
35566             if(panel.background !== true){
35567                 this.setActivePanel(panel);
35568             }
35569             this.fireEvent("paneladded", this, panel);
35570             return panel;
35571         }
35572         */
35573         if(!this.tabs){
35574             this.initTabs();
35575         }else{
35576             this.initPanelAsTab(panel);
35577         }
35578         
35579         
35580         if(panel.background !== true){
35581             this.tabs.activate(panel.getEl().id);
35582         }
35583         this.fireEvent("paneladded", this, panel);
35584         return panel;
35585     },
35586
35587     /**
35588      * Hides the tab for the specified panel.
35589      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35590      */
35591     hidePanel : function(panel){
35592         if(this.tabs && (panel = this.getPanel(panel))){
35593             this.tabs.hideTab(panel.getEl().id);
35594         }
35595     },
35596
35597     /**
35598      * Unhides the tab for a previously hidden panel.
35599      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35600      */
35601     unhidePanel : function(panel){
35602         if(this.tabs && (panel = this.getPanel(panel))){
35603             this.tabs.unhideTab(panel.getEl().id);
35604         }
35605     },
35606
35607     clearPanels : function(){
35608         while(this.panels.getCount() > 0){
35609              this.remove(this.panels.first());
35610         }
35611     },
35612
35613     /**
35614      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35615      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35616      * @param {Boolean} preservePanel Overrides the config preservePanel option
35617      * @return {Roo.ContentPanel} The panel that was removed
35618      */
35619     remove : function(panel, preservePanel)
35620     {
35621         panel = this.getPanel(panel);
35622         if(!panel){
35623             return null;
35624         }
35625         var e = {};
35626         this.fireEvent("beforeremove", this, panel, e);
35627         if(e.cancel === true){
35628             return null;
35629         }
35630         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35631         var panelId = panel.getId();
35632         this.panels.removeKey(panelId);
35633         if(preservePanel){
35634             document.body.appendChild(panel.getEl().dom);
35635         }
35636         if(this.tabs){
35637             this.tabs.removeTab(panel.getEl().id);
35638         }else if (!preservePanel){
35639             this.bodyEl.dom.removeChild(panel.getEl().dom);
35640         }
35641         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35642             var p = this.panels.first();
35643             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35644             tempEl.appendChild(p.getEl().dom);
35645             this.bodyEl.update("");
35646             this.bodyEl.dom.appendChild(p.getEl().dom);
35647             tempEl = null;
35648             this.updateTitle(p.getTitle());
35649             this.tabs = null;
35650             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35651             this.setActivePanel(p);
35652         }
35653         panel.setRegion(null);
35654         if(this.activePanel == panel){
35655             this.activePanel = null;
35656         }
35657         if(this.config.autoDestroy !== false && preservePanel !== true){
35658             try{panel.destroy();}catch(e){}
35659         }
35660         this.fireEvent("panelremoved", this, panel);
35661         return panel;
35662     },
35663
35664     /**
35665      * Returns the TabPanel component used by this region
35666      * @return {Roo.TabPanel}
35667      */
35668     getTabs : function(){
35669         return this.tabs;
35670     },
35671
35672     createTool : function(parentEl, className){
35673         var btn = Roo.DomHelper.append(parentEl, {
35674             tag: "div",
35675             cls: "x-layout-tools-button",
35676             children: [ {
35677                 tag: "div",
35678                 cls: "roo-layout-tools-button-inner " + className,
35679                 html: "&#160;"
35680             }]
35681         }, true);
35682         btn.addClassOnOver("roo-layout-tools-button-over");
35683         return btn;
35684     }
35685 });/*
35686  * Based on:
35687  * Ext JS Library 1.1.1
35688  * Copyright(c) 2006-2007, Ext JS, LLC.
35689  *
35690  * Originally Released Under LGPL - original licence link has changed is not relivant.
35691  *
35692  * Fork - LGPL
35693  * <script type="text/javascript">
35694  */
35695  
35696
35697
35698 /**
35699  * @class Roo.SplitLayoutRegion
35700  * @extends Roo.LayoutRegion
35701  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35702  */
35703 Roo.bootstrap.layout.Split = function(config){
35704     this.cursor = config.cursor;
35705     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35706 };
35707
35708 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35709 {
35710     splitTip : "Drag to resize.",
35711     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35712     useSplitTips : false,
35713
35714     applyConfig : function(config){
35715         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35716     },
35717     
35718     onRender : function(ctr,pos) {
35719         
35720         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35721         if(!this.config.split){
35722             return;
35723         }
35724         if(!this.split){
35725             
35726             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35727                             tag: "div",
35728                             id: this.el.id + "-split",
35729                             cls: "roo-layout-split roo-layout-split-"+this.position,
35730                             html: "&#160;"
35731             });
35732             /** The SplitBar for this region 
35733             * @type Roo.SplitBar */
35734             // does not exist yet...
35735             Roo.log([this.position, this.orientation]);
35736             
35737             this.split = new Roo.bootstrap.SplitBar({
35738                 dragElement : splitEl,
35739                 resizingElement: this.el,
35740                 orientation : this.orientation
35741             });
35742             
35743             this.split.on("moved", this.onSplitMove, this);
35744             this.split.useShim = this.config.useShim === true;
35745             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35746             if(this.useSplitTips){
35747                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35748             }
35749             //if(config.collapsible){
35750             //    this.split.el.on("dblclick", this.collapse,  this);
35751             //}
35752         }
35753         if(typeof this.config.minSize != "undefined"){
35754             this.split.minSize = this.config.minSize;
35755         }
35756         if(typeof this.config.maxSize != "undefined"){
35757             this.split.maxSize = this.config.maxSize;
35758         }
35759         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35760             this.hideSplitter();
35761         }
35762         
35763     },
35764
35765     getHMaxSize : function(){
35766          var cmax = this.config.maxSize || 10000;
35767          var center = this.mgr.getRegion("center");
35768          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35769     },
35770
35771     getVMaxSize : function(){
35772          var cmax = this.config.maxSize || 10000;
35773          var center = this.mgr.getRegion("center");
35774          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35775     },
35776
35777     onSplitMove : function(split, newSize){
35778         this.fireEvent("resized", this, newSize);
35779     },
35780     
35781     /** 
35782      * Returns the {@link Roo.SplitBar} for this region.
35783      * @return {Roo.SplitBar}
35784      */
35785     getSplitBar : function(){
35786         return this.split;
35787     },
35788     
35789     hide : function(){
35790         this.hideSplitter();
35791         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35792     },
35793
35794     hideSplitter : function(){
35795         if(this.split){
35796             this.split.el.setLocation(-2000,-2000);
35797             this.split.el.hide();
35798         }
35799     },
35800
35801     show : function(){
35802         if(this.split){
35803             this.split.el.show();
35804         }
35805         Roo.bootstrap.layout.Split.superclass.show.call(this);
35806     },
35807     
35808     beforeSlide: function(){
35809         if(Roo.isGecko){// firefox overflow auto bug workaround
35810             this.bodyEl.clip();
35811             if(this.tabs) {
35812                 this.tabs.bodyEl.clip();
35813             }
35814             if(this.activePanel){
35815                 this.activePanel.getEl().clip();
35816                 
35817                 if(this.activePanel.beforeSlide){
35818                     this.activePanel.beforeSlide();
35819                 }
35820             }
35821         }
35822     },
35823     
35824     afterSlide : function(){
35825         if(Roo.isGecko){// firefox overflow auto bug workaround
35826             this.bodyEl.unclip();
35827             if(this.tabs) {
35828                 this.tabs.bodyEl.unclip();
35829             }
35830             if(this.activePanel){
35831                 this.activePanel.getEl().unclip();
35832                 if(this.activePanel.afterSlide){
35833                     this.activePanel.afterSlide();
35834                 }
35835             }
35836         }
35837     },
35838
35839     initAutoHide : function(){
35840         if(this.autoHide !== false){
35841             if(!this.autoHideHd){
35842                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35843                 this.autoHideHd = {
35844                     "mouseout": function(e){
35845                         if(!e.within(this.el, true)){
35846                             st.delay(500);
35847                         }
35848                     },
35849                     "mouseover" : function(e){
35850                         st.cancel();
35851                     },
35852                     scope : this
35853                 };
35854             }
35855             this.el.on(this.autoHideHd);
35856         }
35857     },
35858
35859     clearAutoHide : function(){
35860         if(this.autoHide !== false){
35861             this.el.un("mouseout", this.autoHideHd.mouseout);
35862             this.el.un("mouseover", this.autoHideHd.mouseover);
35863         }
35864     },
35865
35866     clearMonitor : function(){
35867         Roo.get(document).un("click", this.slideInIf, this);
35868     },
35869
35870     // these names are backwards but not changed for compat
35871     slideOut : function(){
35872         if(this.isSlid || this.el.hasActiveFx()){
35873             return;
35874         }
35875         this.isSlid = true;
35876         if(this.collapseBtn){
35877             this.collapseBtn.hide();
35878         }
35879         this.closeBtnState = this.closeBtn.getStyle('display');
35880         this.closeBtn.hide();
35881         if(this.stickBtn){
35882             this.stickBtn.show();
35883         }
35884         this.el.show();
35885         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35886         this.beforeSlide();
35887         this.el.setStyle("z-index", 10001);
35888         this.el.slideIn(this.getSlideAnchor(), {
35889             callback: function(){
35890                 this.afterSlide();
35891                 this.initAutoHide();
35892                 Roo.get(document).on("click", this.slideInIf, this);
35893                 this.fireEvent("slideshow", this);
35894             },
35895             scope: this,
35896             block: true
35897         });
35898     },
35899
35900     afterSlideIn : function(){
35901         this.clearAutoHide();
35902         this.isSlid = false;
35903         this.clearMonitor();
35904         this.el.setStyle("z-index", "");
35905         if(this.collapseBtn){
35906             this.collapseBtn.show();
35907         }
35908         this.closeBtn.setStyle('display', this.closeBtnState);
35909         if(this.stickBtn){
35910             this.stickBtn.hide();
35911         }
35912         this.fireEvent("slidehide", this);
35913     },
35914
35915     slideIn : function(cb){
35916         if(!this.isSlid || this.el.hasActiveFx()){
35917             Roo.callback(cb);
35918             return;
35919         }
35920         this.isSlid = false;
35921         this.beforeSlide();
35922         this.el.slideOut(this.getSlideAnchor(), {
35923             callback: function(){
35924                 this.el.setLeftTop(-10000, -10000);
35925                 this.afterSlide();
35926                 this.afterSlideIn();
35927                 Roo.callback(cb);
35928             },
35929             scope: this,
35930             block: true
35931         });
35932     },
35933     
35934     slideInIf : function(e){
35935         if(!e.within(this.el)){
35936             this.slideIn();
35937         }
35938     },
35939
35940     animateCollapse : function(){
35941         this.beforeSlide();
35942         this.el.setStyle("z-index", 20000);
35943         var anchor = this.getSlideAnchor();
35944         this.el.slideOut(anchor, {
35945             callback : function(){
35946                 this.el.setStyle("z-index", "");
35947                 this.collapsedEl.slideIn(anchor, {duration:.3});
35948                 this.afterSlide();
35949                 this.el.setLocation(-10000,-10000);
35950                 this.el.hide();
35951                 this.fireEvent("collapsed", this);
35952             },
35953             scope: this,
35954             block: true
35955         });
35956     },
35957
35958     animateExpand : function(){
35959         this.beforeSlide();
35960         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35961         this.el.setStyle("z-index", 20000);
35962         this.collapsedEl.hide({
35963             duration:.1
35964         });
35965         this.el.slideIn(this.getSlideAnchor(), {
35966             callback : function(){
35967                 this.el.setStyle("z-index", "");
35968                 this.afterSlide();
35969                 if(this.split){
35970                     this.split.el.show();
35971                 }
35972                 this.fireEvent("invalidated", this);
35973                 this.fireEvent("expanded", this);
35974             },
35975             scope: this,
35976             block: true
35977         });
35978     },
35979
35980     anchors : {
35981         "west" : "left",
35982         "east" : "right",
35983         "north" : "top",
35984         "south" : "bottom"
35985     },
35986
35987     sanchors : {
35988         "west" : "l",
35989         "east" : "r",
35990         "north" : "t",
35991         "south" : "b"
35992     },
35993
35994     canchors : {
35995         "west" : "tl-tr",
35996         "east" : "tr-tl",
35997         "north" : "tl-bl",
35998         "south" : "bl-tl"
35999     },
36000
36001     getAnchor : function(){
36002         return this.anchors[this.position];
36003     },
36004
36005     getCollapseAnchor : function(){
36006         return this.canchors[this.position];
36007     },
36008
36009     getSlideAnchor : function(){
36010         return this.sanchors[this.position];
36011     },
36012
36013     getAlignAdj : function(){
36014         var cm = this.cmargins;
36015         switch(this.position){
36016             case "west":
36017                 return [0, 0];
36018             break;
36019             case "east":
36020                 return [0, 0];
36021             break;
36022             case "north":
36023                 return [0, 0];
36024             break;
36025             case "south":
36026                 return [0, 0];
36027             break;
36028         }
36029     },
36030
36031     getExpandAdj : function(){
36032         var c = this.collapsedEl, cm = this.cmargins;
36033         switch(this.position){
36034             case "west":
36035                 return [-(cm.right+c.getWidth()+cm.left), 0];
36036             break;
36037             case "east":
36038                 return [cm.right+c.getWidth()+cm.left, 0];
36039             break;
36040             case "north":
36041                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36042             break;
36043             case "south":
36044                 return [0, cm.top+cm.bottom+c.getHeight()];
36045             break;
36046         }
36047     }
36048 });/*
36049  * Based on:
36050  * Ext JS Library 1.1.1
36051  * Copyright(c) 2006-2007, Ext JS, LLC.
36052  *
36053  * Originally Released Under LGPL - original licence link has changed is not relivant.
36054  *
36055  * Fork - LGPL
36056  * <script type="text/javascript">
36057  */
36058 /*
36059  * These classes are private internal classes
36060  */
36061 Roo.bootstrap.layout.Center = function(config){
36062     config.region = "center";
36063     Roo.bootstrap.layout.Region.call(this, config);
36064     this.visible = true;
36065     this.minWidth = config.minWidth || 20;
36066     this.minHeight = config.minHeight || 20;
36067 };
36068
36069 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36070     hide : function(){
36071         // center panel can't be hidden
36072     },
36073     
36074     show : function(){
36075         // center panel can't be hidden
36076     },
36077     
36078     getMinWidth: function(){
36079         return this.minWidth;
36080     },
36081     
36082     getMinHeight: function(){
36083         return this.minHeight;
36084     }
36085 });
36086
36087
36088
36089
36090  
36091
36092
36093
36094
36095
36096 Roo.bootstrap.layout.North = function(config)
36097 {
36098     config.region = 'north';
36099     config.cursor = 'n-resize';
36100     
36101     Roo.bootstrap.layout.Split.call(this, config);
36102     
36103     
36104     if(this.split){
36105         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36106         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36107         this.split.el.addClass("roo-layout-split-v");
36108     }
36109     var size = config.initialSize || config.height;
36110     if(typeof size != "undefined"){
36111         this.el.setHeight(size);
36112     }
36113 };
36114 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36115 {
36116     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36117     
36118     
36119     
36120     getBox : function(){
36121         if(this.collapsed){
36122             return this.collapsedEl.getBox();
36123         }
36124         var box = this.el.getBox();
36125         if(this.split){
36126             box.height += this.split.el.getHeight();
36127         }
36128         return box;
36129     },
36130     
36131     updateBox : function(box){
36132         if(this.split && !this.collapsed){
36133             box.height -= this.split.el.getHeight();
36134             this.split.el.setLeft(box.x);
36135             this.split.el.setTop(box.y+box.height);
36136             this.split.el.setWidth(box.width);
36137         }
36138         if(this.collapsed){
36139             this.updateBody(box.width, null);
36140         }
36141         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36142     }
36143 });
36144
36145
36146
36147
36148
36149 Roo.bootstrap.layout.South = function(config){
36150     config.region = 'south';
36151     config.cursor = 's-resize';
36152     Roo.bootstrap.layout.Split.call(this, config);
36153     if(this.split){
36154         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36155         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36156         this.split.el.addClass("roo-layout-split-v");
36157     }
36158     var size = config.initialSize || config.height;
36159     if(typeof size != "undefined"){
36160         this.el.setHeight(size);
36161     }
36162 };
36163
36164 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36165     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36166     getBox : function(){
36167         if(this.collapsed){
36168             return this.collapsedEl.getBox();
36169         }
36170         var box = this.el.getBox();
36171         if(this.split){
36172             var sh = this.split.el.getHeight();
36173             box.height += sh;
36174             box.y -= sh;
36175         }
36176         return box;
36177     },
36178     
36179     updateBox : function(box){
36180         if(this.split && !this.collapsed){
36181             var sh = this.split.el.getHeight();
36182             box.height -= sh;
36183             box.y += sh;
36184             this.split.el.setLeft(box.x);
36185             this.split.el.setTop(box.y-sh);
36186             this.split.el.setWidth(box.width);
36187         }
36188         if(this.collapsed){
36189             this.updateBody(box.width, null);
36190         }
36191         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36192     }
36193 });
36194
36195 Roo.bootstrap.layout.East = function(config){
36196     config.region = "east";
36197     config.cursor = "e-resize";
36198     Roo.bootstrap.layout.Split.call(this, config);
36199     if(this.split){
36200         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36201         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36202         this.split.el.addClass("roo-layout-split-h");
36203     }
36204     var size = config.initialSize || config.width;
36205     if(typeof size != "undefined"){
36206         this.el.setWidth(size);
36207     }
36208 };
36209 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36210     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36211     getBox : function(){
36212         if(this.collapsed){
36213             return this.collapsedEl.getBox();
36214         }
36215         var box = this.el.getBox();
36216         if(this.split){
36217             var sw = this.split.el.getWidth();
36218             box.width += sw;
36219             box.x -= sw;
36220         }
36221         return box;
36222     },
36223
36224     updateBox : function(box){
36225         if(this.split && !this.collapsed){
36226             var sw = this.split.el.getWidth();
36227             box.width -= sw;
36228             this.split.el.setLeft(box.x);
36229             this.split.el.setTop(box.y);
36230             this.split.el.setHeight(box.height);
36231             box.x += sw;
36232         }
36233         if(this.collapsed){
36234             this.updateBody(null, box.height);
36235         }
36236         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36237     }
36238 });
36239
36240 Roo.bootstrap.layout.West = function(config){
36241     config.region = "west";
36242     config.cursor = "w-resize";
36243     
36244     Roo.bootstrap.layout.Split.call(this, config);
36245     if(this.split){
36246         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36247         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36248         this.split.el.addClass("roo-layout-split-h");
36249     }
36250     
36251 };
36252 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36253     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36254     
36255     onRender: function(ctr, pos)
36256     {
36257         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36258         var size = this.config.initialSize || this.config.width;
36259         if(typeof size != "undefined"){
36260             this.el.setWidth(size);
36261         }
36262     },
36263     
36264     getBox : function(){
36265         if(this.collapsed){
36266             return this.collapsedEl.getBox();
36267         }
36268         var box = this.el.getBox();
36269         if(this.split){
36270             box.width += this.split.el.getWidth();
36271         }
36272         return box;
36273     },
36274     
36275     updateBox : function(box){
36276         if(this.split && !this.collapsed){
36277             var sw = this.split.el.getWidth();
36278             box.width -= sw;
36279             this.split.el.setLeft(box.x+box.width);
36280             this.split.el.setTop(box.y);
36281             this.split.el.setHeight(box.height);
36282         }
36283         if(this.collapsed){
36284             this.updateBody(null, box.height);
36285         }
36286         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36287     }
36288 });
36289 Roo.namespace("Roo.bootstrap.panel");/*
36290  * Based on:
36291  * Ext JS Library 1.1.1
36292  * Copyright(c) 2006-2007, Ext JS, LLC.
36293  *
36294  * Originally Released Under LGPL - original licence link has changed is not relivant.
36295  *
36296  * Fork - LGPL
36297  * <script type="text/javascript">
36298  */
36299 /**
36300  * @class Roo.ContentPanel
36301  * @extends Roo.util.Observable
36302  * A basic ContentPanel element.
36303  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36304  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36305  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
36306  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36307  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36308  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36309  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36310  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36311  * @cfg {String} title          The title for this panel
36312  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36313  * @cfg {String} url            Calls {@link #setUrl} with this value
36314  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36315  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36316  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36317  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36318  * @cfg {Boolean} badges render the badges
36319
36320  * @constructor
36321  * Create a new ContentPanel.
36322  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36323  * @param {String/Object} config A string to set only the title or a config object
36324  * @param {String} content (optional) Set the HTML content for this panel
36325  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36326  */
36327 Roo.bootstrap.panel.Content = function( config){
36328     
36329     this.tpl = config.tpl || false;
36330     
36331     var el = config.el;
36332     var content = config.content;
36333
36334     if(config.autoCreate){ // xtype is available if this is called from factory
36335         el = Roo.id();
36336     }
36337     this.el = Roo.get(el);
36338     if(!this.el && config && config.autoCreate){
36339         if(typeof config.autoCreate == "object"){
36340             if(!config.autoCreate.id){
36341                 config.autoCreate.id = config.id||el;
36342             }
36343             this.el = Roo.DomHelper.append(document.body,
36344                         config.autoCreate, true);
36345         }else{
36346             var elcfg =  {   tag: "div",
36347                             cls: "roo-layout-inactive-content",
36348                             id: config.id||el
36349                             };
36350             if (config.html) {
36351                 elcfg.html = config.html;
36352                 
36353             }
36354                         
36355             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36356         }
36357     } 
36358     this.closable = false;
36359     this.loaded = false;
36360     this.active = false;
36361    
36362       
36363     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36364         
36365         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36366         
36367         this.wrapEl = this.el; //this.el.wrap();
36368         var ti = [];
36369         if (config.toolbar.items) {
36370             ti = config.toolbar.items ;
36371             delete config.toolbar.items ;
36372         }
36373         
36374         var nitems = [];
36375         this.toolbar.render(this.wrapEl, 'before');
36376         for(var i =0;i < ti.length;i++) {
36377           //  Roo.log(['add child', items[i]]);
36378             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36379         }
36380         this.toolbar.items = nitems;
36381         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36382         delete config.toolbar;
36383         
36384     }
36385     /*
36386     // xtype created footer. - not sure if will work as we normally have to render first..
36387     if (this.footer && !this.footer.el && this.footer.xtype) {
36388         if (!this.wrapEl) {
36389             this.wrapEl = this.el.wrap();
36390         }
36391     
36392         this.footer.container = this.wrapEl.createChild();
36393          
36394         this.footer = Roo.factory(this.footer, Roo);
36395         
36396     }
36397     */
36398     
36399      if(typeof config == "string"){
36400         this.title = config;
36401     }else{
36402         Roo.apply(this, config);
36403     }
36404     
36405     if(this.resizeEl){
36406         this.resizeEl = Roo.get(this.resizeEl, true);
36407     }else{
36408         this.resizeEl = this.el;
36409     }
36410     // handle view.xtype
36411     
36412  
36413     
36414     
36415     this.addEvents({
36416         /**
36417          * @event activate
36418          * Fires when this panel is activated. 
36419          * @param {Roo.ContentPanel} this
36420          */
36421         "activate" : true,
36422         /**
36423          * @event deactivate
36424          * Fires when this panel is activated. 
36425          * @param {Roo.ContentPanel} this
36426          */
36427         "deactivate" : true,
36428
36429         /**
36430          * @event resize
36431          * Fires when this panel is resized if fitToFrame is true.
36432          * @param {Roo.ContentPanel} this
36433          * @param {Number} width The width after any component adjustments
36434          * @param {Number} height The height after any component adjustments
36435          */
36436         "resize" : true,
36437         
36438          /**
36439          * @event render
36440          * Fires when this tab is created
36441          * @param {Roo.ContentPanel} this
36442          */
36443         "render" : true
36444         
36445         
36446         
36447     });
36448     
36449
36450     
36451     
36452     if(this.autoScroll){
36453         this.resizeEl.setStyle("overflow", "auto");
36454     } else {
36455         // fix randome scrolling
36456         //this.el.on('scroll', function() {
36457         //    Roo.log('fix random scolling');
36458         //    this.scrollTo('top',0); 
36459         //});
36460     }
36461     content = content || this.content;
36462     if(content){
36463         this.setContent(content);
36464     }
36465     if(config && config.url){
36466         this.setUrl(this.url, this.params, this.loadOnce);
36467     }
36468     
36469     
36470     
36471     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36472     
36473     if (this.view && typeof(this.view.xtype) != 'undefined') {
36474         this.view.el = this.el.appendChild(document.createElement("div"));
36475         this.view = Roo.factory(this.view); 
36476         this.view.render  &&  this.view.render(false, '');  
36477     }
36478     
36479     
36480     this.fireEvent('render', this);
36481 };
36482
36483 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36484     
36485     tabTip : '',
36486     
36487     setRegion : function(region){
36488         this.region = region;
36489         this.setActiveClass(region && !this.background);
36490     },
36491     
36492     
36493     setActiveClass: function(state)
36494     {
36495         if(state){
36496            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36497            this.el.setStyle('position','relative');
36498         }else{
36499            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36500            this.el.setStyle('position', 'absolute');
36501         } 
36502     },
36503     
36504     /**
36505      * Returns the toolbar for this Panel if one was configured. 
36506      * @return {Roo.Toolbar} 
36507      */
36508     getToolbar : function(){
36509         return this.toolbar;
36510     },
36511     
36512     setActiveState : function(active)
36513     {
36514         this.active = active;
36515         this.setActiveClass(active);
36516         if(!active){
36517             if(this.fireEvent("deactivate", this) === false){
36518                 return false;
36519             }
36520             return true;
36521         }
36522         this.fireEvent("activate", this);
36523         return true;
36524     },
36525     /**
36526      * Updates this panel's element
36527      * @param {String} content The new content
36528      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36529     */
36530     setContent : function(content, loadScripts){
36531         this.el.update(content, loadScripts);
36532     },
36533
36534     ignoreResize : function(w, h){
36535         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36536             return true;
36537         }else{
36538             this.lastSize = {width: w, height: h};
36539             return false;
36540         }
36541     },
36542     /**
36543      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36544      * @return {Roo.UpdateManager} The UpdateManager
36545      */
36546     getUpdateManager : function(){
36547         return this.el.getUpdateManager();
36548     },
36549      /**
36550      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36551      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
36552 <pre><code>
36553 panel.load({
36554     url: "your-url.php",
36555     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36556     callback: yourFunction,
36557     scope: yourObject, //(optional scope)
36558     discardUrl: false,
36559     nocache: false,
36560     text: "Loading...",
36561     timeout: 30,
36562     scripts: false
36563 });
36564 </code></pre>
36565      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36566      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
36567      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
36568      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36569      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
36570      * @return {Roo.ContentPanel} this
36571      */
36572     load : function(){
36573         var um = this.el.getUpdateManager();
36574         um.update.apply(um, arguments);
36575         return this;
36576     },
36577
36578
36579     /**
36580      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
36581      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36582      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
36583      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
36584      * @return {Roo.UpdateManager} The UpdateManager
36585      */
36586     setUrl : function(url, params, loadOnce){
36587         if(this.refreshDelegate){
36588             this.removeListener("activate", this.refreshDelegate);
36589         }
36590         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36591         this.on("activate", this.refreshDelegate);
36592         return this.el.getUpdateManager();
36593     },
36594     
36595     _handleRefresh : function(url, params, loadOnce){
36596         if(!loadOnce || !this.loaded){
36597             var updater = this.el.getUpdateManager();
36598             updater.update(url, params, this._setLoaded.createDelegate(this));
36599         }
36600     },
36601     
36602     _setLoaded : function(){
36603         this.loaded = true;
36604     }, 
36605     
36606     /**
36607      * Returns this panel's id
36608      * @return {String} 
36609      */
36610     getId : function(){
36611         return this.el.id;
36612     },
36613     
36614     /** 
36615      * Returns this panel's element - used by regiosn to add.
36616      * @return {Roo.Element} 
36617      */
36618     getEl : function(){
36619         return this.wrapEl || this.el;
36620     },
36621     
36622    
36623     
36624     adjustForComponents : function(width, height)
36625     {
36626         //Roo.log('adjustForComponents ');
36627         if(this.resizeEl != this.el){
36628             width -= this.el.getFrameWidth('lr');
36629             height -= this.el.getFrameWidth('tb');
36630         }
36631         if(this.toolbar){
36632             var te = this.toolbar.getEl();
36633             te.setWidth(width);
36634             height -= te.getHeight();
36635         }
36636         if(this.footer){
36637             var te = this.footer.getEl();
36638             te.setWidth(width);
36639             height -= te.getHeight();
36640         }
36641         
36642         
36643         if(this.adjustments){
36644             width += this.adjustments[0];
36645             height += this.adjustments[1];
36646         }
36647         return {"width": width, "height": height};
36648     },
36649     
36650     setSize : function(width, height){
36651         if(this.fitToFrame && !this.ignoreResize(width, height)){
36652             if(this.fitContainer && this.resizeEl != this.el){
36653                 this.el.setSize(width, height);
36654             }
36655             var size = this.adjustForComponents(width, height);
36656             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36657             this.fireEvent('resize', this, size.width, size.height);
36658         }
36659     },
36660     
36661     /**
36662      * Returns this panel's title
36663      * @return {String} 
36664      */
36665     getTitle : function(){
36666         
36667         if (typeof(this.title) != 'object') {
36668             return this.title;
36669         }
36670         
36671         var t = '';
36672         for (var k in this.title) {
36673             if (!this.title.hasOwnProperty(k)) {
36674                 continue;
36675             }
36676             
36677             if (k.indexOf('-') >= 0) {
36678                 var s = k.split('-');
36679                 for (var i = 0; i<s.length; i++) {
36680                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36681                 }
36682             } else {
36683                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36684             }
36685         }
36686         return t;
36687     },
36688     
36689     /**
36690      * Set this panel's title
36691      * @param {String} title
36692      */
36693     setTitle : function(title){
36694         this.title = title;
36695         if(this.region){
36696             this.region.updatePanelTitle(this, title);
36697         }
36698     },
36699     
36700     /**
36701      * Returns true is this panel was configured to be closable
36702      * @return {Boolean} 
36703      */
36704     isClosable : function(){
36705         return this.closable;
36706     },
36707     
36708     beforeSlide : function(){
36709         this.el.clip();
36710         this.resizeEl.clip();
36711     },
36712     
36713     afterSlide : function(){
36714         this.el.unclip();
36715         this.resizeEl.unclip();
36716     },
36717     
36718     /**
36719      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36720      *   Will fail silently if the {@link #setUrl} method has not been called.
36721      *   This does not activate the panel, just updates its content.
36722      */
36723     refresh : function(){
36724         if(this.refreshDelegate){
36725            this.loaded = false;
36726            this.refreshDelegate();
36727         }
36728     },
36729     
36730     /**
36731      * Destroys this panel
36732      */
36733     destroy : function(){
36734         this.el.removeAllListeners();
36735         var tempEl = document.createElement("span");
36736         tempEl.appendChild(this.el.dom);
36737         tempEl.innerHTML = "";
36738         this.el.remove();
36739         this.el = null;
36740     },
36741     
36742     /**
36743      * form - if the content panel contains a form - this is a reference to it.
36744      * @type {Roo.form.Form}
36745      */
36746     form : false,
36747     /**
36748      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36749      *    This contains a reference to it.
36750      * @type {Roo.View}
36751      */
36752     view : false,
36753     
36754       /**
36755      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36756      * <pre><code>
36757
36758 layout.addxtype({
36759        xtype : 'Form',
36760        items: [ .... ]
36761    }
36762 );
36763
36764 </code></pre>
36765      * @param {Object} cfg Xtype definition of item to add.
36766      */
36767     
36768     
36769     getChildContainer: function () {
36770         return this.getEl();
36771     }
36772     
36773     
36774     /*
36775         var  ret = new Roo.factory(cfg);
36776         return ret;
36777         
36778         
36779         // add form..
36780         if (cfg.xtype.match(/^Form$/)) {
36781             
36782             var el;
36783             //if (this.footer) {
36784             //    el = this.footer.container.insertSibling(false, 'before');
36785             //} else {
36786                 el = this.el.createChild();
36787             //}
36788
36789             this.form = new  Roo.form.Form(cfg);
36790             
36791             
36792             if ( this.form.allItems.length) {
36793                 this.form.render(el.dom);
36794             }
36795             return this.form;
36796         }
36797         // should only have one of theses..
36798         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36799             // views.. should not be just added - used named prop 'view''
36800             
36801             cfg.el = this.el.appendChild(document.createElement("div"));
36802             // factory?
36803             
36804             var ret = new Roo.factory(cfg);
36805              
36806              ret.render && ret.render(false, ''); // render blank..
36807             this.view = ret;
36808             return ret;
36809         }
36810         return false;
36811     }
36812     \*/
36813 });
36814  
36815 /**
36816  * @class Roo.bootstrap.panel.Grid
36817  * @extends Roo.bootstrap.panel.Content
36818  * @constructor
36819  * Create a new GridPanel.
36820  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36821  * @param {Object} config A the config object
36822   
36823  */
36824
36825
36826
36827 Roo.bootstrap.panel.Grid = function(config)
36828 {
36829     
36830       
36831     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36832         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36833
36834     config.el = this.wrapper;
36835     //this.el = this.wrapper;
36836     
36837       if (config.container) {
36838         // ctor'ed from a Border/panel.grid
36839         
36840         
36841         this.wrapper.setStyle("overflow", "hidden");
36842         this.wrapper.addClass('roo-grid-container');
36843
36844     }
36845     
36846     
36847     if(config.toolbar){
36848         var tool_el = this.wrapper.createChild();    
36849         this.toolbar = Roo.factory(config.toolbar);
36850         var ti = [];
36851         if (config.toolbar.items) {
36852             ti = config.toolbar.items ;
36853             delete config.toolbar.items ;
36854         }
36855         
36856         var nitems = [];
36857         this.toolbar.render(tool_el);
36858         for(var i =0;i < ti.length;i++) {
36859           //  Roo.log(['add child', items[i]]);
36860             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36861         }
36862         this.toolbar.items = nitems;
36863         
36864         delete config.toolbar;
36865     }
36866     
36867     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36868     config.grid.scrollBody = true;;
36869     config.grid.monitorWindowResize = false; // turn off autosizing
36870     config.grid.autoHeight = false;
36871     config.grid.autoWidth = false;
36872     
36873     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36874     
36875     if (config.background) {
36876         // render grid on panel activation (if panel background)
36877         this.on('activate', function(gp) {
36878             if (!gp.grid.rendered) {
36879                 gp.grid.render(this.wrapper);
36880                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36881             }
36882         });
36883             
36884     } else {
36885         this.grid.render(this.wrapper);
36886         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36887
36888     }
36889     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36890     // ??? needed ??? config.el = this.wrapper;
36891     
36892     
36893     
36894   
36895     // xtype created footer. - not sure if will work as we normally have to render first..
36896     if (this.footer && !this.footer.el && this.footer.xtype) {
36897         
36898         var ctr = this.grid.getView().getFooterPanel(true);
36899         this.footer.dataSource = this.grid.dataSource;
36900         this.footer = Roo.factory(this.footer, Roo);
36901         this.footer.render(ctr);
36902         
36903     }
36904     
36905     
36906     
36907     
36908      
36909 };
36910
36911 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36912     getId : function(){
36913         return this.grid.id;
36914     },
36915     
36916     /**
36917      * Returns the grid for this panel
36918      * @return {Roo.bootstrap.Table} 
36919      */
36920     getGrid : function(){
36921         return this.grid;    
36922     },
36923     
36924     setSize : function(width, height){
36925         if(!this.ignoreResize(width, height)){
36926             var grid = this.grid;
36927             var size = this.adjustForComponents(width, height);
36928             var gridel = grid.getGridEl();
36929             gridel.setSize(size.width, size.height);
36930             /*
36931             var thd = grid.getGridEl().select('thead',true).first();
36932             var tbd = grid.getGridEl().select('tbody', true).first();
36933             if (tbd) {
36934                 tbd.setSize(width, height - thd.getHeight());
36935             }
36936             */
36937             grid.autoSize();
36938         }
36939     },
36940      
36941     
36942     
36943     beforeSlide : function(){
36944         this.grid.getView().scroller.clip();
36945     },
36946     
36947     afterSlide : function(){
36948         this.grid.getView().scroller.unclip();
36949     },
36950     
36951     destroy : function(){
36952         this.grid.destroy();
36953         delete this.grid;
36954         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36955     }
36956 });
36957
36958 /**
36959  * @class Roo.bootstrap.panel.Nest
36960  * @extends Roo.bootstrap.panel.Content
36961  * @constructor
36962  * Create a new Panel, that can contain a layout.Border.
36963  * 
36964  * 
36965  * @param {Roo.BorderLayout} layout The layout for this panel
36966  * @param {String/Object} config A string to set only the title or a config object
36967  */
36968 Roo.bootstrap.panel.Nest = function(config)
36969 {
36970     // construct with only one argument..
36971     /* FIXME - implement nicer consturctors
36972     if (layout.layout) {
36973         config = layout;
36974         layout = config.layout;
36975         delete config.layout;
36976     }
36977     if (layout.xtype && !layout.getEl) {
36978         // then layout needs constructing..
36979         layout = Roo.factory(layout, Roo);
36980     }
36981     */
36982     
36983     config.el =  config.layout.getEl();
36984     
36985     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36986     
36987     config.layout.monitorWindowResize = false; // turn off autosizing
36988     this.layout = config.layout;
36989     this.layout.getEl().addClass("roo-layout-nested-layout");
36990     
36991     
36992     
36993     
36994 };
36995
36996 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36997
36998     setSize : function(width, height){
36999         if(!this.ignoreResize(width, height)){
37000             var size = this.adjustForComponents(width, height);
37001             var el = this.layout.getEl();
37002             if (size.height < 1) {
37003                 el.setWidth(size.width);   
37004             } else {
37005                 el.setSize(size.width, size.height);
37006             }
37007             var touch = el.dom.offsetWidth;
37008             this.layout.layout();
37009             // ie requires a double layout on the first pass
37010             if(Roo.isIE && !this.initialized){
37011                 this.initialized = true;
37012                 this.layout.layout();
37013             }
37014         }
37015     },
37016     
37017     // activate all subpanels if not currently active..
37018     
37019     setActiveState : function(active){
37020         this.active = active;
37021         this.setActiveClass(active);
37022         
37023         if(!active){
37024             this.fireEvent("deactivate", this);
37025             return;
37026         }
37027         
37028         this.fireEvent("activate", this);
37029         // not sure if this should happen before or after..
37030         if (!this.layout) {
37031             return; // should not happen..
37032         }
37033         var reg = false;
37034         for (var r in this.layout.regions) {
37035             reg = this.layout.getRegion(r);
37036             if (reg.getActivePanel()) {
37037                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37038                 reg.setActivePanel(reg.getActivePanel());
37039                 continue;
37040             }
37041             if (!reg.panels.length) {
37042                 continue;
37043             }
37044             reg.showPanel(reg.getPanel(0));
37045         }
37046         
37047         
37048         
37049         
37050     },
37051     
37052     /**
37053      * Returns the nested BorderLayout for this panel
37054      * @return {Roo.BorderLayout} 
37055      */
37056     getLayout : function(){
37057         return this.layout;
37058     },
37059     
37060      /**
37061      * Adds a xtype elements to the layout of the nested panel
37062      * <pre><code>
37063
37064 panel.addxtype({
37065        xtype : 'ContentPanel',
37066        region: 'west',
37067        items: [ .... ]
37068    }
37069 );
37070
37071 panel.addxtype({
37072         xtype : 'NestedLayoutPanel',
37073         region: 'west',
37074         layout: {
37075            center: { },
37076            west: { }   
37077         },
37078         items : [ ... list of content panels or nested layout panels.. ]
37079    }
37080 );
37081 </code></pre>
37082      * @param {Object} cfg Xtype definition of item to add.
37083      */
37084     addxtype : function(cfg) {
37085         return this.layout.addxtype(cfg);
37086     
37087     }
37088 });        /*
37089  * Based on:
37090  * Ext JS Library 1.1.1
37091  * Copyright(c) 2006-2007, Ext JS, LLC.
37092  *
37093  * Originally Released Under LGPL - original licence link has changed is not relivant.
37094  *
37095  * Fork - LGPL
37096  * <script type="text/javascript">
37097  */
37098 /**
37099  * @class Roo.TabPanel
37100  * @extends Roo.util.Observable
37101  * A lightweight tab container.
37102  * <br><br>
37103  * Usage:
37104  * <pre><code>
37105 // basic tabs 1, built from existing content
37106 var tabs = new Roo.TabPanel("tabs1");
37107 tabs.addTab("script", "View Script");
37108 tabs.addTab("markup", "View Markup");
37109 tabs.activate("script");
37110
37111 // more advanced tabs, built from javascript
37112 var jtabs = new Roo.TabPanel("jtabs");
37113 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37114
37115 // set up the UpdateManager
37116 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37117 var updater = tab2.getUpdateManager();
37118 updater.setDefaultUrl("ajax1.htm");
37119 tab2.on('activate', updater.refresh, updater, true);
37120
37121 // Use setUrl for Ajax loading
37122 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37123 tab3.setUrl("ajax2.htm", null, true);
37124
37125 // Disabled tab
37126 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37127 tab4.disable();
37128
37129 jtabs.activate("jtabs-1");
37130  * </code></pre>
37131  * @constructor
37132  * Create a new TabPanel.
37133  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37134  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37135  */
37136 Roo.bootstrap.panel.Tabs = function(config){
37137     /**
37138     * The container element for this TabPanel.
37139     * @type Roo.Element
37140     */
37141     this.el = Roo.get(config.el);
37142     delete config.el;
37143     if(config){
37144         if(typeof config == "boolean"){
37145             this.tabPosition = config ? "bottom" : "top";
37146         }else{
37147             Roo.apply(this, config);
37148         }
37149     }
37150     
37151     if(this.tabPosition == "bottom"){
37152         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37153         this.el.addClass("roo-tabs-bottom");
37154     }
37155     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37156     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37157     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37158     if(Roo.isIE){
37159         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37160     }
37161     if(this.tabPosition != "bottom"){
37162         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37163          * @type Roo.Element
37164          */
37165         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37166         this.el.addClass("roo-tabs-top");
37167     }
37168     this.items = [];
37169
37170     this.bodyEl.setStyle("position", "relative");
37171
37172     this.active = null;
37173     this.activateDelegate = this.activate.createDelegate(this);
37174
37175     this.addEvents({
37176         /**
37177          * @event tabchange
37178          * Fires when the active tab changes
37179          * @param {Roo.TabPanel} this
37180          * @param {Roo.TabPanelItem} activePanel The new active tab
37181          */
37182         "tabchange": true,
37183         /**
37184          * @event beforetabchange
37185          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37186          * @param {Roo.TabPanel} this
37187          * @param {Object} e Set cancel to true on this object to cancel the tab change
37188          * @param {Roo.TabPanelItem} tab The tab being changed to
37189          */
37190         "beforetabchange" : true
37191     });
37192
37193     Roo.EventManager.onWindowResize(this.onResize, this);
37194     this.cpad = this.el.getPadding("lr");
37195     this.hiddenCount = 0;
37196
37197
37198     // toolbar on the tabbar support...
37199     if (this.toolbar) {
37200         alert("no toolbar support yet");
37201         this.toolbar  = false;
37202         /*
37203         var tcfg = this.toolbar;
37204         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37205         this.toolbar = new Roo.Toolbar(tcfg);
37206         if (Roo.isSafari) {
37207             var tbl = tcfg.container.child('table', true);
37208             tbl.setAttribute('width', '100%');
37209         }
37210         */
37211         
37212     }
37213    
37214
37215
37216     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37217 };
37218
37219 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37220     /*
37221      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37222      */
37223     tabPosition : "top",
37224     /*
37225      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37226      */
37227     currentTabWidth : 0,
37228     /*
37229      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37230      */
37231     minTabWidth : 40,
37232     /*
37233      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37234      */
37235     maxTabWidth : 250,
37236     /*
37237      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37238      */
37239     preferredTabWidth : 175,
37240     /*
37241      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37242      */
37243     resizeTabs : false,
37244     /*
37245      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37246      */
37247     monitorResize : true,
37248     /*
37249      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37250      */
37251     toolbar : false,
37252
37253     /**
37254      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37255      * @param {String} id The id of the div to use <b>or create</b>
37256      * @param {String} text The text for the tab
37257      * @param {String} content (optional) Content to put in the TabPanelItem body
37258      * @param {Boolean} closable (optional) True to create a close icon on the tab
37259      * @return {Roo.TabPanelItem} The created TabPanelItem
37260      */
37261     addTab : function(id, text, content, closable, tpl)
37262     {
37263         var item = new Roo.bootstrap.panel.TabItem({
37264             panel: this,
37265             id : id,
37266             text : text,
37267             closable : closable,
37268             tpl : tpl
37269         });
37270         this.addTabItem(item);
37271         if(content){
37272             item.setContent(content);
37273         }
37274         return item;
37275     },
37276
37277     /**
37278      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37279      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37280      * @return {Roo.TabPanelItem}
37281      */
37282     getTab : function(id){
37283         return this.items[id];
37284     },
37285
37286     /**
37287      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37288      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37289      */
37290     hideTab : function(id){
37291         var t = this.items[id];
37292         if(!t.isHidden()){
37293            t.setHidden(true);
37294            this.hiddenCount++;
37295            this.autoSizeTabs();
37296         }
37297     },
37298
37299     /**
37300      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37301      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37302      */
37303     unhideTab : function(id){
37304         var t = this.items[id];
37305         if(t.isHidden()){
37306            t.setHidden(false);
37307            this.hiddenCount--;
37308            this.autoSizeTabs();
37309         }
37310     },
37311
37312     /**
37313      * Adds an existing {@link Roo.TabPanelItem}.
37314      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37315      */
37316     addTabItem : function(item){
37317         this.items[item.id] = item;
37318         this.items.push(item);
37319       //  if(this.resizeTabs){
37320     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37321   //         this.autoSizeTabs();
37322 //        }else{
37323 //            item.autoSize();
37324        // }
37325     },
37326
37327     /**
37328      * Removes a {@link Roo.TabPanelItem}.
37329      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37330      */
37331     removeTab : function(id){
37332         var items = this.items;
37333         var tab = items[id];
37334         if(!tab) { return; }
37335         var index = items.indexOf(tab);
37336         if(this.active == tab && items.length > 1){
37337             var newTab = this.getNextAvailable(index);
37338             if(newTab) {
37339                 newTab.activate();
37340             }
37341         }
37342         this.stripEl.dom.removeChild(tab.pnode.dom);
37343         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37344             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37345         }
37346         items.splice(index, 1);
37347         delete this.items[tab.id];
37348         tab.fireEvent("close", tab);
37349         tab.purgeListeners();
37350         this.autoSizeTabs();
37351     },
37352
37353     getNextAvailable : function(start){
37354         var items = this.items;
37355         var index = start;
37356         // look for a next tab that will slide over to
37357         // replace the one being removed
37358         while(index < items.length){
37359             var item = items[++index];
37360             if(item && !item.isHidden()){
37361                 return item;
37362             }
37363         }
37364         // if one isn't found select the previous tab (on the left)
37365         index = start;
37366         while(index >= 0){
37367             var item = items[--index];
37368             if(item && !item.isHidden()){
37369                 return item;
37370             }
37371         }
37372         return null;
37373     },
37374
37375     /**
37376      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37377      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37378      */
37379     disableTab : function(id){
37380         var tab = this.items[id];
37381         if(tab && this.active != tab){
37382             tab.disable();
37383         }
37384     },
37385
37386     /**
37387      * Enables a {@link Roo.TabPanelItem} that is disabled.
37388      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37389      */
37390     enableTab : function(id){
37391         var tab = this.items[id];
37392         tab.enable();
37393     },
37394
37395     /**
37396      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37397      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37398      * @return {Roo.TabPanelItem} The TabPanelItem.
37399      */
37400     activate : function(id){
37401         var tab = this.items[id];
37402         if(!tab){
37403             return null;
37404         }
37405         if(tab == this.active || tab.disabled){
37406             return tab;
37407         }
37408         var e = {};
37409         this.fireEvent("beforetabchange", this, e, tab);
37410         if(e.cancel !== true && !tab.disabled){
37411             if(this.active){
37412                 this.active.hide();
37413             }
37414             this.active = this.items[id];
37415             this.active.show();
37416             this.fireEvent("tabchange", this, this.active);
37417         }
37418         return tab;
37419     },
37420
37421     /**
37422      * Gets the active {@link Roo.TabPanelItem}.
37423      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37424      */
37425     getActiveTab : function(){
37426         return this.active;
37427     },
37428
37429     /**
37430      * Updates the tab body element to fit the height of the container element
37431      * for overflow scrolling
37432      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37433      */
37434     syncHeight : function(targetHeight){
37435         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37436         var bm = this.bodyEl.getMargins();
37437         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37438         this.bodyEl.setHeight(newHeight);
37439         return newHeight;
37440     },
37441
37442     onResize : function(){
37443         if(this.monitorResize){
37444             this.autoSizeTabs();
37445         }
37446     },
37447
37448     /**
37449      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37450      */
37451     beginUpdate : function(){
37452         this.updating = true;
37453     },
37454
37455     /**
37456      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37457      */
37458     endUpdate : function(){
37459         this.updating = false;
37460         this.autoSizeTabs();
37461     },
37462
37463     /**
37464      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37465      */
37466     autoSizeTabs : function(){
37467         var count = this.items.length;
37468         var vcount = count - this.hiddenCount;
37469         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37470             return;
37471         }
37472         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37473         var availWidth = Math.floor(w / vcount);
37474         var b = this.stripBody;
37475         if(b.getWidth() > w){
37476             var tabs = this.items;
37477             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37478             if(availWidth < this.minTabWidth){
37479                 /*if(!this.sleft){    // incomplete scrolling code
37480                     this.createScrollButtons();
37481                 }
37482                 this.showScroll();
37483                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37484             }
37485         }else{
37486             if(this.currentTabWidth < this.preferredTabWidth){
37487                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37488             }
37489         }
37490     },
37491
37492     /**
37493      * Returns the number of tabs in this TabPanel.
37494      * @return {Number}
37495      */
37496      getCount : function(){
37497          return this.items.length;
37498      },
37499
37500     /**
37501      * Resizes all the tabs to the passed width
37502      * @param {Number} The new width
37503      */
37504     setTabWidth : function(width){
37505         this.currentTabWidth = width;
37506         for(var i = 0, len = this.items.length; i < len; i++) {
37507                 if(!this.items[i].isHidden()) {
37508                 this.items[i].setWidth(width);
37509             }
37510         }
37511     },
37512
37513     /**
37514      * Destroys this TabPanel
37515      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37516      */
37517     destroy : function(removeEl){
37518         Roo.EventManager.removeResizeListener(this.onResize, this);
37519         for(var i = 0, len = this.items.length; i < len; i++){
37520             this.items[i].purgeListeners();
37521         }
37522         if(removeEl === true){
37523             this.el.update("");
37524             this.el.remove();
37525         }
37526     },
37527     
37528     createStrip : function(container)
37529     {
37530         var strip = document.createElement("nav");
37531         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37532         container.appendChild(strip);
37533         return strip;
37534     },
37535     
37536     createStripList : function(strip)
37537     {
37538         // div wrapper for retard IE
37539         // returns the "tr" element.
37540         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37541         //'<div class="x-tabs-strip-wrap">'+
37542           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37543           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37544         return strip.firstChild; //.firstChild.firstChild.firstChild;
37545     },
37546     createBody : function(container)
37547     {
37548         var body = document.createElement("div");
37549         Roo.id(body, "tab-body");
37550         //Roo.fly(body).addClass("x-tabs-body");
37551         Roo.fly(body).addClass("tab-content");
37552         container.appendChild(body);
37553         return body;
37554     },
37555     createItemBody :function(bodyEl, id){
37556         var body = Roo.getDom(id);
37557         if(!body){
37558             body = document.createElement("div");
37559             body.id = id;
37560         }
37561         //Roo.fly(body).addClass("x-tabs-item-body");
37562         Roo.fly(body).addClass("tab-pane");
37563          bodyEl.insertBefore(body, bodyEl.firstChild);
37564         return body;
37565     },
37566     /** @private */
37567     createStripElements :  function(stripEl, text, closable, tpl)
37568     {
37569         var td = document.createElement("li"); // was td..
37570         
37571         
37572         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37573         
37574         
37575         stripEl.appendChild(td);
37576         /*if(closable){
37577             td.className = "x-tabs-closable";
37578             if(!this.closeTpl){
37579                 this.closeTpl = new Roo.Template(
37580                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37581                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37582                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37583                 );
37584             }
37585             var el = this.closeTpl.overwrite(td, {"text": text});
37586             var close = el.getElementsByTagName("div")[0];
37587             var inner = el.getElementsByTagName("em")[0];
37588             return {"el": el, "close": close, "inner": inner};
37589         } else {
37590         */
37591         // not sure what this is..
37592 //            if(!this.tabTpl){
37593                 //this.tabTpl = new Roo.Template(
37594                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37595                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37596                 //);
37597 //                this.tabTpl = new Roo.Template(
37598 //                   '<a href="#">' +
37599 //                   '<span unselectable="on"' +
37600 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37601 //                            ' >{text}</span></a>'
37602 //                );
37603 //                
37604 //            }
37605
37606
37607             var template = tpl || this.tabTpl || false;
37608             
37609             if(!template){
37610                 
37611                 template = new Roo.Template(
37612                    '<a href="#">' +
37613                    '<span unselectable="on"' +
37614                             (this.disableTooltips ? '' : ' title="{text}"') +
37615                             ' >{text}</span></a>'
37616                 );
37617             }
37618             
37619             switch (typeof(template)) {
37620                 case 'object' :
37621                     break;
37622                 case 'string' :
37623                     template = new Roo.Template(template);
37624                     break;
37625                 default :
37626                     break;
37627             }
37628             
37629             var el = template.overwrite(td, {"text": text});
37630             
37631             var inner = el.getElementsByTagName("span")[0];
37632             
37633             return {"el": el, "inner": inner};
37634             
37635     }
37636         
37637     
37638 });
37639
37640 /**
37641  * @class Roo.TabPanelItem
37642  * @extends Roo.util.Observable
37643  * Represents an individual item (tab plus body) in a TabPanel.
37644  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37645  * @param {String} id The id of this TabPanelItem
37646  * @param {String} text The text for the tab of this TabPanelItem
37647  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37648  */
37649 Roo.bootstrap.panel.TabItem = function(config){
37650     /**
37651      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37652      * @type Roo.TabPanel
37653      */
37654     this.tabPanel = config.panel;
37655     /**
37656      * The id for this TabPanelItem
37657      * @type String
37658      */
37659     this.id = config.id;
37660     /** @private */
37661     this.disabled = false;
37662     /** @private */
37663     this.text = config.text;
37664     /** @private */
37665     this.loaded = false;
37666     this.closable = config.closable;
37667
37668     /**
37669      * The body element for this TabPanelItem.
37670      * @type Roo.Element
37671      */
37672     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37673     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37674     this.bodyEl.setStyle("display", "block");
37675     this.bodyEl.setStyle("zoom", "1");
37676     //this.hideAction();
37677
37678     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37679     /** @private */
37680     this.el = Roo.get(els.el);
37681     this.inner = Roo.get(els.inner, true);
37682     this.textEl = Roo.get(this.el.dom.firstChild, true);
37683     this.pnode = Roo.get(els.el.parentNode, true);
37684 //    this.el.on("mousedown", this.onTabMouseDown, this);
37685     this.el.on("click", this.onTabClick, this);
37686     /** @private */
37687     if(config.closable){
37688         var c = Roo.get(els.close, true);
37689         c.dom.title = this.closeText;
37690         c.addClassOnOver("close-over");
37691         c.on("click", this.closeClick, this);
37692      }
37693
37694     this.addEvents({
37695          /**
37696          * @event activate
37697          * Fires when this tab becomes the active tab.
37698          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37699          * @param {Roo.TabPanelItem} this
37700          */
37701         "activate": true,
37702         /**
37703          * @event beforeclose
37704          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37705          * @param {Roo.TabPanelItem} this
37706          * @param {Object} e Set cancel to true on this object to cancel the close.
37707          */
37708         "beforeclose": true,
37709         /**
37710          * @event close
37711          * Fires when this tab is closed.
37712          * @param {Roo.TabPanelItem} this
37713          */
37714          "close": true,
37715         /**
37716          * @event deactivate
37717          * Fires when this tab is no longer the active tab.
37718          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37719          * @param {Roo.TabPanelItem} this
37720          */
37721          "deactivate" : true
37722     });
37723     this.hidden = false;
37724
37725     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37726 };
37727
37728 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37729            {
37730     purgeListeners : function(){
37731        Roo.util.Observable.prototype.purgeListeners.call(this);
37732        this.el.removeAllListeners();
37733     },
37734     /**
37735      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37736      */
37737     show : function(){
37738         this.pnode.addClass("active");
37739         this.showAction();
37740         if(Roo.isOpera){
37741             this.tabPanel.stripWrap.repaint();
37742         }
37743         this.fireEvent("activate", this.tabPanel, this);
37744     },
37745
37746     /**
37747      * Returns true if this tab is the active tab.
37748      * @return {Boolean}
37749      */
37750     isActive : function(){
37751         return this.tabPanel.getActiveTab() == this;
37752     },
37753
37754     /**
37755      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37756      */
37757     hide : function(){
37758         this.pnode.removeClass("active");
37759         this.hideAction();
37760         this.fireEvent("deactivate", this.tabPanel, this);
37761     },
37762
37763     hideAction : function(){
37764         this.bodyEl.hide();
37765         this.bodyEl.setStyle("position", "absolute");
37766         this.bodyEl.setLeft("-20000px");
37767         this.bodyEl.setTop("-20000px");
37768     },
37769
37770     showAction : function(){
37771         this.bodyEl.setStyle("position", "relative");
37772         this.bodyEl.setTop("");
37773         this.bodyEl.setLeft("");
37774         this.bodyEl.show();
37775     },
37776
37777     /**
37778      * Set the tooltip for the tab.
37779      * @param {String} tooltip The tab's tooltip
37780      */
37781     setTooltip : function(text){
37782         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37783             this.textEl.dom.qtip = text;
37784             this.textEl.dom.removeAttribute('title');
37785         }else{
37786             this.textEl.dom.title = text;
37787         }
37788     },
37789
37790     onTabClick : function(e){
37791         e.preventDefault();
37792         this.tabPanel.activate(this.id);
37793     },
37794
37795     onTabMouseDown : function(e){
37796         e.preventDefault();
37797         this.tabPanel.activate(this.id);
37798     },
37799 /*
37800     getWidth : function(){
37801         return this.inner.getWidth();
37802     },
37803
37804     setWidth : function(width){
37805         var iwidth = width - this.pnode.getPadding("lr");
37806         this.inner.setWidth(iwidth);
37807         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37808         this.pnode.setWidth(width);
37809     },
37810 */
37811     /**
37812      * Show or hide the tab
37813      * @param {Boolean} hidden True to hide or false to show.
37814      */
37815     setHidden : function(hidden){
37816         this.hidden = hidden;
37817         this.pnode.setStyle("display", hidden ? "none" : "");
37818     },
37819
37820     /**
37821      * Returns true if this tab is "hidden"
37822      * @return {Boolean}
37823      */
37824     isHidden : function(){
37825         return this.hidden;
37826     },
37827
37828     /**
37829      * Returns the text for this tab
37830      * @return {String}
37831      */
37832     getText : function(){
37833         return this.text;
37834     },
37835     /*
37836     autoSize : function(){
37837         //this.el.beginMeasure();
37838         this.textEl.setWidth(1);
37839         /*
37840          *  #2804 [new] Tabs in Roojs
37841          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37842          */
37843         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37844         //this.el.endMeasure();
37845     //},
37846
37847     /**
37848      * Sets the text for the tab (Note: this also sets the tooltip text)
37849      * @param {String} text The tab's text and tooltip
37850      */
37851     setText : function(text){
37852         this.text = text;
37853         this.textEl.update(text);
37854         this.setTooltip(text);
37855         //if(!this.tabPanel.resizeTabs){
37856         //    this.autoSize();
37857         //}
37858     },
37859     /**
37860      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37861      */
37862     activate : function(){
37863         this.tabPanel.activate(this.id);
37864     },
37865
37866     /**
37867      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37868      */
37869     disable : function(){
37870         if(this.tabPanel.active != this){
37871             this.disabled = true;
37872             this.pnode.addClass("disabled");
37873         }
37874     },
37875
37876     /**
37877      * Enables this TabPanelItem if it was previously disabled.
37878      */
37879     enable : function(){
37880         this.disabled = false;
37881         this.pnode.removeClass("disabled");
37882     },
37883
37884     /**
37885      * Sets the content for this TabPanelItem.
37886      * @param {String} content The content
37887      * @param {Boolean} loadScripts true to look for and load scripts
37888      */
37889     setContent : function(content, loadScripts){
37890         this.bodyEl.update(content, loadScripts);
37891     },
37892
37893     /**
37894      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37895      * @return {Roo.UpdateManager} The UpdateManager
37896      */
37897     getUpdateManager : function(){
37898         return this.bodyEl.getUpdateManager();
37899     },
37900
37901     /**
37902      * Set a URL to be used to load the content for this TabPanelItem.
37903      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37904      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
37905      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
37906      * @return {Roo.UpdateManager} The UpdateManager
37907      */
37908     setUrl : function(url, params, loadOnce){
37909         if(this.refreshDelegate){
37910             this.un('activate', this.refreshDelegate);
37911         }
37912         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37913         this.on("activate", this.refreshDelegate);
37914         return this.bodyEl.getUpdateManager();
37915     },
37916
37917     /** @private */
37918     _handleRefresh : function(url, params, loadOnce){
37919         if(!loadOnce || !this.loaded){
37920             var updater = this.bodyEl.getUpdateManager();
37921             updater.update(url, params, this._setLoaded.createDelegate(this));
37922         }
37923     },
37924
37925     /**
37926      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37927      *   Will fail silently if the setUrl method has not been called.
37928      *   This does not activate the panel, just updates its content.
37929      */
37930     refresh : function(){
37931         if(this.refreshDelegate){
37932            this.loaded = false;
37933            this.refreshDelegate();
37934         }
37935     },
37936
37937     /** @private */
37938     _setLoaded : function(){
37939         this.loaded = true;
37940     },
37941
37942     /** @private */
37943     closeClick : function(e){
37944         var o = {};
37945         e.stopEvent();
37946         this.fireEvent("beforeclose", this, o);
37947         if(o.cancel !== true){
37948             this.tabPanel.removeTab(this.id);
37949         }
37950     },
37951     /**
37952      * The text displayed in the tooltip for the close icon.
37953      * @type String
37954      */
37955     closeText : "Close this tab"
37956 });
37957 /**
37958 *    This script refer to:
37959 *    Title: International Telephone Input
37960 *    Author: Jack O'Connor
37961 *    Code version:  v12.1.12
37962 *    Availability: https://github.com/jackocnr/intl-tel-input.git
37963 **/
37964
37965 Roo.bootstrap.PhoneInputData = function() {
37966     var d = [
37967       [
37968         "Afghanistan (‫افغانستان‬‎)",
37969         "af",
37970         "93"
37971       ],
37972       [
37973         "Albania (Shqipëri)",
37974         "al",
37975         "355"
37976       ],
37977       [
37978         "Algeria (‫الجزائر‬‎)",
37979         "dz",
37980         "213"
37981       ],
37982       [
37983         "American Samoa",
37984         "as",
37985         "1684"
37986       ],
37987       [
37988         "Andorra",
37989         "ad",
37990         "376"
37991       ],
37992       [
37993         "Angola",
37994         "ao",
37995         "244"
37996       ],
37997       [
37998         "Anguilla",
37999         "ai",
38000         "1264"
38001       ],
38002       [
38003         "Antigua and Barbuda",
38004         "ag",
38005         "1268"
38006       ],
38007       [
38008         "Argentina",
38009         "ar",
38010         "54"
38011       ],
38012       [
38013         "Armenia (Հայաստան)",
38014         "am",
38015         "374"
38016       ],
38017       [
38018         "Aruba",
38019         "aw",
38020         "297"
38021       ],
38022       [
38023         "Australia",
38024         "au",
38025         "61",
38026         0
38027       ],
38028       [
38029         "Austria (Österreich)",
38030         "at",
38031         "43"
38032       ],
38033       [
38034         "Azerbaijan (Azərbaycan)",
38035         "az",
38036         "994"
38037       ],
38038       [
38039         "Bahamas",
38040         "bs",
38041         "1242"
38042       ],
38043       [
38044         "Bahrain (‫البحرين‬‎)",
38045         "bh",
38046         "973"
38047       ],
38048       [
38049         "Bangladesh (বাংলাদেশ)",
38050         "bd",
38051         "880"
38052       ],
38053       [
38054         "Barbados",
38055         "bb",
38056         "1246"
38057       ],
38058       [
38059         "Belarus (Беларусь)",
38060         "by",
38061         "375"
38062       ],
38063       [
38064         "Belgium (België)",
38065         "be",
38066         "32"
38067       ],
38068       [
38069         "Belize",
38070         "bz",
38071         "501"
38072       ],
38073       [
38074         "Benin (Bénin)",
38075         "bj",
38076         "229"
38077       ],
38078       [
38079         "Bermuda",
38080         "bm",
38081         "1441"
38082       ],
38083       [
38084         "Bhutan (འབྲུག)",
38085         "bt",
38086         "975"
38087       ],
38088       [
38089         "Bolivia",
38090         "bo",
38091         "591"
38092       ],
38093       [
38094         "Bosnia and Herzegovina (Босна и Херцеговина)",
38095         "ba",
38096         "387"
38097       ],
38098       [
38099         "Botswana",
38100         "bw",
38101         "267"
38102       ],
38103       [
38104         "Brazil (Brasil)",
38105         "br",
38106         "55"
38107       ],
38108       [
38109         "British Indian Ocean Territory",
38110         "io",
38111         "246"
38112       ],
38113       [
38114         "British Virgin Islands",
38115         "vg",
38116         "1284"
38117       ],
38118       [
38119         "Brunei",
38120         "bn",
38121         "673"
38122       ],
38123       [
38124         "Bulgaria (България)",
38125         "bg",
38126         "359"
38127       ],
38128       [
38129         "Burkina Faso",
38130         "bf",
38131         "226"
38132       ],
38133       [
38134         "Burundi (Uburundi)",
38135         "bi",
38136         "257"
38137       ],
38138       [
38139         "Cambodia (កម្ពុជា)",
38140         "kh",
38141         "855"
38142       ],
38143       [
38144         "Cameroon (Cameroun)",
38145         "cm",
38146         "237"
38147       ],
38148       [
38149         "Canada",
38150         "ca",
38151         "1",
38152         1,
38153         ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
38154       ],
38155       [
38156         "Cape Verde (Kabu Verdi)",
38157         "cv",
38158         "238"
38159       ],
38160       [
38161         "Caribbean Netherlands",
38162         "bq",
38163         "599",
38164         1
38165       ],
38166       [
38167         "Cayman Islands",
38168         "ky",
38169         "1345"
38170       ],
38171       [
38172         "Central African Republic (République centrafricaine)",
38173         "cf",
38174         "236"
38175       ],
38176       [
38177         "Chad (Tchad)",
38178         "td",
38179         "235"
38180       ],
38181       [
38182         "Chile",
38183         "cl",
38184         "56"
38185       ],
38186       [
38187         "China (中国)",
38188         "cn",
38189         "86"
38190       ],
38191       [
38192         "Christmas Island",
38193         "cx",
38194         "61",
38195         2
38196       ],
38197       [
38198         "Cocos (Keeling) Islands",
38199         "cc",
38200         "61",
38201         1
38202       ],
38203       [
38204         "Colombia",
38205         "co",
38206         "57"
38207       ],
38208       [
38209         "Comoros (‫جزر القمر‬‎)",
38210         "km",
38211         "269"
38212       ],
38213       [
38214         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38215         "cd",
38216         "243"
38217       ],
38218       [
38219         "Congo (Republic) (Congo-Brazzaville)",
38220         "cg",
38221         "242"
38222       ],
38223       [
38224         "Cook Islands",
38225         "ck",
38226         "682"
38227       ],
38228       [
38229         "Costa Rica",
38230         "cr",
38231         "506"
38232       ],
38233       [
38234         "Côte d’Ivoire",
38235         "ci",
38236         "225"
38237       ],
38238       [
38239         "Croatia (Hrvatska)",
38240         "hr",
38241         "385"
38242       ],
38243       [
38244         "Cuba",
38245         "cu",
38246         "53"
38247       ],
38248       [
38249         "Curaçao",
38250         "cw",
38251         "599",
38252         0
38253       ],
38254       [
38255         "Cyprus (Κύπρος)",
38256         "cy",
38257         "357"
38258       ],
38259       [
38260         "Czech Republic (Česká republika)",
38261         "cz",
38262         "420"
38263       ],
38264       [
38265         "Denmark (Danmark)",
38266         "dk",
38267         "45"
38268       ],
38269       [
38270         "Djibouti",
38271         "dj",
38272         "253"
38273       ],
38274       [
38275         "Dominica",
38276         "dm",
38277         "1767"
38278       ],
38279       [
38280         "Dominican Republic (República Dominicana)",
38281         "do",
38282         "1",
38283         2,
38284         ["809", "829", "849"]
38285       ],
38286       [
38287         "Ecuador",
38288         "ec",
38289         "593"
38290       ],
38291       [
38292         "Egypt (‫مصر‬‎)",
38293         "eg",
38294         "20"
38295       ],
38296       [
38297         "El Salvador",
38298         "sv",
38299         "503"
38300       ],
38301       [
38302         "Equatorial Guinea (Guinea Ecuatorial)",
38303         "gq",
38304         "240"
38305       ],
38306       [
38307         "Eritrea",
38308         "er",
38309         "291"
38310       ],
38311       [
38312         "Estonia (Eesti)",
38313         "ee",
38314         "372"
38315       ],
38316       [
38317         "Ethiopia",
38318         "et",
38319         "251"
38320       ],
38321       [
38322         "Falkland Islands (Islas Malvinas)",
38323         "fk",
38324         "500"
38325       ],
38326       [
38327         "Faroe Islands (Føroyar)",
38328         "fo",
38329         "298"
38330       ],
38331       [
38332         "Fiji",
38333         "fj",
38334         "679"
38335       ],
38336       [
38337         "Finland (Suomi)",
38338         "fi",
38339         "358",
38340         0
38341       ],
38342       [
38343         "France",
38344         "fr",
38345         "33"
38346       ],
38347       [
38348         "French Guiana (Guyane française)",
38349         "gf",
38350         "594"
38351       ],
38352       [
38353         "French Polynesia (Polynésie française)",
38354         "pf",
38355         "689"
38356       ],
38357       [
38358         "Gabon",
38359         "ga",
38360         "241"
38361       ],
38362       [
38363         "Gambia",
38364         "gm",
38365         "220"
38366       ],
38367       [
38368         "Georgia (საქართველო)",
38369         "ge",
38370         "995"
38371       ],
38372       [
38373         "Germany (Deutschland)",
38374         "de",
38375         "49"
38376       ],
38377       [
38378         "Ghana (Gaana)",
38379         "gh",
38380         "233"
38381       ],
38382       [
38383         "Gibraltar",
38384         "gi",
38385         "350"
38386       ],
38387       [
38388         "Greece (Ελλάδα)",
38389         "gr",
38390         "30"
38391       ],
38392       [
38393         "Greenland (Kalaallit Nunaat)",
38394         "gl",
38395         "299"
38396       ],
38397       [
38398         "Grenada",
38399         "gd",
38400         "1473"
38401       ],
38402       [
38403         "Guadeloupe",
38404         "gp",
38405         "590",
38406         0
38407       ],
38408       [
38409         "Guam",
38410         "gu",
38411         "1671"
38412       ],
38413       [
38414         "Guatemala",
38415         "gt",
38416         "502"
38417       ],
38418       [
38419         "Guernsey",
38420         "gg",
38421         "44",
38422         1
38423       ],
38424       [
38425         "Guinea (Guinée)",
38426         "gn",
38427         "224"
38428       ],
38429       [
38430         "Guinea-Bissau (Guiné Bissau)",
38431         "gw",
38432         "245"
38433       ],
38434       [
38435         "Guyana",
38436         "gy",
38437         "592"
38438       ],
38439       [
38440         "Haiti",
38441         "ht",
38442         "509"
38443       ],
38444       [
38445         "Honduras",
38446         "hn",
38447         "504"
38448       ],
38449       [
38450         "Hong Kong (香港)",
38451         "hk",
38452         "852"
38453       ],
38454       [
38455         "Hungary (Magyarország)",
38456         "hu",
38457         "36"
38458       ],
38459       [
38460         "Iceland (Ísland)",
38461         "is",
38462         "354"
38463       ],
38464       [
38465         "India (भारत)",
38466         "in",
38467         "91"
38468       ],
38469       [
38470         "Indonesia",
38471         "id",
38472         "62"
38473       ],
38474       [
38475         "Iran (‫ایران‬‎)",
38476         "ir",
38477         "98"
38478       ],
38479       [
38480         "Iraq (‫العراق‬‎)",
38481         "iq",
38482         "964"
38483       ],
38484       [
38485         "Ireland",
38486         "ie",
38487         "353"
38488       ],
38489       [
38490         "Isle of Man",
38491         "im",
38492         "44",
38493         2
38494       ],
38495       [
38496         "Israel (‫ישראל‬‎)",
38497         "il",
38498         "972"
38499       ],
38500       [
38501         "Italy (Italia)",
38502         "it",
38503         "39",
38504         0
38505       ],
38506       [
38507         "Jamaica",
38508         "jm",
38509         "1876"
38510       ],
38511       [
38512         "Japan (日本)",
38513         "jp",
38514         "81"
38515       ],
38516       [
38517         "Jersey",
38518         "je",
38519         "44",
38520         3
38521       ],
38522       [
38523         "Jordan (‫الأردن‬‎)",
38524         "jo",
38525         "962"
38526       ],
38527       [
38528         "Kazakhstan (Казахстан)",
38529         "kz",
38530         "7",
38531         1
38532       ],
38533       [
38534         "Kenya",
38535         "ke",
38536         "254"
38537       ],
38538       [
38539         "Kiribati",
38540         "ki",
38541         "686"
38542       ],
38543       [
38544         "Kosovo",
38545         "xk",
38546         "383"
38547       ],
38548       [
38549         "Kuwait (‫الكويت‬‎)",
38550         "kw",
38551         "965"
38552       ],
38553       [
38554         "Kyrgyzstan (Кыргызстан)",
38555         "kg",
38556         "996"
38557       ],
38558       [
38559         "Laos (ລາວ)",
38560         "la",
38561         "856"
38562       ],
38563       [
38564         "Latvia (Latvija)",
38565         "lv",
38566         "371"
38567       ],
38568       [
38569         "Lebanon (‫لبنان‬‎)",
38570         "lb",
38571         "961"
38572       ],
38573       [
38574         "Lesotho",
38575         "ls",
38576         "266"
38577       ],
38578       [
38579         "Liberia",
38580         "lr",
38581         "231"
38582       ],
38583       [
38584         "Libya (‫ليبيا‬‎)",
38585         "ly",
38586         "218"
38587       ],
38588       [
38589         "Liechtenstein",
38590         "li",
38591         "423"
38592       ],
38593       [
38594         "Lithuania (Lietuva)",
38595         "lt",
38596         "370"
38597       ],
38598       [
38599         "Luxembourg",
38600         "lu",
38601         "352"
38602       ],
38603       [
38604         "Macau (澳門)",
38605         "mo",
38606         "853"
38607       ],
38608       [
38609         "Macedonia (FYROM) (Македонија)",
38610         "mk",
38611         "389"
38612       ],
38613       [
38614         "Madagascar (Madagasikara)",
38615         "mg",
38616         "261"
38617       ],
38618       [
38619         "Malawi",
38620         "mw",
38621         "265"
38622       ],
38623       [
38624         "Malaysia",
38625         "my",
38626         "60"
38627       ],
38628       [
38629         "Maldives",
38630         "mv",
38631         "960"
38632       ],
38633       [
38634         "Mali",
38635         "ml",
38636         "223"
38637       ],
38638       [
38639         "Malta",
38640         "mt",
38641         "356"
38642       ],
38643       [
38644         "Marshall Islands",
38645         "mh",
38646         "692"
38647       ],
38648       [
38649         "Martinique",
38650         "mq",
38651         "596"
38652       ],
38653       [
38654         "Mauritania (‫موريتانيا‬‎)",
38655         "mr",
38656         "222"
38657       ],
38658       [
38659         "Mauritius (Moris)",
38660         "mu",
38661         "230"
38662       ],
38663       [
38664         "Mayotte",
38665         "yt",
38666         "262",
38667         1
38668       ],
38669       [
38670         "Mexico (México)",
38671         "mx",
38672         "52"
38673       ],
38674       [
38675         "Micronesia",
38676         "fm",
38677         "691"
38678       ],
38679       [
38680         "Moldova (Republica Moldova)",
38681         "md",
38682         "373"
38683       ],
38684       [
38685         "Monaco",
38686         "mc",
38687         "377"
38688       ],
38689       [
38690         "Mongolia (Монгол)",
38691         "mn",
38692         "976"
38693       ],
38694       [
38695         "Montenegro (Crna Gora)",
38696         "me",
38697         "382"
38698       ],
38699       [
38700         "Montserrat",
38701         "ms",
38702         "1664"
38703       ],
38704       [
38705         "Morocco (‫المغرب‬‎)",
38706         "ma",
38707         "212",
38708         0
38709       ],
38710       [
38711         "Mozambique (Moçambique)",
38712         "mz",
38713         "258"
38714       ],
38715       [
38716         "Myanmar (Burma) (မြန်မာ)",
38717         "mm",
38718         "95"
38719       ],
38720       [
38721         "Namibia (Namibië)",
38722         "na",
38723         "264"
38724       ],
38725       [
38726         "Nauru",
38727         "nr",
38728         "674"
38729       ],
38730       [
38731         "Nepal (नेपाल)",
38732         "np",
38733         "977"
38734       ],
38735       [
38736         "Netherlands (Nederland)",
38737         "nl",
38738         "31"
38739       ],
38740       [
38741         "New Caledonia (Nouvelle-Calédonie)",
38742         "nc",
38743         "687"
38744       ],
38745       [
38746         "New Zealand",
38747         "nz",
38748         "64"
38749       ],
38750       [
38751         "Nicaragua",
38752         "ni",
38753         "505"
38754       ],
38755       [
38756         "Niger (Nijar)",
38757         "ne",
38758         "227"
38759       ],
38760       [
38761         "Nigeria",
38762         "ng",
38763         "234"
38764       ],
38765       [
38766         "Niue",
38767         "nu",
38768         "683"
38769       ],
38770       [
38771         "Norfolk Island",
38772         "nf",
38773         "672"
38774       ],
38775       [
38776         "North Korea (조선 민주주의 인민 공화국)",
38777         "kp",
38778         "850"
38779       ],
38780       [
38781         "Northern Mariana Islands",
38782         "mp",
38783         "1670"
38784       ],
38785       [
38786         "Norway (Norge)",
38787         "no",
38788         "47",
38789         0
38790       ],
38791       [
38792         "Oman (‫عُمان‬‎)",
38793         "om",
38794         "968"
38795       ],
38796       [
38797         "Pakistan (‫پاکستان‬‎)",
38798         "pk",
38799         "92"
38800       ],
38801       [
38802         "Palau",
38803         "pw",
38804         "680"
38805       ],
38806       [
38807         "Palestine (‫فلسطين‬‎)",
38808         "ps",
38809         "970"
38810       ],
38811       [
38812         "Panama (Panamá)",
38813         "pa",
38814         "507"
38815       ],
38816       [
38817         "Papua New Guinea",
38818         "pg",
38819         "675"
38820       ],
38821       [
38822         "Paraguay",
38823         "py",
38824         "595"
38825       ],
38826       [
38827         "Peru (Perú)",
38828         "pe",
38829         "51"
38830       ],
38831       [
38832         "Philippines",
38833         "ph",
38834         "63"
38835       ],
38836       [
38837         "Poland (Polska)",
38838         "pl",
38839         "48"
38840       ],
38841       [
38842         "Portugal",
38843         "pt",
38844         "351"
38845       ],
38846       [
38847         "Puerto Rico",
38848         "pr",
38849         "1",
38850         3,
38851         ["787", "939"]
38852       ],
38853       [
38854         "Qatar (‫قطر‬‎)",
38855         "qa",
38856         "974"
38857       ],
38858       [
38859         "Réunion (La Réunion)",
38860         "re",
38861         "262",
38862         0
38863       ],
38864       [
38865         "Romania (România)",
38866         "ro",
38867         "40"
38868       ],
38869       [
38870         "Russia (Россия)",
38871         "ru",
38872         "7",
38873         0
38874       ],
38875       [
38876         "Rwanda",
38877         "rw",
38878         "250"
38879       ],
38880       [
38881         "Saint Barthélemy",
38882         "bl",
38883         "590",
38884         1
38885       ],
38886       [
38887         "Saint Helena",
38888         "sh",
38889         "290"
38890       ],
38891       [
38892         "Saint Kitts and Nevis",
38893         "kn",
38894         "1869"
38895       ],
38896       [
38897         "Saint Lucia",
38898         "lc",
38899         "1758"
38900       ],
38901       [
38902         "Saint Martin (Saint-Martin (partie française))",
38903         "mf",
38904         "590",
38905         2
38906       ],
38907       [
38908         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38909         "pm",
38910         "508"
38911       ],
38912       [
38913         "Saint Vincent and the Grenadines",
38914         "vc",
38915         "1784"
38916       ],
38917       [
38918         "Samoa",
38919         "ws",
38920         "685"
38921       ],
38922       [
38923         "San Marino",
38924         "sm",
38925         "378"
38926       ],
38927       [
38928         "São Tomé and Príncipe (São Tomé e Príncipe)",
38929         "st",
38930         "239"
38931       ],
38932       [
38933         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
38934         "sa",
38935         "966"
38936       ],
38937       [
38938         "Senegal (Sénégal)",
38939         "sn",
38940         "221"
38941       ],
38942       [
38943         "Serbia (Србија)",
38944         "rs",
38945         "381"
38946       ],
38947       [
38948         "Seychelles",
38949         "sc",
38950         "248"
38951       ],
38952       [
38953         "Sierra Leone",
38954         "sl",
38955         "232"
38956       ],
38957       [
38958         "Singapore",
38959         "sg",
38960         "65"
38961       ],
38962       [
38963         "Sint Maarten",
38964         "sx",
38965         "1721"
38966       ],
38967       [
38968         "Slovakia (Slovensko)",
38969         "sk",
38970         "421"
38971       ],
38972       [
38973         "Slovenia (Slovenija)",
38974         "si",
38975         "386"
38976       ],
38977       [
38978         "Solomon Islands",
38979         "sb",
38980         "677"
38981       ],
38982       [
38983         "Somalia (Soomaaliya)",
38984         "so",
38985         "252"
38986       ],
38987       [
38988         "South Africa",
38989         "za",
38990         "27"
38991       ],
38992       [
38993         "South Korea (대한민국)",
38994         "kr",
38995         "82"
38996       ],
38997       [
38998         "South Sudan (‫جنوب السودان‬‎)",
38999         "ss",
39000         "211"
39001       ],
39002       [
39003         "Spain (España)",
39004         "es",
39005         "34"
39006       ],
39007       [
39008         "Sri Lanka (ශ්‍රී ලංකාව)",
39009         "lk",
39010         "94"
39011       ],
39012       [
39013         "Sudan (‫السودان‬‎)",
39014         "sd",
39015         "249"
39016       ],
39017       [
39018         "Suriname",
39019         "sr",
39020         "597"
39021       ],
39022       [
39023         "Svalbard and Jan Mayen",
39024         "sj",
39025         "47",
39026         1
39027       ],
39028       [
39029         "Swaziland",
39030         "sz",
39031         "268"
39032       ],
39033       [
39034         "Sweden (Sverige)",
39035         "se",
39036         "46"
39037       ],
39038       [
39039         "Switzerland (Schweiz)",
39040         "ch",
39041         "41"
39042       ],
39043       [
39044         "Syria (‫سوريا‬‎)",
39045         "sy",
39046         "963"
39047       ],
39048       [
39049         "Taiwan (台灣)",
39050         "tw",
39051         "886"
39052       ],
39053       [
39054         "Tajikistan",
39055         "tj",
39056         "992"
39057       ],
39058       [
39059         "Tanzania",
39060         "tz",
39061         "255"
39062       ],
39063       [
39064         "Thailand (ไทย)",
39065         "th",
39066         "66"
39067       ],
39068       [
39069         "Timor-Leste",
39070         "tl",
39071         "670"
39072       ],
39073       [
39074         "Togo",
39075         "tg",
39076         "228"
39077       ],
39078       [
39079         "Tokelau",
39080         "tk",
39081         "690"
39082       ],
39083       [
39084         "Tonga",
39085         "to",
39086         "676"
39087       ],
39088       [
39089         "Trinidad and Tobago",
39090         "tt",
39091         "1868"
39092       ],
39093       [
39094         "Tunisia (‫تونس‬‎)",
39095         "tn",
39096         "216"
39097       ],
39098       [
39099         "Turkey (Türkiye)",
39100         "tr",
39101         "90"
39102       ],
39103       [
39104         "Turkmenistan",
39105         "tm",
39106         "993"
39107       ],
39108       [
39109         "Turks and Caicos Islands",
39110         "tc",
39111         "1649"
39112       ],
39113       [
39114         "Tuvalu",
39115         "tv",
39116         "688"
39117       ],
39118       [
39119         "U.S. Virgin Islands",
39120         "vi",
39121         "1340"
39122       ],
39123       [
39124         "Uganda",
39125         "ug",
39126         "256"
39127       ],
39128       [
39129         "Ukraine (Україна)",
39130         "ua",
39131         "380"
39132       ],
39133       [
39134         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39135         "ae",
39136         "971"
39137       ],
39138       [
39139         "United Kingdom",
39140         "gb",
39141         "44",
39142         0
39143       ],
39144       [
39145         "United States",
39146         "us",
39147         "1",
39148         0
39149       ],
39150       [
39151         "Uruguay",
39152         "uy",
39153         "598"
39154       ],
39155       [
39156         "Uzbekistan (Oʻzbekiston)",
39157         "uz",
39158         "998"
39159       ],
39160       [
39161         "Vanuatu",
39162         "vu",
39163         "678"
39164       ],
39165       [
39166         "Vatican City (Città del Vaticano)",
39167         "va",
39168         "39",
39169         1
39170       ],
39171       [
39172         "Venezuela",
39173         "ve",
39174         "58"
39175       ],
39176       [
39177         "Vietnam (Việt Nam)",
39178         "vn",
39179         "84"
39180       ],
39181       [
39182         "Wallis and Futuna (Wallis-et-Futuna)",
39183         "wf",
39184         "681"
39185       ],
39186       [
39187         "Western Sahara (‫الصحراء الغربية‬‎)",
39188         "eh",
39189         "212",
39190         1
39191       ],
39192       [
39193         "Yemen (‫اليمن‬‎)",
39194         "ye",
39195         "967"
39196       ],
39197       [
39198         "Zambia",
39199         "zm",
39200         "260"
39201       ],
39202       [
39203         "Zimbabwe",
39204         "zw",
39205         "263"
39206       ],
39207       [
39208         "Åland Islands",
39209         "ax",
39210         "358",
39211         1
39212       ]
39213   ];
39214   
39215   return d;
39216 }/**
39217 *    This script refer to:
39218 *    Title: International Telephone Input
39219 *    Author: Jack O'Connor
39220 *    Code version:  v12.1.12
39221 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39222 **/
39223
39224 /**
39225  * @class Roo.bootstrap.PhoneInput
39226  * @extends Roo.bootstrap.TriggerField
39227  * An input with International dial-code selection
39228  
39229  * @cfg {String} defaultDialCode default '+852'
39230  * @cfg {Array} preferedCountries default []
39231   
39232  * @constructor
39233  * Create a new PhoneInput.
39234  * @param {Object} config Configuration options
39235  */
39236
39237 Roo.bootstrap.PhoneInput = function(config) {
39238     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39239 };
39240
39241 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39242         
39243         listWidth: undefined,
39244         
39245         selectedClass: 'active',
39246         
39247         invalidClass : "has-warning",
39248         
39249         validClass: 'has-success',
39250         
39251         allowed: '0123456789',
39252         
39253         /**
39254          * @cfg {String} defaultDialCode The default dial code when initializing the input
39255          */
39256         defaultDialCode: '+852',
39257         
39258         /**
39259          * @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
39260          */
39261         preferedCountries: false,
39262         
39263         getAutoCreate : function()
39264         {
39265             var data = Roo.bootstrap.PhoneInputData();
39266             var align = this.labelAlign || this.parentLabelAlign();
39267             var id = Roo.id();
39268             
39269             this.allCountries = [];
39270             this.dialCodeMapping = [];
39271             
39272             for (var i = 0; i < data.length; i++) {
39273               var c = data[i];
39274               this.allCountries[i] = {
39275                 name: c[0],
39276                 iso2: c[1],
39277                 dialCode: c[2],
39278                 priority: c[3] || 0,
39279                 areaCodes: c[4] || null
39280               };
39281               this.dialCodeMapping[c[2]] = {
39282                   name: c[0],
39283                   iso2: c[1],
39284                   priority: c[3] || 0,
39285                   areaCodes: c[4] || null
39286               };
39287             }
39288             
39289             var cfg = {
39290                 cls: 'form-group',
39291                 cn: []
39292             };
39293             
39294             var input =  {
39295                 tag: 'input',
39296                 id : id,
39297                 cls : 'form-control tel-input',
39298                 autocomplete: 'new-password'
39299             };
39300             
39301             var hiddenInput = {
39302                 tag: 'input',
39303                 type: 'hidden',
39304                 cls: 'hidden-tel-input'
39305             };
39306             
39307             if (this.name) {
39308                 hiddenInput.name = this.name;
39309             }
39310             
39311             if (this.disabled) {
39312                 input.disabled = true;
39313             }
39314             
39315             var flag_container = {
39316                 tag: 'div',
39317                 cls: 'flag-box',
39318                 cn: [
39319                     {
39320                         tag: 'div',
39321                         cls: 'flag'
39322                     },
39323                     {
39324                         tag: 'div',
39325                         cls: 'caret'
39326                     }
39327                 ]
39328             };
39329             
39330             var box = {
39331                 tag: 'div',
39332                 cls: this.hasFeedback ? 'has-feedback' : '',
39333                 cn: [
39334                     hiddenInput,
39335                     input,
39336                     {
39337                         tag: 'input',
39338                         cls: 'dial-code-holder',
39339                         disabled: true
39340                     }
39341                 ]
39342             };
39343             
39344             var container = {
39345                 cls: 'roo-select2-container input-group',
39346                 cn: [
39347                     flag_container,
39348                     box
39349                 ]
39350             };
39351             
39352             if (this.fieldLabel.length) {
39353                 var indicator = {
39354                     tag: 'i',
39355                     tooltip: 'This field is required'
39356                 };
39357                 
39358                 var label = {
39359                     tag: 'label',
39360                     'for':  id,
39361                     cls: 'control-label',
39362                     cn: []
39363                 };
39364                 
39365                 var label_text = {
39366                     tag: 'span',
39367                     html: this.fieldLabel
39368                 };
39369                 
39370                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39371                 label.cn = [
39372                     indicator,
39373                     label_text
39374                 ];
39375                 
39376                 if(this.indicatorpos == 'right') {
39377                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39378                     label.cn = [
39379                         label_text,
39380                         indicator
39381                     ];
39382                 }
39383                 
39384                 if(align == 'left') {
39385                     container = {
39386                         tag: 'div',
39387                         cn: [
39388                             container
39389                         ]
39390                     };
39391                     
39392                     if(this.labelWidth > 12){
39393                         label.style = "width: " + this.labelWidth + 'px';
39394                     }
39395                     if(this.labelWidth < 13 && this.labelmd == 0){
39396                         this.labelmd = this.labelWidth;
39397                     }
39398                     if(this.labellg > 0){
39399                         label.cls += ' col-lg-' + this.labellg;
39400                         input.cls += ' col-lg-' + (12 - this.labellg);
39401                     }
39402                     if(this.labelmd > 0){
39403                         label.cls += ' col-md-' + this.labelmd;
39404                         container.cls += ' col-md-' + (12 - this.labelmd);
39405                     }
39406                     if(this.labelsm > 0){
39407                         label.cls += ' col-sm-' + this.labelsm;
39408                         container.cls += ' col-sm-' + (12 - this.labelsm);
39409                     }
39410                     if(this.labelxs > 0){
39411                         label.cls += ' col-xs-' + this.labelxs;
39412                         container.cls += ' col-xs-' + (12 - this.labelxs);
39413                     }
39414                 }
39415             }
39416             
39417             cfg.cn = [
39418                 label,
39419                 container
39420             ];
39421             
39422             var settings = this;
39423             
39424             ['xs','sm','md','lg'].map(function(size){
39425                 if (settings[size]) {
39426                     cfg.cls += ' col-' + size + '-' + settings[size];
39427                 }
39428             });
39429             
39430             this.store = new Roo.data.Store({
39431                 proxy : new Roo.data.MemoryProxy({}),
39432                 reader : new Roo.data.JsonReader({
39433                     fields : [
39434                         {
39435                             'name' : 'name',
39436                             'type' : 'string'
39437                         },
39438                         {
39439                             'name' : 'iso2',
39440                             'type' : 'string'
39441                         },
39442                         {
39443                             'name' : 'dialCode',
39444                             'type' : 'string'
39445                         },
39446                         {
39447                             'name' : 'priority',
39448                             'type' : 'string'
39449                         },
39450                         {
39451                             'name' : 'areaCodes',
39452                             'type' : 'string'
39453                         }
39454                     ]
39455                 })
39456             });
39457             
39458             if(!this.preferedCountries) {
39459                 this.preferedCountries = [
39460                     'hk',
39461                     'gb',
39462                     'us'
39463                 ];
39464             }
39465             
39466             var p = this.preferedCountries.reverse();
39467             
39468             if(p) {
39469                 for (var i = 0; i < p.length; i++) {
39470                     for (var j = 0; j < this.allCountries.length; j++) {
39471                         if(this.allCountries[j].iso2 == p[i]) {
39472                             var t = this.allCountries[j];
39473                             this.allCountries.splice(j,1);
39474                             this.allCountries.unshift(t);
39475                         }
39476                     } 
39477                 }
39478             }
39479             
39480             this.store.proxy.data = {
39481                 success: true,
39482                 data: this.allCountries
39483             };
39484             
39485             return cfg;
39486         },
39487         
39488         initEvents : function()
39489         {
39490             this.createList();
39491             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39492             
39493             this.indicator = this.indicatorEl();
39494             this.flag = this.flagEl();
39495             this.dialCodeHolder = this.dialCodeHolderEl();
39496             
39497             this.trigger = this.el.select('div.flag-box',true).first();
39498             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39499             
39500             var _this = this;
39501             
39502             (function(){
39503                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39504                 _this.list.setWidth(lw);
39505             }).defer(100);
39506             
39507             this.list.on('mouseover', this.onViewOver, this);
39508             this.list.on('mousemove', this.onViewMove, this);
39509             this.inputEl().on("keyup", this.onKeyUp, this);
39510             
39511             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39512
39513             this.view = new Roo.View(this.list, this.tpl, {
39514                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39515             });
39516             
39517             this.view.on('click', this.onViewClick, this);
39518             this.setValue(this.defaultDialCode);
39519         },
39520         
39521         onTriggerClick : function(e)
39522         {
39523             Roo.log('trigger click');
39524             if(this.disabled){
39525                 return;
39526             }
39527             
39528             if(this.isExpanded()){
39529                 this.collapse();
39530                 this.hasFocus = false;
39531             }else {
39532                 this.store.load({});
39533                 this.hasFocus = true;
39534                 this.expand();
39535             }
39536         },
39537         
39538         isExpanded : function()
39539         {
39540             return this.list.isVisible();
39541         },
39542         
39543         collapse : function()
39544         {
39545             if(!this.isExpanded()){
39546                 return;
39547             }
39548             this.list.hide();
39549             Roo.get(document).un('mousedown', this.collapseIf, this);
39550             Roo.get(document).un('mousewheel', this.collapseIf, this);
39551             this.fireEvent('collapse', this);
39552             this.validate();
39553         },
39554         
39555         expand : function()
39556         {
39557             Roo.log('expand');
39558
39559             if(this.isExpanded() || !this.hasFocus){
39560                 return;
39561             }
39562             
39563             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39564             this.list.setWidth(lw);
39565             
39566             this.list.show();
39567             this.restrictHeight();
39568             
39569             Roo.get(document).on('mousedown', this.collapseIf, this);
39570             Roo.get(document).on('mousewheel', this.collapseIf, this);
39571             
39572             this.fireEvent('expand', this);
39573         },
39574         
39575         restrictHeight : function()
39576         {
39577             this.list.alignTo(this.inputEl(), this.listAlign);
39578             this.list.alignTo(this.inputEl(), this.listAlign);
39579         },
39580         
39581         onViewOver : function(e, t)
39582         {
39583             if(this.inKeyMode){
39584                 return;
39585             }
39586             var item = this.view.findItemFromChild(t);
39587             
39588             if(item){
39589                 var index = this.view.indexOf(item);
39590                 this.select(index, false);
39591             }
39592         },
39593
39594         // private
39595         onViewClick : function(view, doFocus, el, e)
39596         {
39597             var index = this.view.getSelectedIndexes()[0];
39598             
39599             var r = this.store.getAt(index);
39600             
39601             if(r){
39602                 this.onSelect(r, index);
39603             }
39604             if(doFocus !== false && !this.blockFocus){
39605                 this.inputEl().focus();
39606             }
39607         },
39608         
39609         onViewMove : function(e, t)
39610         {
39611             this.inKeyMode = false;
39612         },
39613         
39614         select : function(index, scrollIntoView)
39615         {
39616             this.selectedIndex = index;
39617             this.view.select(index);
39618             if(scrollIntoView !== false){
39619                 var el = this.view.getNode(index);
39620                 if(el){
39621                     this.list.scrollChildIntoView(el, false);
39622                 }
39623             }
39624         },
39625         
39626         createList : function()
39627         {
39628             this.list = Roo.get(document.body).createChild({
39629                 tag: 'ul',
39630                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39631                 style: 'display:none'
39632             });
39633             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39634         },
39635         
39636         collapseIf : function(e)
39637         {
39638             var in_combo  = e.within(this.el);
39639             var in_list =  e.within(this.list);
39640             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39641             
39642             if (in_combo || in_list || is_list) {
39643                 return;
39644             }
39645             this.collapse();
39646         },
39647         
39648         onSelect : function(record, index)
39649         {
39650             if(this.fireEvent('beforeselect', this, record, index) !== false){
39651                 
39652                 this.setFlagClass(record.data.iso2);
39653                 this.setDialCode(record.data.dialCode);
39654                 this.hasFocus = false;
39655                 this.collapse();
39656                 this.fireEvent('select', this, record, index);
39657             }
39658         },
39659         
39660         flagEl : function()
39661         {
39662             var flag = this.el.select('div.flag',true).first();
39663             if(!flag){
39664                 return false;
39665             }
39666             return flag;
39667         },
39668         
39669         dialCodeHolderEl : function()
39670         {
39671             var d = this.el.select('input.dial-code-holder',true).first();
39672             if(!d){
39673                 return false;
39674             }
39675             return d;
39676         },
39677         
39678         setDialCode : function(v)
39679         {
39680             this.dialCodeHolder.dom.value = '+'+v;
39681         },
39682         
39683         setFlagClass : function(n)
39684         {
39685             this.flag.dom.className = 'flag '+n;
39686         },
39687         
39688         getValue : function()
39689         {
39690             var v = this.inputEl().getValue();
39691             if(this.dialCodeHolder) {
39692                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39693             }
39694             return v;
39695         },
39696         
39697         setValue : function(v)
39698         {
39699             var d = this.getDialCode(v);
39700             
39701             //invalid dial code
39702             if(v.length == 0 || !d || d.length == 0) {
39703                 if(this.rendered){
39704                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39705                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39706                 }
39707                 return;
39708             }
39709             
39710             //valid dial code
39711             this.setFlagClass(this.dialCodeMapping[d].iso2);
39712             this.setDialCode(d);
39713             this.inputEl().dom.value = v.replace('+'+d,'');
39714             this.hiddenEl().dom.value = this.getValue();
39715             
39716             this.validate();
39717         },
39718         
39719         getDialCode : function(v = '')
39720         {
39721             if (v.length == 0) {
39722                 return this.dialCodeHolder.dom.value;
39723             }
39724             
39725             var dialCode = "";
39726             if (v.charAt(0) != "+") {
39727                 return false;
39728             }
39729             var numericChars = "";
39730             for (var i = 1; i < v.length; i++) {
39731               var c = v.charAt(i);
39732               if (!isNaN(c)) {
39733                 numericChars += c;
39734                 if (this.dialCodeMapping[numericChars]) {
39735                   dialCode = v.substr(1, i);
39736                 }
39737                 if (numericChars.length == 4) {
39738                   break;
39739                 }
39740               }
39741             }
39742             return dialCode;
39743         },
39744         
39745         reset : function()
39746         {
39747             this.setValue(this.defaultDialCode);
39748             this.validate();
39749         },
39750         
39751         hiddenEl : function()
39752         {
39753             return this.el.select('input.hidden-tel-input',true).first();
39754         },
39755         
39756         onKeyUp : function(e){
39757             
39758             var k = e.getKey();
39759             var c = e.getCharCode();
39760             
39761             if(
39762                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39763                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39764             ){
39765                 e.stopEvent();
39766             }
39767             
39768             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39769             //     return;
39770             // }
39771             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39772                 e.stopEvent();
39773             }
39774             
39775             this.setValue(this.getValue());
39776         }
39777         
39778 });
39779 /**
39780  * @class Roo.bootstrap.MoneyField
39781  * @extends Roo.bootstrap.ComboBox
39782  * Bootstrap MoneyField class
39783  * 
39784  * @constructor
39785  * Create a new MoneyField.
39786  * @param {Object} config Configuration options
39787  */
39788
39789 Roo.bootstrap.MoneyField = function(config) {
39790     
39791     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39792     
39793 };
39794
39795 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39796     
39797     /**
39798      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39799      */
39800     allowDecimals : true,
39801     /**
39802      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39803      */
39804     decimalSeparator : ".",
39805     /**
39806      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39807      */
39808     decimalPrecision : 2,
39809     /**
39810      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39811      */
39812     allowNegative : true,
39813     /**
39814      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39815      */
39816     minValue : Number.NEGATIVE_INFINITY,
39817     /**
39818      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39819      */
39820     maxValue : Number.MAX_VALUE,
39821     /**
39822      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39823      */
39824     minText : "The minimum value for this field is {0}",
39825     /**
39826      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39827      */
39828     maxText : "The maximum value for this field is {0}",
39829     /**
39830      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39831      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39832      */
39833     nanText : "{0} is not a valid number",
39834     /**
39835      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39836      */
39837     castInt : true,
39838     
39839     inputlg : 9,
39840     inputmd : 9,
39841     inputsm : 9,
39842     inputxs : 6,
39843     
39844     store : false,
39845     
39846     getAutoCreate : function()
39847     {
39848         var align = this.labelAlign || this.parentLabelAlign();
39849         
39850         var id = Roo.id();
39851
39852         var cfg = {
39853             cls: 'form-group',
39854             cn: []
39855         };
39856
39857         var input =  {
39858             tag: 'input',
39859             id : id,
39860             cls : 'form-control roo-money-amount-input',
39861             autocomplete: 'new-password'
39862         };
39863         
39864         if (this.name) {
39865             input.name = this.name;
39866         }
39867
39868         if (this.disabled) {
39869             input.disabled = true;
39870         }
39871
39872         var clg = 12 - this.inputlg;
39873         var cmd = 12 - this.inputmd;
39874         var csm = 12 - this.inputsm;
39875         var cxs = 12 - this.inputxs;
39876         
39877         var container = {
39878             tag : 'div',
39879             cls : 'row roo-money-field',
39880             cn : [
39881                 {
39882                     tag : 'div',
39883                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
39884                     cn : [
39885                         {
39886                             tag : 'div',
39887                             cls: 'roo-select2-container input-group',
39888                             cn: [
39889                                 {
39890                                     tag : 'input',
39891                                     cls : 'form-control roo-money-currency-input',
39892                                     autocomplete: 'new-password'
39893                                 },
39894                                 {
39895                                     tag :'span',
39896                                     cls : 'input-group-addon',
39897                                     cn : [
39898                                         {
39899                                             tag: 'span',
39900                                             cls: 'caret'
39901                                         }
39902                                     ]
39903                                 }
39904                             ]
39905                         }
39906                     ]
39907                 },
39908                 {
39909                     tag : 'div',
39910                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
39911                     cn : [
39912                         {
39913                             tag: 'div',
39914                             cls: this.hasFeedback ? 'has-feedback' : '',
39915                             cn: [
39916                                 input
39917                             ]
39918                         }
39919                     ]
39920                 }
39921             ]
39922             
39923         };
39924         
39925         if (this.fieldLabel.length) {
39926             var indicator = {
39927                 tag: 'i',
39928                 tooltip: 'This field is required'
39929             };
39930
39931             var label = {
39932                 tag: 'label',
39933                 'for':  id,
39934                 cls: 'control-label',
39935                 cn: []
39936             };
39937
39938             var label_text = {
39939                 tag: 'span',
39940                 html: this.fieldLabel
39941             };
39942
39943             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39944             label.cn = [
39945                 indicator,
39946                 label_text
39947             ];
39948
39949             if(this.indicatorpos == 'right') {
39950                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39951                 label.cn = [
39952                     label_text,
39953                     indicator
39954                 ];
39955             }
39956
39957             if(align == 'left') {
39958                 container = {
39959                     tag: 'div',
39960                     cn: [
39961                         container
39962                     ]
39963                 };
39964
39965                 if(this.labelWidth > 12){
39966                     label.style = "width: " + this.labelWidth + 'px';
39967                 }
39968                 if(this.labelWidth < 13 && this.labelmd == 0){
39969                     this.labelmd = this.labelWidth;
39970                 }
39971                 if(this.labellg > 0){
39972                     label.cls += ' col-lg-' + this.labellg;
39973                     input.cls += ' col-lg-' + (12 - this.labellg);
39974                 }
39975                 if(this.labelmd > 0){
39976                     label.cls += ' col-md-' + this.labelmd;
39977                     container.cls += ' col-md-' + (12 - this.labelmd);
39978                 }
39979                 if(this.labelsm > 0){
39980                     label.cls += ' col-sm-' + this.labelsm;
39981                     container.cls += ' col-sm-' + (12 - this.labelsm);
39982                 }
39983                 if(this.labelxs > 0){
39984                     label.cls += ' col-xs-' + this.labelxs;
39985                     container.cls += ' col-xs-' + (12 - this.labelxs);
39986                 }
39987             }
39988         }
39989
39990         cfg.cn = [
39991             label,
39992             container
39993         ];
39994
39995         var settings = this;
39996
39997         ['xs','sm','md','lg'].map(function(size){
39998             if (settings[size]) {
39999                 cfg.cls += ' col-' + size + '-' + settings[size];
40000             }
40001         });
40002         
40003         return cfg;
40004         
40005     },
40006     
40007     initEvents : function()
40008     {
40009         this.indicator = this.indicatorEl();
40010         
40011         this.initCurrencyEvent();
40012         
40013         this.initNumberEvent();
40014         
40015     },
40016     
40017     initCurrencyEvent : function()
40018     {
40019         if (!this.store) {
40020             throw "can not find store for combo";
40021         }
40022         
40023         this.store = Roo.factory(this.store, Roo.data);
40024         this.store.parent = this;
40025         
40026         this.createList();
40027         
40028         this.triggerEl = this.el.select('.input-group-addon', true).first();
40029         
40030         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40031         
40032         var _this = this;
40033         
40034         (function(){
40035             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40036             _this.list.setWidth(lw);
40037         }).defer(100);
40038         
40039         this.list.on('mouseover', this.onViewOver, this);
40040         this.list.on('mousemove', this.onViewMove, this);
40041         this.list.on('scroll', this.onViewScroll, this);
40042         
40043         if(!this.tpl){
40044             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40045         }
40046         
40047         this.view = new Roo.View(this.list, this.tpl, {
40048             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40049         });
40050         
40051         this.view.on('click', this.onViewClick, this);
40052         
40053         this.store.on('beforeload', this.onBeforeLoad, this);
40054         this.store.on('load', this.onLoad, this);
40055         this.store.on('loadexception', this.onLoadException, this);
40056         
40057         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40058             "up" : function(e){
40059                 this.inKeyMode = true;
40060                 this.selectPrev();
40061             },
40062
40063             "down" : function(e){
40064                 if(!this.isExpanded()){
40065                     this.onTriggerClick();
40066                 }else{
40067                     this.inKeyMode = true;
40068                     this.selectNext();
40069                 }
40070             },
40071
40072             "enter" : function(e){
40073                 this.collapse();
40074                 
40075                 if(this.fireEvent("specialkey", this, e)){
40076                     this.onViewClick(false);
40077                 }
40078                 
40079                 return true;
40080             },
40081
40082             "esc" : function(e){
40083                 this.collapse();
40084             },
40085
40086             "tab" : function(e){
40087                 this.collapse();
40088                 
40089                 if(this.fireEvent("specialkey", this, e)){
40090                     this.onViewClick(false);
40091                 }
40092                 
40093                 return true;
40094             },
40095
40096             scope : this,
40097
40098             doRelay : function(foo, bar, hname){
40099                 if(hname == 'down' || this.scope.isExpanded()){
40100                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40101                 }
40102                 return true;
40103             },
40104
40105             forceKeyDown: true
40106         });
40107         
40108         this.queryDelay = Math.max(this.queryDelay || 10,
40109                 this.mode == 'local' ? 10 : 250);
40110         
40111         
40112         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
40113         
40114         if(this.typeAhead){
40115             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
40116         }
40117         
40118         this.currencyEl().on("keyup", this.onCurrencyKeyUp, this);
40119         
40120     },
40121     
40122     initNumberEvent : function(e)
40123     {
40124         this.inputEl().on("keydown" , this.fireKey,  this);
40125         this.inputEl().on("focus", this.onFocus,  this);
40126         this.inputEl().on("blur", this.onBlur,  this);
40127         
40128         this.inputEl().relayEvent('keyup', this);
40129         
40130         if(this.indicator){
40131             this.indicator.addClass('invisible');
40132         }
40133  
40134         this.originalValue = this.getValue();
40135         
40136         if(this.validationEvent == 'keyup'){
40137             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40138             this.inputEl().on('keyup', this.filterValidation, this);
40139         }
40140         else if(this.validationEvent !== false){
40141             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40142         }
40143         
40144         if(this.selectOnFocus){
40145             this.on("focus", this.preFocus, this);
40146             
40147         }
40148         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40149             this.inputEl().on("keypress", this.filterKeys, this);
40150         } else {
40151             this.inputEl().relayEvent('keypress', this);
40152         }
40153         
40154         var allowed = "0123456789";
40155         
40156         if(this.allowDecimals){
40157             allowed += this.decimalSeparator;
40158         }
40159         
40160         if(this.allowNegative){
40161             allowed += "-";
40162         }
40163         
40164         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40165         
40166         var keyPress = function(e){
40167             
40168             var k = e.getKey();
40169             
40170             var c = e.getCharCode();
40171             
40172             if(
40173                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40174                     allowed.indexOf(String.fromCharCode(c)) === -1
40175             ){
40176                 e.stopEvent();
40177                 return;
40178             }
40179             
40180             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40181                 return;
40182             }
40183             
40184             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40185                 e.stopEvent();
40186             }
40187         };
40188         
40189         this.inputEl().on("keypress", keyPress, this);
40190         
40191     },
40192     
40193     onTriggerClick : function(e)
40194     {   
40195         if(this.disabled){
40196             return;
40197         }
40198         
40199         this.page = 0;
40200         this.loadNext = false;
40201         
40202         if(this.isExpanded()){
40203             this.collapse();
40204             return;
40205         }
40206         
40207         this.hasFocus = true;
40208         
40209         if(this.triggerAction == 'all') {
40210             this.doQuery(this.allQuery, true);
40211             return;
40212         }
40213         
40214         this.doQuery(this.getRawValue());
40215     },
40216     
40217     getCurrency : function()
40218     {   
40219         var v = this.currencyEl().getValue();
40220         
40221         return v;
40222     },
40223     
40224     restrictHeight : function()
40225     {
40226         this.list.alignTo(this.currencyEl(), this.listAlign);
40227         this.list.alignTo(this.currencyEl(), this.listAlign);
40228     },
40229     
40230     onViewClick : function(view, doFocus, el, e)
40231     {
40232         var index = this.view.getSelectedIndexes()[0];
40233         
40234         var r = this.store.getAt(index);
40235         
40236         if(r){
40237             this.onSelect(r, index);
40238         }
40239     },
40240     
40241     onSelect : function(record, index){
40242         
40243         if(this.fireEvent('beforeselect', this, record, index) !== false){
40244         
40245             this.setFromCurrencyData(index > -1 ? record.data : false);
40246             
40247             this.collapse();
40248             
40249             this.fireEvent('select', this, record, index);
40250         }
40251     },
40252     
40253     setFromCurrencyData : function(o)
40254     {
40255         var currency = '';
40256         
40257         this.lastCurrency = o;
40258         
40259         if (this.currencyField) {
40260             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40261         } else {
40262             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40263         }
40264         
40265         this.lastSelectionText = currency;
40266         
40267         this.setCurrency(currency);
40268     },
40269     
40270     setFromData : function(o)
40271     {
40272         var c = {};
40273         
40274         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40275         
40276         this.setFromCurrencyData(c);
40277         
40278         var value = '';
40279         
40280         if (this.name) {
40281             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40282         } else {
40283             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40284         }
40285         
40286         this.setValue(value);
40287         
40288     },
40289     
40290     setCurrency : function(v)
40291     {   
40292         this.currencyValue = v;
40293         
40294         if(this.rendered){
40295             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40296             this.validate();
40297         }
40298     },
40299     
40300     setValue : function(v)
40301     {
40302         v = this.fixPrecision(v);
40303         
40304         v = String(v).replace(".", this.decimalSeparator);
40305         
40306         this.value = v;
40307         
40308         if(this.rendered){
40309             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40310             this.validate();
40311         }
40312     },
40313     
40314     getRawValue : function()
40315     {
40316         var v = this.inputEl().getValue();
40317         
40318         return v;
40319     },
40320     
40321     getValue : function()
40322     {
40323         return this.fixPrecision(this.parseValue(this.getRawValue()));
40324     },
40325     
40326     parseValue : function(value)
40327     {
40328         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40329         return isNaN(value) ? '' : value;
40330     },
40331     
40332     fixPrecision : function(value)
40333     {
40334         var nan = isNaN(value);
40335         
40336         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40337             return nan ? '' : value;
40338         }
40339         
40340         return parseFloat(value).toFixed(this.decimalPrecision);
40341     },
40342     
40343     decimalPrecisionFcn : function(v)
40344     {
40345         return Math.floor(v);
40346     },
40347     
40348     validateValue : function(value)
40349     {
40350         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40351             return false;
40352         }
40353         
40354         var num = this.parseValue(value);
40355         
40356         if(isNaN(num)){
40357             this.markInvalid(String.format(this.nanText, value));
40358             return false;
40359         }
40360         
40361         if(num < this.minValue){
40362             this.markInvalid(String.format(this.minText, this.minValue));
40363             return false;
40364         }
40365         
40366         if(num > this.maxValue){
40367             this.markInvalid(String.format(this.maxText, this.maxValue));
40368             return false;
40369         }
40370         
40371         return true;
40372     },
40373     
40374     validate : function()
40375     {
40376         if(this.disabled){
40377             this.markValid();
40378             return true;
40379         }
40380         
40381         var currency = this.getCurrency();
40382         
40383         if(this.validateValue(this.getRawValue()) && currency.length){
40384             this.markValid();
40385             return true;
40386         }
40387         
40388         this.markInvalid();
40389         return false;
40390     },
40391     
40392     getName: function()
40393     {
40394         return this.name;
40395     },
40396     
40397     beforeBlur : function()
40398     {
40399         if(!this.castInt){
40400             return;
40401         }
40402         
40403         var v = this.parseValue(this.getRawValue());
40404         
40405         if(v){
40406             this.setValue(v);
40407         }
40408     },
40409     
40410     onBlur : function()
40411     {
40412         this.beforeBlur();
40413         
40414         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40415             //this.el.removeClass(this.focusClass);
40416         }
40417         
40418         this.hasFocus = false;
40419         
40420         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40421             this.validate();
40422         }
40423         
40424         var v = this.getValue();
40425         
40426         if(String(v) !== String(this.startValue)){
40427             this.fireEvent('change', this, v, this.startValue);
40428         }
40429         
40430         this.fireEvent("blur", this);
40431     },
40432     
40433     onCurrencyKeyUp : function(e)
40434     {
40435         Roo.log('on currency keyup');
40436         if(!e.isSpecialKey()){
40437             this.lastKey = e.getKey();
40438             this.dqTask.delay(this.queryDelay);
40439         }
40440     },
40441     
40442     inputEl : function()
40443     {
40444         return this.el.select('.roo-money-amount-input', true).first();
40445     },
40446     
40447     currencyEl : function()
40448     {
40449         return this.el.select('.roo-money-currency-input', true).first();
40450     },
40451     
40452     initQuery : function()
40453     {
40454         var v = this.getCurrency();
40455         Roo.log('initQuery???');
40456         this.doQuery(v);
40457     },
40458     
40459     onTypeAhead : function()
40460     {
40461         if(this.store.getCount() > 0){
40462             var r = this.store.getAt(0);
40463             var newValue = r.data[this.currencyField];
40464             var len = newValue.length;
40465             var selStart = this.getCurrency().length;
40466             
40467             if(selStart != len){
40468                 this.setCurrency(newValue);
40469                 this.selectText(selStart, newValue.length);
40470             }
40471         }
40472     },
40473     
40474     selectText : function(start, end)
40475     {
40476         var v = this.getCurrency();
40477         
40478         if(v.length > 0){
40479             start = start === undefined ? 0 : start;
40480             end = end === undefined ? v.length : end;
40481             var d = this.el.dom;
40482             if(d.setSelectionRange){
40483                 d.setSelectionRange(start, end);
40484             }else if(d.createTextRange){
40485                 var range = d.createTextRange();
40486                 range.moveStart("character", start);
40487                 range.moveEnd("character", v.length-end);
40488                 range.select();
40489             }
40490         }
40491     }
40492     
40493 });