0f1d506a2b4b5b6e1d3434a9eec7ec1c54593986
[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                 }else{
14085                     
14086                     this.store.baseParams[this.queryParam] = q;
14087                     
14088                     var options = {params : this.getParams(q)};
14089                     
14090                     if(this.loadNext){
14091                         options.add = true;
14092                         options.params.start = this.page * this.pageSize;
14093                     }
14094                     
14095                     this.store.load(options);
14096                     
14097                     /*
14098                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14099                      *  we should expand the list on onLoad
14100                      *  so command out it
14101                      */
14102 //                    this.expand();
14103                 }
14104             }else{
14105                 this.selectedIndex = -1;
14106                 this.onLoad();   
14107             }
14108         }
14109         
14110         this.loadNext = false;
14111     },
14112     
14113     // private
14114     getParams : function(q){
14115         var p = {};
14116         //p[this.queryParam] = q;
14117         
14118         if(this.pageSize){
14119             p.start = 0;
14120             p.limit = this.pageSize;
14121         }
14122         return p;
14123     },
14124
14125     /**
14126      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14127      */
14128     collapse : function(){
14129         if(!this.isExpanded()){
14130             return;
14131         }
14132         
14133         this.list.hide();
14134         
14135         this.hasFocus = false;
14136         
14137         if(this.tickable){
14138             this.okBtn.hide();
14139             this.cancelBtn.hide();
14140             this.trigger.show();
14141             
14142             if(this.editable){
14143                 this.tickableInputEl().dom.value = '';
14144                 this.tickableInputEl().blur();
14145             }
14146             
14147         }
14148         
14149         Roo.get(document).un('mousedown', this.collapseIf, this);
14150         Roo.get(document).un('mousewheel', this.collapseIf, this);
14151         if (!this.editable) {
14152             Roo.get(document).un('keydown', this.listKeyPress, this);
14153         }
14154         this.fireEvent('collapse', this);
14155         
14156         this.validate();
14157     },
14158
14159     // private
14160     collapseIf : function(e){
14161         var in_combo  = e.within(this.el);
14162         var in_list =  e.within(this.list);
14163         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14164         
14165         if (in_combo || in_list || is_list) {
14166             //e.stopPropagation();
14167             return;
14168         }
14169         
14170         if(this.tickable){
14171             this.onTickableFooterButtonClick(e, false, false);
14172         }
14173
14174         this.collapse();
14175         
14176     },
14177
14178     /**
14179      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14180      */
14181     expand : function(){
14182        
14183         if(this.isExpanded() || !this.hasFocus){
14184             return;
14185         }
14186         
14187         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14188         this.list.setWidth(lw);
14189         
14190         Roo.log('expand');
14191         
14192         this.list.show();
14193         
14194         this.restrictHeight();
14195         
14196         if(this.tickable){
14197             
14198             this.tickItems = Roo.apply([], this.item);
14199             
14200             this.okBtn.show();
14201             this.cancelBtn.show();
14202             this.trigger.hide();
14203             
14204             if(this.editable){
14205                 this.tickableInputEl().focus();
14206             }
14207             
14208         }
14209         
14210         Roo.get(document).on('mousedown', this.collapseIf, this);
14211         Roo.get(document).on('mousewheel', this.collapseIf, this);
14212         if (!this.editable) {
14213             Roo.get(document).on('keydown', this.listKeyPress, this);
14214         }
14215         
14216         this.fireEvent('expand', this);
14217     },
14218
14219     // private
14220     // Implements the default empty TriggerField.onTriggerClick function
14221     onTriggerClick : function(e)
14222     {
14223         Roo.log('trigger click');
14224         
14225         if(this.disabled || !this.triggerList){
14226             return;
14227         }
14228         
14229         this.page = 0;
14230         this.loadNext = false;
14231         
14232         if(this.isExpanded()){
14233             this.collapse();
14234             if (!this.blockFocus) {
14235                 this.inputEl().focus();
14236             }
14237             
14238         }else {
14239             this.hasFocus = true;
14240             if(this.triggerAction == 'all') {
14241                 this.doQuery(this.allQuery, true);
14242             } else {
14243                 this.doQuery(this.getRawValue());
14244             }
14245             if (!this.blockFocus) {
14246                 this.inputEl().focus();
14247             }
14248         }
14249     },
14250     
14251     onTickableTriggerClick : function(e)
14252     {
14253         if(this.disabled){
14254             return;
14255         }
14256         
14257         this.page = 0;
14258         this.loadNext = false;
14259         this.hasFocus = true;
14260         
14261         if(this.triggerAction == 'all') {
14262             this.doQuery(this.allQuery, true);
14263         } else {
14264             this.doQuery(this.getRawValue());
14265         }
14266     },
14267     
14268     onSearchFieldClick : function(e)
14269     {
14270         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14271             this.onTickableFooterButtonClick(e, false, false);
14272             return;
14273         }
14274         
14275         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14276             return;
14277         }
14278         
14279         this.page = 0;
14280         this.loadNext = false;
14281         this.hasFocus = true;
14282         
14283         if(this.triggerAction == 'all') {
14284             this.doQuery(this.allQuery, true);
14285         } else {
14286             this.doQuery(this.getRawValue());
14287         }
14288     },
14289     
14290     listKeyPress : function(e)
14291     {
14292         //Roo.log('listkeypress');
14293         // scroll to first matching element based on key pres..
14294         if (e.isSpecialKey()) {
14295             return false;
14296         }
14297         var k = String.fromCharCode(e.getKey()).toUpperCase();
14298         //Roo.log(k);
14299         var match  = false;
14300         var csel = this.view.getSelectedNodes();
14301         var cselitem = false;
14302         if (csel.length) {
14303             var ix = this.view.indexOf(csel[0]);
14304             cselitem  = this.store.getAt(ix);
14305             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14306                 cselitem = false;
14307             }
14308             
14309         }
14310         
14311         this.store.each(function(v) { 
14312             if (cselitem) {
14313                 // start at existing selection.
14314                 if (cselitem.id == v.id) {
14315                     cselitem = false;
14316                 }
14317                 return true;
14318             }
14319                 
14320             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14321                 match = this.store.indexOf(v);
14322                 return false;
14323             }
14324             return true;
14325         }, this);
14326         
14327         if (match === false) {
14328             return true; // no more action?
14329         }
14330         // scroll to?
14331         this.view.select(match);
14332         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14333         sn.scrollIntoView(sn.dom.parentNode, false);
14334     },
14335     
14336     onViewScroll : function(e, t){
14337         
14338         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){
14339             return;
14340         }
14341         
14342         this.hasQuery = true;
14343         
14344         this.loading = this.list.select('.loading', true).first();
14345         
14346         if(this.loading === null){
14347             this.list.createChild({
14348                 tag: 'div',
14349                 cls: 'loading roo-select2-more-results roo-select2-active',
14350                 html: 'Loading more results...'
14351             });
14352             
14353             this.loading = this.list.select('.loading', true).first();
14354             
14355             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14356             
14357             this.loading.hide();
14358         }
14359         
14360         this.loading.show();
14361         
14362         var _combo = this;
14363         
14364         this.page++;
14365         this.loadNext = true;
14366         
14367         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14368         
14369         return;
14370     },
14371     
14372     addItem : function(o)
14373     {   
14374         var dv = ''; // display value
14375         
14376         if (this.displayField) {
14377             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14378         } else {
14379             // this is an error condition!!!
14380             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14381         }
14382         
14383         if(!dv.length){
14384             return;
14385         }
14386         
14387         var choice = this.choices.createChild({
14388             tag: 'li',
14389             cls: 'roo-select2-search-choice',
14390             cn: [
14391                 {
14392                     tag: 'div',
14393                     html: dv
14394                 },
14395                 {
14396                     tag: 'a',
14397                     href: '#',
14398                     cls: 'roo-select2-search-choice-close fa fa-times',
14399                     tabindex: '-1'
14400                 }
14401             ]
14402             
14403         }, this.searchField);
14404         
14405         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14406         
14407         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14408         
14409         this.item.push(o);
14410         
14411         this.lastData = o;
14412         
14413         this.syncValue();
14414         
14415         this.inputEl().dom.value = '';
14416         
14417         this.validate();
14418     },
14419     
14420     onRemoveItem : function(e, _self, o)
14421     {
14422         e.preventDefault();
14423         
14424         this.lastItem = Roo.apply([], this.item);
14425         
14426         var index = this.item.indexOf(o.data) * 1;
14427         
14428         if( index < 0){
14429             Roo.log('not this item?!');
14430             return;
14431         }
14432         
14433         this.item.splice(index, 1);
14434         o.item.remove();
14435         
14436         this.syncValue();
14437         
14438         this.fireEvent('remove', this, e);
14439         
14440         this.validate();
14441         
14442     },
14443     
14444     syncValue : function()
14445     {
14446         if(!this.item.length){
14447             this.clearValue();
14448             return;
14449         }
14450             
14451         var value = [];
14452         var _this = this;
14453         Roo.each(this.item, function(i){
14454             if(_this.valueField){
14455                 value.push(i[_this.valueField]);
14456                 return;
14457             }
14458
14459             value.push(i);
14460         });
14461
14462         this.value = value.join(',');
14463
14464         if(this.hiddenField){
14465             this.hiddenField.dom.value = this.value;
14466         }
14467         
14468         this.store.fireEvent("datachanged", this.store);
14469         
14470         this.validate();
14471     },
14472     
14473     clearItem : function()
14474     {
14475         if(!this.multiple){
14476             return;
14477         }
14478         
14479         this.item = [];
14480         
14481         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14482            c.remove();
14483         });
14484         
14485         this.syncValue();
14486         
14487         this.validate();
14488         
14489         if(this.tickable && !Roo.isTouch){
14490             this.view.refresh();
14491         }
14492     },
14493     
14494     inputEl: function ()
14495     {
14496         if(Roo.isIOS && this.useNativeIOS){
14497             return this.el.select('select.roo-ios-select', true).first();
14498         }
14499         
14500         if(Roo.isTouch && this.mobileTouchView){
14501             return this.el.select('input.form-control',true).first();
14502         }
14503         
14504         if(this.tickable){
14505             return this.searchField;
14506         }
14507         
14508         return this.el.select('input.form-control',true).first();
14509     },
14510     
14511     onTickableFooterButtonClick : function(e, btn, el)
14512     {
14513         e.preventDefault();
14514         
14515         this.lastItem = Roo.apply([], this.item);
14516         
14517         if(btn && btn.name == 'cancel'){
14518             this.tickItems = Roo.apply([], this.item);
14519             this.collapse();
14520             return;
14521         }
14522         
14523         this.clearItem();
14524         
14525         var _this = this;
14526         
14527         Roo.each(this.tickItems, function(o){
14528             _this.addItem(o);
14529         });
14530         
14531         this.collapse();
14532         
14533     },
14534     
14535     validate : function()
14536     {
14537         var v = this.getRawValue();
14538         
14539         if(this.multiple){
14540             v = this.getValue();
14541         }
14542         
14543         if(this.disabled || this.allowBlank || v.length){
14544             this.markValid();
14545             return true;
14546         }
14547         
14548         this.markInvalid();
14549         return false;
14550     },
14551     
14552     tickableInputEl : function()
14553     {
14554         if(!this.tickable || !this.editable){
14555             return this.inputEl();
14556         }
14557         
14558         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14559     },
14560     
14561     
14562     getAutoCreateTouchView : function()
14563     {
14564         var id = Roo.id();
14565         
14566         var cfg = {
14567             cls: 'form-group' //input-group
14568         };
14569         
14570         var input =  {
14571             tag: 'input',
14572             id : id,
14573             type : this.inputType,
14574             cls : 'form-control x-combo-noedit',
14575             autocomplete: 'new-password',
14576             placeholder : this.placeholder || '',
14577             readonly : true
14578         };
14579         
14580         if (this.name) {
14581             input.name = this.name;
14582         }
14583         
14584         if (this.size) {
14585             input.cls += ' input-' + this.size;
14586         }
14587         
14588         if (this.disabled) {
14589             input.disabled = true;
14590         }
14591         
14592         var inputblock = {
14593             cls : '',
14594             cn : [
14595                 input
14596             ]
14597         };
14598         
14599         if(this.before){
14600             inputblock.cls += ' input-group';
14601             
14602             inputblock.cn.unshift({
14603                 tag :'span',
14604                 cls : 'input-group-addon',
14605                 html : this.before
14606             });
14607         }
14608         
14609         if(this.removable && !this.multiple){
14610             inputblock.cls += ' roo-removable';
14611             
14612             inputblock.cn.push({
14613                 tag: 'button',
14614                 html : 'x',
14615                 cls : 'roo-combo-removable-btn close'
14616             });
14617         }
14618
14619         if(this.hasFeedback && !this.allowBlank){
14620             
14621             inputblock.cls += ' has-feedback';
14622             
14623             inputblock.cn.push({
14624                 tag: 'span',
14625                 cls: 'glyphicon form-control-feedback'
14626             });
14627             
14628         }
14629         
14630         if (this.after) {
14631             
14632             inputblock.cls += (this.before) ? '' : ' input-group';
14633             
14634             inputblock.cn.push({
14635                 tag :'span',
14636                 cls : 'input-group-addon',
14637                 html : this.after
14638             });
14639         }
14640
14641         var box = {
14642             tag: 'div',
14643             cn: [
14644                 {
14645                     tag: 'input',
14646                     type : 'hidden',
14647                     cls: 'form-hidden-field'
14648                 },
14649                 inputblock
14650             ]
14651             
14652         };
14653         
14654         if(this.multiple){
14655             box = {
14656                 tag: 'div',
14657                 cn: [
14658                     {
14659                         tag: 'input',
14660                         type : 'hidden',
14661                         cls: 'form-hidden-field'
14662                     },
14663                     {
14664                         tag: 'ul',
14665                         cls: 'roo-select2-choices',
14666                         cn:[
14667                             {
14668                                 tag: 'li',
14669                                 cls: 'roo-select2-search-field',
14670                                 cn: [
14671
14672                                     inputblock
14673                                 ]
14674                             }
14675                         ]
14676                     }
14677                 ]
14678             }
14679         };
14680         
14681         var combobox = {
14682             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14683             cn: [
14684                 box
14685             ]
14686         };
14687         
14688         if(!this.multiple && this.showToggleBtn){
14689             
14690             var caret = {
14691                         tag: 'span',
14692                         cls: 'caret'
14693             };
14694             
14695             if (this.caret != false) {
14696                 caret = {
14697                      tag: 'i',
14698                      cls: 'fa fa-' + this.caret
14699                 };
14700                 
14701             }
14702             
14703             combobox.cn.push({
14704                 tag :'span',
14705                 cls : 'input-group-addon btn dropdown-toggle',
14706                 cn : [
14707                     caret,
14708                     {
14709                         tag: 'span',
14710                         cls: 'combobox-clear',
14711                         cn  : [
14712                             {
14713                                 tag : 'i',
14714                                 cls: 'icon-remove'
14715                             }
14716                         ]
14717                     }
14718                 ]
14719
14720             })
14721         }
14722         
14723         if(this.multiple){
14724             combobox.cls += ' roo-select2-container-multi';
14725         }
14726         
14727         var align = this.labelAlign || this.parentLabelAlign();
14728         
14729         if (align ==='left' && this.fieldLabel.length) {
14730
14731             cfg.cn = [
14732                 {
14733                    tag : 'i',
14734                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14735                    tooltip : 'This field is required'
14736                 },
14737                 {
14738                     tag: 'label',
14739                     cls : 'control-label',
14740                     html : this.fieldLabel
14741
14742                 },
14743                 {
14744                     cls : '', 
14745                     cn: [
14746                         combobox
14747                     ]
14748                 }
14749             ];
14750             
14751             var labelCfg = cfg.cn[1];
14752             var contentCfg = cfg.cn[2];
14753             
14754
14755             if(this.indicatorpos == 'right'){
14756                 cfg.cn = [
14757                     {
14758                         tag: 'label',
14759                         'for' :  id,
14760                         cls : 'control-label',
14761                         cn : [
14762                             {
14763                                 tag : 'span',
14764                                 html : this.fieldLabel
14765                             },
14766                             {
14767                                 tag : 'i',
14768                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14769                                 tooltip : 'This field is required'
14770                             }
14771                         ]
14772                     },
14773                     {
14774                         cls : "",
14775                         cn: [
14776                             combobox
14777                         ]
14778                     }
14779
14780                 ];
14781                 
14782                 labelCfg = cfg.cn[0];
14783                 contentCfg = cfg.cn[1];
14784             }
14785             
14786            
14787             
14788             if(this.labelWidth > 12){
14789                 labelCfg.style = "width: " + this.labelWidth + 'px';
14790             }
14791             
14792             if(this.labelWidth < 13 && this.labelmd == 0){
14793                 this.labelmd = this.labelWidth;
14794             }
14795             
14796             if(this.labellg > 0){
14797                 labelCfg.cls += ' col-lg-' + this.labellg;
14798                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14799             }
14800             
14801             if(this.labelmd > 0){
14802                 labelCfg.cls += ' col-md-' + this.labelmd;
14803                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14804             }
14805             
14806             if(this.labelsm > 0){
14807                 labelCfg.cls += ' col-sm-' + this.labelsm;
14808                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14809             }
14810             
14811             if(this.labelxs > 0){
14812                 labelCfg.cls += ' col-xs-' + this.labelxs;
14813                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14814             }
14815                 
14816                 
14817         } else if ( this.fieldLabel.length) {
14818             cfg.cn = [
14819                 {
14820                    tag : 'i',
14821                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14822                    tooltip : 'This field is required'
14823                 },
14824                 {
14825                     tag: 'label',
14826                     cls : 'control-label',
14827                     html : this.fieldLabel
14828
14829                 },
14830                 {
14831                     cls : '', 
14832                     cn: [
14833                         combobox
14834                     ]
14835                 }
14836             ];
14837             
14838             if(this.indicatorpos == 'right'){
14839                 cfg.cn = [
14840                     {
14841                         tag: 'label',
14842                         cls : 'control-label',
14843                         html : this.fieldLabel,
14844                         cn : [
14845                             {
14846                                tag : 'i',
14847                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14848                                tooltip : 'This field is required'
14849                             }
14850                         ]
14851                     },
14852                     {
14853                         cls : '', 
14854                         cn: [
14855                             combobox
14856                         ]
14857                     }
14858                 ];
14859             }
14860         } else {
14861             cfg.cn = combobox;    
14862         }
14863         
14864         
14865         var settings = this;
14866         
14867         ['xs','sm','md','lg'].map(function(size){
14868             if (settings[size]) {
14869                 cfg.cls += ' col-' + size + '-' + settings[size];
14870             }
14871         });
14872         
14873         return cfg;
14874     },
14875     
14876     initTouchView : function()
14877     {
14878         this.renderTouchView();
14879         
14880         this.touchViewEl.on('scroll', function(){
14881             this.el.dom.scrollTop = 0;
14882         }, this);
14883         
14884         this.originalValue = this.getValue();
14885         
14886         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14887         
14888         this.inputEl().on("click", this.showTouchView, this);
14889         if (this.triggerEl) {
14890             this.triggerEl.on("click", this.showTouchView, this);
14891         }
14892         
14893         
14894         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14895         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14896         
14897         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14898         
14899         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14900         this.store.on('load', this.onTouchViewLoad, this);
14901         this.store.on('loadexception', this.onTouchViewLoadException, this);
14902         
14903         if(this.hiddenName){
14904             
14905             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14906             
14907             this.hiddenField.dom.value =
14908                 this.hiddenValue !== undefined ? this.hiddenValue :
14909                 this.value !== undefined ? this.value : '';
14910         
14911             this.el.dom.removeAttribute('name');
14912             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14913         }
14914         
14915         if(this.multiple){
14916             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14917             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14918         }
14919         
14920         if(this.removable && !this.multiple){
14921             var close = this.closeTriggerEl();
14922             if(close){
14923                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14924                 close.on('click', this.removeBtnClick, this, close);
14925             }
14926         }
14927         /*
14928          * fix the bug in Safari iOS8
14929          */
14930         this.inputEl().on("focus", function(e){
14931             document.activeElement.blur();
14932         }, this);
14933         
14934         return;
14935         
14936         
14937     },
14938     
14939     renderTouchView : function()
14940     {
14941         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14942         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14943         
14944         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14945         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14946         
14947         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14948         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14949         this.touchViewBodyEl.setStyle('overflow', 'auto');
14950         
14951         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14952         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14953         
14954         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14955         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14956         
14957     },
14958     
14959     showTouchView : function()
14960     {
14961         if(this.disabled){
14962             return;
14963         }
14964         
14965         this.touchViewHeaderEl.hide();
14966
14967         if(this.modalTitle.length){
14968             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14969             this.touchViewHeaderEl.show();
14970         }
14971
14972         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14973         this.touchViewEl.show();
14974
14975         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
14976         
14977         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14978         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14979
14980         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14981
14982         if(this.modalTitle.length){
14983             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14984         }
14985         
14986         this.touchViewBodyEl.setHeight(bodyHeight);
14987
14988         if(this.animate){
14989             var _this = this;
14990             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14991         }else{
14992             this.touchViewEl.addClass('in');
14993         }
14994
14995         this.doTouchViewQuery();
14996         
14997     },
14998     
14999     hideTouchView : function()
15000     {
15001         this.touchViewEl.removeClass('in');
15002
15003         if(this.animate){
15004             var _this = this;
15005             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15006         }else{
15007             this.touchViewEl.setStyle('display', 'none');
15008         }
15009         
15010     },
15011     
15012     setTouchViewValue : function()
15013     {
15014         if(this.multiple){
15015             this.clearItem();
15016         
15017             var _this = this;
15018
15019             Roo.each(this.tickItems, function(o){
15020                 this.addItem(o);
15021             }, this);
15022         }
15023         
15024         this.hideTouchView();
15025     },
15026     
15027     doTouchViewQuery : function()
15028     {
15029         var qe = {
15030             query: '',
15031             forceAll: true,
15032             combo: this,
15033             cancel:false
15034         };
15035         
15036         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15037             return false;
15038         }
15039         
15040         if(!this.alwaysQuery || this.mode == 'local'){
15041             this.onTouchViewLoad();
15042             return;
15043         }
15044         
15045         this.store.load();
15046     },
15047     
15048     onTouchViewBeforeLoad : function(combo,opts)
15049     {
15050         return;
15051     },
15052
15053     // private
15054     onTouchViewLoad : function()
15055     {
15056         if(this.store.getCount() < 1){
15057             this.onTouchViewEmptyResults();
15058             return;
15059         }
15060         
15061         this.clearTouchView();
15062         
15063         var rawValue = this.getRawValue();
15064         
15065         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15066         
15067         this.tickItems = [];
15068         
15069         this.store.data.each(function(d, rowIndex){
15070             var row = this.touchViewListGroup.createChild(template);
15071             
15072             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15073                 row.addClass(d.data.cls);
15074             }
15075             
15076             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15077                 var cfg = {
15078                     data : d.data,
15079                     html : d.data[this.displayField]
15080                 };
15081                 
15082                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15083                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15084                 }
15085             }
15086             row.removeClass('selected');
15087             if(!this.multiple && this.valueField &&
15088                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15089             {
15090                 // radio buttons..
15091                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15092                 row.addClass('selected');
15093             }
15094             
15095             if(this.multiple && this.valueField &&
15096                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15097             {
15098                 
15099                 // checkboxes...
15100                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15101                 this.tickItems.push(d.data);
15102             }
15103             
15104             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15105             
15106         }, this);
15107         
15108         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15109         
15110         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15111
15112         if(this.modalTitle.length){
15113             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15114         }
15115
15116         var listHeight = this.touchViewListGroup.getHeight();
15117         
15118         var _this = this;
15119         
15120         if(firstChecked && listHeight > bodyHeight){
15121             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15122         }
15123         
15124     },
15125     
15126     onTouchViewLoadException : function()
15127     {
15128         this.hideTouchView();
15129     },
15130     
15131     onTouchViewEmptyResults : function()
15132     {
15133         this.clearTouchView();
15134         
15135         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15136         
15137         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15138         
15139     },
15140     
15141     clearTouchView : function()
15142     {
15143         this.touchViewListGroup.dom.innerHTML = '';
15144     },
15145     
15146     onTouchViewClick : function(e, el, o)
15147     {
15148         e.preventDefault();
15149         
15150         var row = o.row;
15151         var rowIndex = o.rowIndex;
15152         
15153         var r = this.store.getAt(rowIndex);
15154         
15155         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15156             
15157             if(!this.multiple){
15158                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15159                     c.dom.removeAttribute('checked');
15160                 }, this);
15161
15162                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15163
15164                 this.setFromData(r.data);
15165
15166                 var close = this.closeTriggerEl();
15167
15168                 if(close){
15169                     close.show();
15170                 }
15171
15172                 this.hideTouchView();
15173
15174                 this.fireEvent('select', this, r, rowIndex);
15175
15176                 return;
15177             }
15178
15179             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15180                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15181                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15182                 return;
15183             }
15184
15185             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15186             this.addItem(r.data);
15187             this.tickItems.push(r.data);
15188         }
15189     },
15190     
15191     getAutoCreateNativeIOS : function()
15192     {
15193         var cfg = {
15194             cls: 'form-group' //input-group,
15195         };
15196         
15197         var combobox =  {
15198             tag: 'select',
15199             cls : 'roo-ios-select'
15200         };
15201         
15202         if (this.name) {
15203             combobox.name = this.name;
15204         }
15205         
15206         if (this.disabled) {
15207             combobox.disabled = true;
15208         }
15209         
15210         var settings = this;
15211         
15212         ['xs','sm','md','lg'].map(function(size){
15213             if (settings[size]) {
15214                 cfg.cls += ' col-' + size + '-' + settings[size];
15215             }
15216         });
15217         
15218         cfg.cn = combobox;
15219         
15220         return cfg;
15221         
15222     },
15223     
15224     initIOSView : function()
15225     {
15226         this.store.on('load', this.onIOSViewLoad, this);
15227         
15228         return;
15229     },
15230     
15231     onIOSViewLoad : function()
15232     {
15233         if(this.store.getCount() < 1){
15234             return;
15235         }
15236         
15237         this.clearIOSView();
15238         
15239         if(this.allowBlank) {
15240             
15241             var default_text = '-- SELECT --';
15242             
15243             if(this.placeholder.length){
15244                 default_text = this.placeholder;
15245             }
15246             
15247             if(this.emptyTitle.length){
15248                 default_text += ' - ' + this.emptyTitle + ' -';
15249             }
15250             
15251             var opt = this.inputEl().createChild({
15252                 tag: 'option',
15253                 value : 0,
15254                 html : default_text
15255             });
15256             
15257             var o = {};
15258             o[this.valueField] = 0;
15259             o[this.displayField] = default_text;
15260             
15261             this.ios_options.push({
15262                 data : o,
15263                 el : opt
15264             });
15265             
15266         }
15267         
15268         this.store.data.each(function(d, rowIndex){
15269             
15270             var html = '';
15271             
15272             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15273                 html = d.data[this.displayField];
15274             }
15275             
15276             var value = '';
15277             
15278             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15279                 value = d.data[this.valueField];
15280             }
15281             
15282             var option = {
15283                 tag: 'option',
15284                 value : value,
15285                 html : html
15286             };
15287             
15288             if(this.value == d.data[this.valueField]){
15289                 option['selected'] = true;
15290             }
15291             
15292             var opt = this.inputEl().createChild(option);
15293             
15294             this.ios_options.push({
15295                 data : d.data,
15296                 el : opt
15297             });
15298             
15299         }, this);
15300         
15301         this.inputEl().on('change', function(){
15302            this.fireEvent('select', this);
15303         }, this);
15304         
15305     },
15306     
15307     clearIOSView: function()
15308     {
15309         this.inputEl().dom.innerHTML = '';
15310         
15311         this.ios_options = [];
15312     },
15313     
15314     setIOSValue: function(v)
15315     {
15316         this.value = v;
15317         
15318         if(!this.ios_options){
15319             return;
15320         }
15321         
15322         Roo.each(this.ios_options, function(opts){
15323            
15324            opts.el.dom.removeAttribute('selected');
15325            
15326            if(opts.data[this.valueField] != v){
15327                return;
15328            }
15329            
15330            opts.el.dom.setAttribute('selected', true);
15331            
15332         }, this);
15333     }
15334
15335     /** 
15336     * @cfg {Boolean} grow 
15337     * @hide 
15338     */
15339     /** 
15340     * @cfg {Number} growMin 
15341     * @hide 
15342     */
15343     /** 
15344     * @cfg {Number} growMax 
15345     * @hide 
15346     */
15347     /**
15348      * @hide
15349      * @method autoSize
15350      */
15351 });
15352
15353 Roo.apply(Roo.bootstrap.ComboBox,  {
15354     
15355     header : {
15356         tag: 'div',
15357         cls: 'modal-header',
15358         cn: [
15359             {
15360                 tag: 'h4',
15361                 cls: 'modal-title'
15362             }
15363         ]
15364     },
15365     
15366     body : {
15367         tag: 'div',
15368         cls: 'modal-body',
15369         cn: [
15370             {
15371                 tag: 'ul',
15372                 cls: 'list-group'
15373             }
15374         ]
15375     },
15376     
15377     listItemRadio : {
15378         tag: 'li',
15379         cls: 'list-group-item',
15380         cn: [
15381             {
15382                 tag: 'span',
15383                 cls: 'roo-combobox-list-group-item-value'
15384             },
15385             {
15386                 tag: 'div',
15387                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15388                 cn: [
15389                     {
15390                         tag: 'input',
15391                         type: 'radio'
15392                     },
15393                     {
15394                         tag: 'label'
15395                     }
15396                 ]
15397             }
15398         ]
15399     },
15400     
15401     listItemCheckbox : {
15402         tag: 'li',
15403         cls: 'list-group-item',
15404         cn: [
15405             {
15406                 tag: 'span',
15407                 cls: 'roo-combobox-list-group-item-value'
15408             },
15409             {
15410                 tag: 'div',
15411                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15412                 cn: [
15413                     {
15414                         tag: 'input',
15415                         type: 'checkbox'
15416                     },
15417                     {
15418                         tag: 'label'
15419                     }
15420                 ]
15421             }
15422         ]
15423     },
15424     
15425     emptyResult : {
15426         tag: 'div',
15427         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15428     },
15429     
15430     footer : {
15431         tag: 'div',
15432         cls: 'modal-footer',
15433         cn: [
15434             {
15435                 tag: 'div',
15436                 cls: 'row',
15437                 cn: [
15438                     {
15439                         tag: 'div',
15440                         cls: 'col-xs-6 text-left',
15441                         cn: {
15442                             tag: 'button',
15443                             cls: 'btn btn-danger roo-touch-view-cancel',
15444                             html: 'Cancel'
15445                         }
15446                     },
15447                     {
15448                         tag: 'div',
15449                         cls: 'col-xs-6 text-right',
15450                         cn: {
15451                             tag: 'button',
15452                             cls: 'btn btn-success roo-touch-view-ok',
15453                             html: 'OK'
15454                         }
15455                     }
15456                 ]
15457             }
15458         ]
15459         
15460     }
15461 });
15462
15463 Roo.apply(Roo.bootstrap.ComboBox,  {
15464     
15465     touchViewTemplate : {
15466         tag: 'div',
15467         cls: 'modal fade roo-combobox-touch-view',
15468         cn: [
15469             {
15470                 tag: 'div',
15471                 cls: 'modal-dialog',
15472                 style : 'position:fixed', // we have to fix position....
15473                 cn: [
15474                     {
15475                         tag: 'div',
15476                         cls: 'modal-content',
15477                         cn: [
15478                             Roo.bootstrap.ComboBox.header,
15479                             Roo.bootstrap.ComboBox.body,
15480                             Roo.bootstrap.ComboBox.footer
15481                         ]
15482                     }
15483                 ]
15484             }
15485         ]
15486     }
15487 });/*
15488  * Based on:
15489  * Ext JS Library 1.1.1
15490  * Copyright(c) 2006-2007, Ext JS, LLC.
15491  *
15492  * Originally Released Under LGPL - original licence link has changed is not relivant.
15493  *
15494  * Fork - LGPL
15495  * <script type="text/javascript">
15496  */
15497
15498 /**
15499  * @class Roo.View
15500  * @extends Roo.util.Observable
15501  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15502  * This class also supports single and multi selection modes. <br>
15503  * Create a data model bound view:
15504  <pre><code>
15505  var store = new Roo.data.Store(...);
15506
15507  var view = new Roo.View({
15508     el : "my-element",
15509     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15510  
15511     singleSelect: true,
15512     selectedClass: "ydataview-selected",
15513     store: store
15514  });
15515
15516  // listen for node click?
15517  view.on("click", function(vw, index, node, e){
15518  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15519  });
15520
15521  // load XML data
15522  dataModel.load("foobar.xml");
15523  </code></pre>
15524  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15525  * <br><br>
15526  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15527  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15528  * 
15529  * Note: old style constructor is still suported (container, template, config)
15530  * 
15531  * @constructor
15532  * Create a new View
15533  * @param {Object} config The config object
15534  * 
15535  */
15536 Roo.View = function(config, depreciated_tpl, depreciated_config){
15537     
15538     this.parent = false;
15539     
15540     if (typeof(depreciated_tpl) == 'undefined') {
15541         // new way.. - universal constructor.
15542         Roo.apply(this, config);
15543         this.el  = Roo.get(this.el);
15544     } else {
15545         // old format..
15546         this.el  = Roo.get(config);
15547         this.tpl = depreciated_tpl;
15548         Roo.apply(this, depreciated_config);
15549     }
15550     this.wrapEl  = this.el.wrap().wrap();
15551     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15552     
15553     
15554     if(typeof(this.tpl) == "string"){
15555         this.tpl = new Roo.Template(this.tpl);
15556     } else {
15557         // support xtype ctors..
15558         this.tpl = new Roo.factory(this.tpl, Roo);
15559     }
15560     
15561     
15562     this.tpl.compile();
15563     
15564     /** @private */
15565     this.addEvents({
15566         /**
15567          * @event beforeclick
15568          * Fires before a click is processed. Returns false to cancel the default action.
15569          * @param {Roo.View} this
15570          * @param {Number} index The index of the target node
15571          * @param {HTMLElement} node The target node
15572          * @param {Roo.EventObject} e The raw event object
15573          */
15574             "beforeclick" : true,
15575         /**
15576          * @event click
15577          * Fires when a template node is clicked.
15578          * @param {Roo.View} this
15579          * @param {Number} index The index of the target node
15580          * @param {HTMLElement} node The target node
15581          * @param {Roo.EventObject} e The raw event object
15582          */
15583             "click" : true,
15584         /**
15585          * @event dblclick
15586          * Fires when a template node is double clicked.
15587          * @param {Roo.View} this
15588          * @param {Number} index The index of the target node
15589          * @param {HTMLElement} node The target node
15590          * @param {Roo.EventObject} e The raw event object
15591          */
15592             "dblclick" : true,
15593         /**
15594          * @event contextmenu
15595          * Fires when a template node is right clicked.
15596          * @param {Roo.View} this
15597          * @param {Number} index The index of the target node
15598          * @param {HTMLElement} node The target node
15599          * @param {Roo.EventObject} e The raw event object
15600          */
15601             "contextmenu" : true,
15602         /**
15603          * @event selectionchange
15604          * Fires when the selected nodes change.
15605          * @param {Roo.View} this
15606          * @param {Array} selections Array of the selected nodes
15607          */
15608             "selectionchange" : true,
15609     
15610         /**
15611          * @event beforeselect
15612          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15613          * @param {Roo.View} this
15614          * @param {HTMLElement} node The node to be selected
15615          * @param {Array} selections Array of currently selected nodes
15616          */
15617             "beforeselect" : true,
15618         /**
15619          * @event preparedata
15620          * Fires on every row to render, to allow you to change the data.
15621          * @param {Roo.View} this
15622          * @param {Object} data to be rendered (change this)
15623          */
15624           "preparedata" : true
15625           
15626           
15627         });
15628
15629
15630
15631     this.el.on({
15632         "click": this.onClick,
15633         "dblclick": this.onDblClick,
15634         "contextmenu": this.onContextMenu,
15635         scope:this
15636     });
15637
15638     this.selections = [];
15639     this.nodes = [];
15640     this.cmp = new Roo.CompositeElementLite([]);
15641     if(this.store){
15642         this.store = Roo.factory(this.store, Roo.data);
15643         this.setStore(this.store, true);
15644     }
15645     
15646     if ( this.footer && this.footer.xtype) {
15647            
15648          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15649         
15650         this.footer.dataSource = this.store;
15651         this.footer.container = fctr;
15652         this.footer = Roo.factory(this.footer, Roo);
15653         fctr.insertFirst(this.el);
15654         
15655         // this is a bit insane - as the paging toolbar seems to detach the el..
15656 //        dom.parentNode.parentNode.parentNode
15657          // they get detached?
15658     }
15659     
15660     
15661     Roo.View.superclass.constructor.call(this);
15662     
15663     
15664 };
15665
15666 Roo.extend(Roo.View, Roo.util.Observable, {
15667     
15668      /**
15669      * @cfg {Roo.data.Store} store Data store to load data from.
15670      */
15671     store : false,
15672     
15673     /**
15674      * @cfg {String|Roo.Element} el The container element.
15675      */
15676     el : '',
15677     
15678     /**
15679      * @cfg {String|Roo.Template} tpl The template used by this View 
15680      */
15681     tpl : false,
15682     /**
15683      * @cfg {String} dataName the named area of the template to use as the data area
15684      *                          Works with domtemplates roo-name="name"
15685      */
15686     dataName: false,
15687     /**
15688      * @cfg {String} selectedClass The css class to add to selected nodes
15689      */
15690     selectedClass : "x-view-selected",
15691      /**
15692      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15693      */
15694     emptyText : "",
15695     
15696     /**
15697      * @cfg {String} text to display on mask (default Loading)
15698      */
15699     mask : false,
15700     /**
15701      * @cfg {Boolean} multiSelect Allow multiple selection
15702      */
15703     multiSelect : false,
15704     /**
15705      * @cfg {Boolean} singleSelect Allow single selection
15706      */
15707     singleSelect:  false,
15708     
15709     /**
15710      * @cfg {Boolean} toggleSelect - selecting 
15711      */
15712     toggleSelect : false,
15713     
15714     /**
15715      * @cfg {Boolean} tickable - selecting 
15716      */
15717     tickable : false,
15718     
15719     /**
15720      * Returns the element this view is bound to.
15721      * @return {Roo.Element}
15722      */
15723     getEl : function(){
15724         return this.wrapEl;
15725     },
15726     
15727     
15728
15729     /**
15730      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15731      */
15732     refresh : function(){
15733         //Roo.log('refresh');
15734         var t = this.tpl;
15735         
15736         // if we are using something like 'domtemplate', then
15737         // the what gets used is:
15738         // t.applySubtemplate(NAME, data, wrapping data..)
15739         // the outer template then get' applied with
15740         //     the store 'extra data'
15741         // and the body get's added to the
15742         //      roo-name="data" node?
15743         //      <span class='roo-tpl-{name}'></span> ?????
15744         
15745         
15746         
15747         this.clearSelections();
15748         this.el.update("");
15749         var html = [];
15750         var records = this.store.getRange();
15751         if(records.length < 1) {
15752             
15753             // is this valid??  = should it render a template??
15754             
15755             this.el.update(this.emptyText);
15756             return;
15757         }
15758         var el = this.el;
15759         if (this.dataName) {
15760             this.el.update(t.apply(this.store.meta)); //????
15761             el = this.el.child('.roo-tpl-' + this.dataName);
15762         }
15763         
15764         for(var i = 0, len = records.length; i < len; i++){
15765             var data = this.prepareData(records[i].data, i, records[i]);
15766             this.fireEvent("preparedata", this, data, i, records[i]);
15767             
15768             var d = Roo.apply({}, data);
15769             
15770             if(this.tickable){
15771                 Roo.apply(d, {'roo-id' : Roo.id()});
15772                 
15773                 var _this = this;
15774             
15775                 Roo.each(this.parent.item, function(item){
15776                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15777                         return;
15778                     }
15779                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15780                 });
15781             }
15782             
15783             html[html.length] = Roo.util.Format.trim(
15784                 this.dataName ?
15785                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15786                     t.apply(d)
15787             );
15788         }
15789         
15790         
15791         
15792         el.update(html.join(""));
15793         this.nodes = el.dom.childNodes;
15794         this.updateIndexes(0);
15795     },
15796     
15797
15798     /**
15799      * Function to override to reformat the data that is sent to
15800      * the template for each node.
15801      * DEPRICATED - use the preparedata event handler.
15802      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15803      * a JSON object for an UpdateManager bound view).
15804      */
15805     prepareData : function(data, index, record)
15806     {
15807         this.fireEvent("preparedata", this, data, index, record);
15808         return data;
15809     },
15810
15811     onUpdate : function(ds, record){
15812         // Roo.log('on update');   
15813         this.clearSelections();
15814         var index = this.store.indexOf(record);
15815         var n = this.nodes[index];
15816         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15817         n.parentNode.removeChild(n);
15818         this.updateIndexes(index, index);
15819     },
15820
15821     
15822     
15823 // --------- FIXME     
15824     onAdd : function(ds, records, index)
15825     {
15826         //Roo.log(['on Add', ds, records, index] );        
15827         this.clearSelections();
15828         if(this.nodes.length == 0){
15829             this.refresh();
15830             return;
15831         }
15832         var n = this.nodes[index];
15833         for(var i = 0, len = records.length; i < len; i++){
15834             var d = this.prepareData(records[i].data, i, records[i]);
15835             if(n){
15836                 this.tpl.insertBefore(n, d);
15837             }else{
15838                 
15839                 this.tpl.append(this.el, d);
15840             }
15841         }
15842         this.updateIndexes(index);
15843     },
15844
15845     onRemove : function(ds, record, index){
15846        // Roo.log('onRemove');
15847         this.clearSelections();
15848         var el = this.dataName  ?
15849             this.el.child('.roo-tpl-' + this.dataName) :
15850             this.el; 
15851         
15852         el.dom.removeChild(this.nodes[index]);
15853         this.updateIndexes(index);
15854     },
15855
15856     /**
15857      * Refresh an individual node.
15858      * @param {Number} index
15859      */
15860     refreshNode : function(index){
15861         this.onUpdate(this.store, this.store.getAt(index));
15862     },
15863
15864     updateIndexes : function(startIndex, endIndex){
15865         var ns = this.nodes;
15866         startIndex = startIndex || 0;
15867         endIndex = endIndex || ns.length - 1;
15868         for(var i = startIndex; i <= endIndex; i++){
15869             ns[i].nodeIndex = i;
15870         }
15871     },
15872
15873     /**
15874      * Changes the data store this view uses and refresh the view.
15875      * @param {Store} store
15876      */
15877     setStore : function(store, initial){
15878         if(!initial && this.store){
15879             this.store.un("datachanged", this.refresh);
15880             this.store.un("add", this.onAdd);
15881             this.store.un("remove", this.onRemove);
15882             this.store.un("update", this.onUpdate);
15883             this.store.un("clear", this.refresh);
15884             this.store.un("beforeload", this.onBeforeLoad);
15885             this.store.un("load", this.onLoad);
15886             this.store.un("loadexception", this.onLoad);
15887         }
15888         if(store){
15889           
15890             store.on("datachanged", this.refresh, this);
15891             store.on("add", this.onAdd, this);
15892             store.on("remove", this.onRemove, this);
15893             store.on("update", this.onUpdate, this);
15894             store.on("clear", this.refresh, this);
15895             store.on("beforeload", this.onBeforeLoad, this);
15896             store.on("load", this.onLoad, this);
15897             store.on("loadexception", this.onLoad, this);
15898         }
15899         
15900         if(store){
15901             this.refresh();
15902         }
15903     },
15904     /**
15905      * onbeforeLoad - masks the loading area.
15906      *
15907      */
15908     onBeforeLoad : function(store,opts)
15909     {
15910          //Roo.log('onBeforeLoad');   
15911         if (!opts.add) {
15912             this.el.update("");
15913         }
15914         this.el.mask(this.mask ? this.mask : "Loading" ); 
15915     },
15916     onLoad : function ()
15917     {
15918         this.el.unmask();
15919     },
15920     
15921
15922     /**
15923      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15924      * @param {HTMLElement} node
15925      * @return {HTMLElement} The template node
15926      */
15927     findItemFromChild : function(node){
15928         var el = this.dataName  ?
15929             this.el.child('.roo-tpl-' + this.dataName,true) :
15930             this.el.dom; 
15931         
15932         if(!node || node.parentNode == el){
15933                     return node;
15934             }
15935             var p = node.parentNode;
15936             while(p && p != el){
15937             if(p.parentNode == el){
15938                 return p;
15939             }
15940             p = p.parentNode;
15941         }
15942             return null;
15943     },
15944
15945     /** @ignore */
15946     onClick : function(e){
15947         var item = this.findItemFromChild(e.getTarget());
15948         if(item){
15949             var index = this.indexOf(item);
15950             if(this.onItemClick(item, index, e) !== false){
15951                 this.fireEvent("click", this, index, item, e);
15952             }
15953         }else{
15954             this.clearSelections();
15955         }
15956     },
15957
15958     /** @ignore */
15959     onContextMenu : function(e){
15960         var item = this.findItemFromChild(e.getTarget());
15961         if(item){
15962             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15963         }
15964     },
15965
15966     /** @ignore */
15967     onDblClick : function(e){
15968         var item = this.findItemFromChild(e.getTarget());
15969         if(item){
15970             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15971         }
15972     },
15973
15974     onItemClick : function(item, index, e)
15975     {
15976         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15977             return false;
15978         }
15979         if (this.toggleSelect) {
15980             var m = this.isSelected(item) ? 'unselect' : 'select';
15981             //Roo.log(m);
15982             var _t = this;
15983             _t[m](item, true, false);
15984             return true;
15985         }
15986         if(this.multiSelect || this.singleSelect){
15987             if(this.multiSelect && e.shiftKey && this.lastSelection){
15988                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15989             }else{
15990                 this.select(item, this.multiSelect && e.ctrlKey);
15991                 this.lastSelection = item;
15992             }
15993             
15994             if(!this.tickable){
15995                 e.preventDefault();
15996             }
15997             
15998         }
15999         return true;
16000     },
16001
16002     /**
16003      * Get the number of selected nodes.
16004      * @return {Number}
16005      */
16006     getSelectionCount : function(){
16007         return this.selections.length;
16008     },
16009
16010     /**
16011      * Get the currently selected nodes.
16012      * @return {Array} An array of HTMLElements
16013      */
16014     getSelectedNodes : function(){
16015         return this.selections;
16016     },
16017
16018     /**
16019      * Get the indexes of the selected nodes.
16020      * @return {Array}
16021      */
16022     getSelectedIndexes : function(){
16023         var indexes = [], s = this.selections;
16024         for(var i = 0, len = s.length; i < len; i++){
16025             indexes.push(s[i].nodeIndex);
16026         }
16027         return indexes;
16028     },
16029
16030     /**
16031      * Clear all selections
16032      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16033      */
16034     clearSelections : function(suppressEvent){
16035         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16036             this.cmp.elements = this.selections;
16037             this.cmp.removeClass(this.selectedClass);
16038             this.selections = [];
16039             if(!suppressEvent){
16040                 this.fireEvent("selectionchange", this, this.selections);
16041             }
16042         }
16043     },
16044
16045     /**
16046      * Returns true if the passed node is selected
16047      * @param {HTMLElement/Number} node The node or node index
16048      * @return {Boolean}
16049      */
16050     isSelected : function(node){
16051         var s = this.selections;
16052         if(s.length < 1){
16053             return false;
16054         }
16055         node = this.getNode(node);
16056         return s.indexOf(node) !== -1;
16057     },
16058
16059     /**
16060      * Selects nodes.
16061      * @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
16062      * @param {Boolean} keepExisting (optional) true to keep existing selections
16063      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16064      */
16065     select : function(nodeInfo, keepExisting, suppressEvent){
16066         if(nodeInfo instanceof Array){
16067             if(!keepExisting){
16068                 this.clearSelections(true);
16069             }
16070             for(var i = 0, len = nodeInfo.length; i < len; i++){
16071                 this.select(nodeInfo[i], true, true);
16072             }
16073             return;
16074         } 
16075         var node = this.getNode(nodeInfo);
16076         if(!node || this.isSelected(node)){
16077             return; // already selected.
16078         }
16079         if(!keepExisting){
16080             this.clearSelections(true);
16081         }
16082         
16083         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16084             Roo.fly(node).addClass(this.selectedClass);
16085             this.selections.push(node);
16086             if(!suppressEvent){
16087                 this.fireEvent("selectionchange", this, this.selections);
16088             }
16089         }
16090         
16091         
16092     },
16093       /**
16094      * Unselects nodes.
16095      * @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
16096      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16097      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16098      */
16099     unselect : function(nodeInfo, keepExisting, suppressEvent)
16100     {
16101         if(nodeInfo instanceof Array){
16102             Roo.each(this.selections, function(s) {
16103                 this.unselect(s, nodeInfo);
16104             }, this);
16105             return;
16106         }
16107         var node = this.getNode(nodeInfo);
16108         if(!node || !this.isSelected(node)){
16109             //Roo.log("not selected");
16110             return; // not selected.
16111         }
16112         // fireevent???
16113         var ns = [];
16114         Roo.each(this.selections, function(s) {
16115             if (s == node ) {
16116                 Roo.fly(node).removeClass(this.selectedClass);
16117
16118                 return;
16119             }
16120             ns.push(s);
16121         },this);
16122         
16123         this.selections= ns;
16124         this.fireEvent("selectionchange", this, this.selections);
16125     },
16126
16127     /**
16128      * Gets a template node.
16129      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16130      * @return {HTMLElement} The node or null if it wasn't found
16131      */
16132     getNode : function(nodeInfo){
16133         if(typeof nodeInfo == "string"){
16134             return document.getElementById(nodeInfo);
16135         }else if(typeof nodeInfo == "number"){
16136             return this.nodes[nodeInfo];
16137         }
16138         return nodeInfo;
16139     },
16140
16141     /**
16142      * Gets a range template nodes.
16143      * @param {Number} startIndex
16144      * @param {Number} endIndex
16145      * @return {Array} An array of nodes
16146      */
16147     getNodes : function(start, end){
16148         var ns = this.nodes;
16149         start = start || 0;
16150         end = typeof end == "undefined" ? ns.length - 1 : end;
16151         var nodes = [];
16152         if(start <= end){
16153             for(var i = start; i <= end; i++){
16154                 nodes.push(ns[i]);
16155             }
16156         } else{
16157             for(var i = start; i >= end; i--){
16158                 nodes.push(ns[i]);
16159             }
16160         }
16161         return nodes;
16162     },
16163
16164     /**
16165      * Finds the index of the passed node
16166      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16167      * @return {Number} The index of the node or -1
16168      */
16169     indexOf : function(node){
16170         node = this.getNode(node);
16171         if(typeof node.nodeIndex == "number"){
16172             return node.nodeIndex;
16173         }
16174         var ns = this.nodes;
16175         for(var i = 0, len = ns.length; i < len; i++){
16176             if(ns[i] == node){
16177                 return i;
16178             }
16179         }
16180         return -1;
16181     }
16182 });
16183 /*
16184  * - LGPL
16185  *
16186  * based on jquery fullcalendar
16187  * 
16188  */
16189
16190 Roo.bootstrap = Roo.bootstrap || {};
16191 /**
16192  * @class Roo.bootstrap.Calendar
16193  * @extends Roo.bootstrap.Component
16194  * Bootstrap Calendar class
16195  * @cfg {Boolean} loadMask (true|false) default false
16196  * @cfg {Object} header generate the user specific header of the calendar, default false
16197
16198  * @constructor
16199  * Create a new Container
16200  * @param {Object} config The config object
16201  */
16202
16203
16204
16205 Roo.bootstrap.Calendar = function(config){
16206     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16207      this.addEvents({
16208         /**
16209              * @event select
16210              * Fires when a date is selected
16211              * @param {DatePicker} this
16212              * @param {Date} date The selected date
16213              */
16214         'select': true,
16215         /**
16216              * @event monthchange
16217              * Fires when the displayed month changes 
16218              * @param {DatePicker} this
16219              * @param {Date} date The selected month
16220              */
16221         'monthchange': true,
16222         /**
16223              * @event evententer
16224              * Fires when mouse over an event
16225              * @param {Calendar} this
16226              * @param {event} Event
16227              */
16228         'evententer': true,
16229         /**
16230              * @event eventleave
16231              * Fires when the mouse leaves an
16232              * @param {Calendar} this
16233              * @param {event}
16234              */
16235         'eventleave': true,
16236         /**
16237              * @event eventclick
16238              * Fires when the mouse click an
16239              * @param {Calendar} this
16240              * @param {event}
16241              */
16242         'eventclick': true
16243         
16244     });
16245
16246 };
16247
16248 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16249     
16250      /**
16251      * @cfg {Number} startDay
16252      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16253      */
16254     startDay : 0,
16255     
16256     loadMask : false,
16257     
16258     header : false,
16259       
16260     getAutoCreate : function(){
16261         
16262         
16263         var fc_button = function(name, corner, style, content ) {
16264             return Roo.apply({},{
16265                 tag : 'span',
16266                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16267                          (corner.length ?
16268                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16269                             ''
16270                         ),
16271                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16272                 unselectable: 'on'
16273             });
16274         };
16275         
16276         var header = {};
16277         
16278         if(!this.header){
16279             header = {
16280                 tag : 'table',
16281                 cls : 'fc-header',
16282                 style : 'width:100%',
16283                 cn : [
16284                     {
16285                         tag: 'tr',
16286                         cn : [
16287                             {
16288                                 tag : 'td',
16289                                 cls : 'fc-header-left',
16290                                 cn : [
16291                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16292                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16293                                     { tag: 'span', cls: 'fc-header-space' },
16294                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16295
16296
16297                                 ]
16298                             },
16299
16300                             {
16301                                 tag : 'td',
16302                                 cls : 'fc-header-center',
16303                                 cn : [
16304                                     {
16305                                         tag: 'span',
16306                                         cls: 'fc-header-title',
16307                                         cn : {
16308                                             tag: 'H2',
16309                                             html : 'month / year'
16310                                         }
16311                                     }
16312
16313                                 ]
16314                             },
16315                             {
16316                                 tag : 'td',
16317                                 cls : 'fc-header-right',
16318                                 cn : [
16319                               /*      fc_button('month', 'left', '', 'month' ),
16320                                     fc_button('week', '', '', 'week' ),
16321                                     fc_button('day', 'right', '', 'day' )
16322                                 */    
16323
16324                                 ]
16325                             }
16326
16327                         ]
16328                     }
16329                 ]
16330             };
16331         }
16332         
16333         header = this.header;
16334         
16335        
16336         var cal_heads = function() {
16337             var ret = [];
16338             // fixme - handle this.
16339             
16340             for (var i =0; i < Date.dayNames.length; i++) {
16341                 var d = Date.dayNames[i];
16342                 ret.push({
16343                     tag: 'th',
16344                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16345                     html : d.substring(0,3)
16346                 });
16347                 
16348             }
16349             ret[0].cls += ' fc-first';
16350             ret[6].cls += ' fc-last';
16351             return ret;
16352         };
16353         var cal_cell = function(n) {
16354             return  {
16355                 tag: 'td',
16356                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16357                 cn : [
16358                     {
16359                         cn : [
16360                             {
16361                                 cls: 'fc-day-number',
16362                                 html: 'D'
16363                             },
16364                             {
16365                                 cls: 'fc-day-content',
16366                              
16367                                 cn : [
16368                                      {
16369                                         style: 'position: relative;' // height: 17px;
16370                                     }
16371                                 ]
16372                             }
16373                             
16374                             
16375                         ]
16376                     }
16377                 ]
16378                 
16379             }
16380         };
16381         var cal_rows = function() {
16382             
16383             var ret = [];
16384             for (var r = 0; r < 6; r++) {
16385                 var row= {
16386                     tag : 'tr',
16387                     cls : 'fc-week',
16388                     cn : []
16389                 };
16390                 
16391                 for (var i =0; i < Date.dayNames.length; i++) {
16392                     var d = Date.dayNames[i];
16393                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16394
16395                 }
16396                 row.cn[0].cls+=' fc-first';
16397                 row.cn[0].cn[0].style = 'min-height:90px';
16398                 row.cn[6].cls+=' fc-last';
16399                 ret.push(row);
16400                 
16401             }
16402             ret[0].cls += ' fc-first';
16403             ret[4].cls += ' fc-prev-last';
16404             ret[5].cls += ' fc-last';
16405             return ret;
16406             
16407         };
16408         
16409         var cal_table = {
16410             tag: 'table',
16411             cls: 'fc-border-separate',
16412             style : 'width:100%',
16413             cellspacing  : 0,
16414             cn : [
16415                 { 
16416                     tag: 'thead',
16417                     cn : [
16418                         { 
16419                             tag: 'tr',
16420                             cls : 'fc-first fc-last',
16421                             cn : cal_heads()
16422                         }
16423                     ]
16424                 },
16425                 { 
16426                     tag: 'tbody',
16427                     cn : cal_rows()
16428                 }
16429                   
16430             ]
16431         };
16432          
16433          var cfg = {
16434             cls : 'fc fc-ltr',
16435             cn : [
16436                 header,
16437                 {
16438                     cls : 'fc-content',
16439                     style : "position: relative;",
16440                     cn : [
16441                         {
16442                             cls : 'fc-view fc-view-month fc-grid',
16443                             style : 'position: relative',
16444                             unselectable : 'on',
16445                             cn : [
16446                                 {
16447                                     cls : 'fc-event-container',
16448                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16449                                 },
16450                                 cal_table
16451                             ]
16452                         }
16453                     ]
16454     
16455                 }
16456            ] 
16457             
16458         };
16459         
16460          
16461         
16462         return cfg;
16463     },
16464     
16465     
16466     initEvents : function()
16467     {
16468         if(!this.store){
16469             throw "can not find store for calendar";
16470         }
16471         
16472         var mark = {
16473             tag: "div",
16474             cls:"x-dlg-mask",
16475             style: "text-align:center",
16476             cn: [
16477                 {
16478                     tag: "div",
16479                     style: "background-color:white;width:50%;margin:250 auto",
16480                     cn: [
16481                         {
16482                             tag: "img",
16483                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16484                         },
16485                         {
16486                             tag: "span",
16487                             html: "Loading"
16488                         }
16489                         
16490                     ]
16491                 }
16492             ]
16493         };
16494         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16495         
16496         var size = this.el.select('.fc-content', true).first().getSize();
16497         this.maskEl.setSize(size.width, size.height);
16498         this.maskEl.enableDisplayMode("block");
16499         if(!this.loadMask){
16500             this.maskEl.hide();
16501         }
16502         
16503         this.store = Roo.factory(this.store, Roo.data);
16504         this.store.on('load', this.onLoad, this);
16505         this.store.on('beforeload', this.onBeforeLoad, this);
16506         
16507         this.resize();
16508         
16509         this.cells = this.el.select('.fc-day',true);
16510         //Roo.log(this.cells);
16511         this.textNodes = this.el.query('.fc-day-number');
16512         this.cells.addClassOnOver('fc-state-hover');
16513         
16514         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16515         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16516         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16517         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16518         
16519         this.on('monthchange', this.onMonthChange, this);
16520         
16521         this.update(new Date().clearTime());
16522     },
16523     
16524     resize : function() {
16525         var sz  = this.el.getSize();
16526         
16527         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16528         this.el.select('.fc-day-content div',true).setHeight(34);
16529     },
16530     
16531     
16532     // private
16533     showPrevMonth : function(e){
16534         this.update(this.activeDate.add("mo", -1));
16535     },
16536     showToday : function(e){
16537         this.update(new Date().clearTime());
16538     },
16539     // private
16540     showNextMonth : function(e){
16541         this.update(this.activeDate.add("mo", 1));
16542     },
16543
16544     // private
16545     showPrevYear : function(){
16546         this.update(this.activeDate.add("y", -1));
16547     },
16548
16549     // private
16550     showNextYear : function(){
16551         this.update(this.activeDate.add("y", 1));
16552     },
16553
16554     
16555    // private
16556     update : function(date)
16557     {
16558         var vd = this.activeDate;
16559         this.activeDate = date;
16560 //        if(vd && this.el){
16561 //            var t = date.getTime();
16562 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16563 //                Roo.log('using add remove');
16564 //                
16565 //                this.fireEvent('monthchange', this, date);
16566 //                
16567 //                this.cells.removeClass("fc-state-highlight");
16568 //                this.cells.each(function(c){
16569 //                   if(c.dateValue == t){
16570 //                       c.addClass("fc-state-highlight");
16571 //                       setTimeout(function(){
16572 //                            try{c.dom.firstChild.focus();}catch(e){}
16573 //                       }, 50);
16574 //                       return false;
16575 //                   }
16576 //                   return true;
16577 //                });
16578 //                return;
16579 //            }
16580 //        }
16581         
16582         var days = date.getDaysInMonth();
16583         
16584         var firstOfMonth = date.getFirstDateOfMonth();
16585         var startingPos = firstOfMonth.getDay()-this.startDay;
16586         
16587         if(startingPos < this.startDay){
16588             startingPos += 7;
16589         }
16590         
16591         var pm = date.add(Date.MONTH, -1);
16592         var prevStart = pm.getDaysInMonth()-startingPos;
16593 //        
16594         this.cells = this.el.select('.fc-day',true);
16595         this.textNodes = this.el.query('.fc-day-number');
16596         this.cells.addClassOnOver('fc-state-hover');
16597         
16598         var cells = this.cells.elements;
16599         var textEls = this.textNodes;
16600         
16601         Roo.each(cells, function(cell){
16602             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16603         });
16604         
16605         days += startingPos;
16606
16607         // convert everything to numbers so it's fast
16608         var day = 86400000;
16609         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16610         //Roo.log(d);
16611         //Roo.log(pm);
16612         //Roo.log(prevStart);
16613         
16614         var today = new Date().clearTime().getTime();
16615         var sel = date.clearTime().getTime();
16616         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16617         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16618         var ddMatch = this.disabledDatesRE;
16619         var ddText = this.disabledDatesText;
16620         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16621         var ddaysText = this.disabledDaysText;
16622         var format = this.format;
16623         
16624         var setCellClass = function(cal, cell){
16625             cell.row = 0;
16626             cell.events = [];
16627             cell.more = [];
16628             //Roo.log('set Cell Class');
16629             cell.title = "";
16630             var t = d.getTime();
16631             
16632             //Roo.log(d);
16633             
16634             cell.dateValue = t;
16635             if(t == today){
16636                 cell.className += " fc-today";
16637                 cell.className += " fc-state-highlight";
16638                 cell.title = cal.todayText;
16639             }
16640             if(t == sel){
16641                 // disable highlight in other month..
16642                 //cell.className += " fc-state-highlight";
16643                 
16644             }
16645             // disabling
16646             if(t < min) {
16647                 cell.className = " fc-state-disabled";
16648                 cell.title = cal.minText;
16649                 return;
16650             }
16651             if(t > max) {
16652                 cell.className = " fc-state-disabled";
16653                 cell.title = cal.maxText;
16654                 return;
16655             }
16656             if(ddays){
16657                 if(ddays.indexOf(d.getDay()) != -1){
16658                     cell.title = ddaysText;
16659                     cell.className = " fc-state-disabled";
16660                 }
16661             }
16662             if(ddMatch && format){
16663                 var fvalue = d.dateFormat(format);
16664                 if(ddMatch.test(fvalue)){
16665                     cell.title = ddText.replace("%0", fvalue);
16666                     cell.className = " fc-state-disabled";
16667                 }
16668             }
16669             
16670             if (!cell.initialClassName) {
16671                 cell.initialClassName = cell.dom.className;
16672             }
16673             
16674             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16675         };
16676
16677         var i = 0;
16678         
16679         for(; i < startingPos; i++) {
16680             textEls[i].innerHTML = (++prevStart);
16681             d.setDate(d.getDate()+1);
16682             
16683             cells[i].className = "fc-past fc-other-month";
16684             setCellClass(this, cells[i]);
16685         }
16686         
16687         var intDay = 0;
16688         
16689         for(; i < days; i++){
16690             intDay = i - startingPos + 1;
16691             textEls[i].innerHTML = (intDay);
16692             d.setDate(d.getDate()+1);
16693             
16694             cells[i].className = ''; // "x-date-active";
16695             setCellClass(this, cells[i]);
16696         }
16697         var extraDays = 0;
16698         
16699         for(; i < 42; i++) {
16700             textEls[i].innerHTML = (++extraDays);
16701             d.setDate(d.getDate()+1);
16702             
16703             cells[i].className = "fc-future fc-other-month";
16704             setCellClass(this, cells[i]);
16705         }
16706         
16707         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16708         
16709         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16710         
16711         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16712         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16713         
16714         if(totalRows != 6){
16715             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16716             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16717         }
16718         
16719         this.fireEvent('monthchange', this, date);
16720         
16721         
16722         /*
16723         if(!this.internalRender){
16724             var main = this.el.dom.firstChild;
16725             var w = main.offsetWidth;
16726             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16727             Roo.fly(main).setWidth(w);
16728             this.internalRender = true;
16729             // opera does not respect the auto grow header center column
16730             // then, after it gets a width opera refuses to recalculate
16731             // without a second pass
16732             if(Roo.isOpera && !this.secondPass){
16733                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16734                 this.secondPass = true;
16735                 this.update.defer(10, this, [date]);
16736             }
16737         }
16738         */
16739         
16740     },
16741     
16742     findCell : function(dt) {
16743         dt = dt.clearTime().getTime();
16744         var ret = false;
16745         this.cells.each(function(c){
16746             //Roo.log("check " +c.dateValue + '?=' + dt);
16747             if(c.dateValue == dt){
16748                 ret = c;
16749                 return false;
16750             }
16751             return true;
16752         });
16753         
16754         return ret;
16755     },
16756     
16757     findCells : function(ev) {
16758         var s = ev.start.clone().clearTime().getTime();
16759        // Roo.log(s);
16760         var e= ev.end.clone().clearTime().getTime();
16761        // Roo.log(e);
16762         var ret = [];
16763         this.cells.each(function(c){
16764              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16765             
16766             if(c.dateValue > e){
16767                 return ;
16768             }
16769             if(c.dateValue < s){
16770                 return ;
16771             }
16772             ret.push(c);
16773         });
16774         
16775         return ret;    
16776     },
16777     
16778 //    findBestRow: function(cells)
16779 //    {
16780 //        var ret = 0;
16781 //        
16782 //        for (var i =0 ; i < cells.length;i++) {
16783 //            ret  = Math.max(cells[i].rows || 0,ret);
16784 //        }
16785 //        return ret;
16786 //        
16787 //    },
16788     
16789     
16790     addItem : function(ev)
16791     {
16792         // look for vertical location slot in
16793         var cells = this.findCells(ev);
16794         
16795 //        ev.row = this.findBestRow(cells);
16796         
16797         // work out the location.
16798         
16799         var crow = false;
16800         var rows = [];
16801         for(var i =0; i < cells.length; i++) {
16802             
16803             cells[i].row = cells[0].row;
16804             
16805             if(i == 0){
16806                 cells[i].row = cells[i].row + 1;
16807             }
16808             
16809             if (!crow) {
16810                 crow = {
16811                     start : cells[i],
16812                     end :  cells[i]
16813                 };
16814                 continue;
16815             }
16816             if (crow.start.getY() == cells[i].getY()) {
16817                 // on same row.
16818                 crow.end = cells[i];
16819                 continue;
16820             }
16821             // different row.
16822             rows.push(crow);
16823             crow = {
16824                 start: cells[i],
16825                 end : cells[i]
16826             };
16827             
16828         }
16829         
16830         rows.push(crow);
16831         ev.els = [];
16832         ev.rows = rows;
16833         ev.cells = cells;
16834         
16835         cells[0].events.push(ev);
16836         
16837         this.calevents.push(ev);
16838     },
16839     
16840     clearEvents: function() {
16841         
16842         if(!this.calevents){
16843             return;
16844         }
16845         
16846         Roo.each(this.cells.elements, function(c){
16847             c.row = 0;
16848             c.events = [];
16849             c.more = [];
16850         });
16851         
16852         Roo.each(this.calevents, function(e) {
16853             Roo.each(e.els, function(el) {
16854                 el.un('mouseenter' ,this.onEventEnter, this);
16855                 el.un('mouseleave' ,this.onEventLeave, this);
16856                 el.remove();
16857             },this);
16858         },this);
16859         
16860         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16861             e.remove();
16862         });
16863         
16864     },
16865     
16866     renderEvents: function()
16867     {   
16868         var _this = this;
16869         
16870         this.cells.each(function(c) {
16871             
16872             if(c.row < 5){
16873                 return;
16874             }
16875             
16876             var ev = c.events;
16877             
16878             var r = 4;
16879             if(c.row != c.events.length){
16880                 r = 4 - (4 - (c.row - c.events.length));
16881             }
16882             
16883             c.events = ev.slice(0, r);
16884             c.more = ev.slice(r);
16885             
16886             if(c.more.length && c.more.length == 1){
16887                 c.events.push(c.more.pop());
16888             }
16889             
16890             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16891             
16892         });
16893             
16894         this.cells.each(function(c) {
16895             
16896             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16897             
16898             
16899             for (var e = 0; e < c.events.length; e++){
16900                 var ev = c.events[e];
16901                 var rows = ev.rows;
16902                 
16903                 for(var i = 0; i < rows.length; i++) {
16904                 
16905                     // how many rows should it span..
16906
16907                     var  cfg = {
16908                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16909                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16910
16911                         unselectable : "on",
16912                         cn : [
16913                             {
16914                                 cls: 'fc-event-inner',
16915                                 cn : [
16916     //                                {
16917     //                                  tag:'span',
16918     //                                  cls: 'fc-event-time',
16919     //                                  html : cells.length > 1 ? '' : ev.time
16920     //                                },
16921                                     {
16922                                       tag:'span',
16923                                       cls: 'fc-event-title',
16924                                       html : String.format('{0}', ev.title)
16925                                     }
16926
16927
16928                                 ]
16929                             },
16930                             {
16931                                 cls: 'ui-resizable-handle ui-resizable-e',
16932                                 html : '&nbsp;&nbsp;&nbsp'
16933                             }
16934
16935                         ]
16936                     };
16937
16938                     if (i == 0) {
16939                         cfg.cls += ' fc-event-start';
16940                     }
16941                     if ((i+1) == rows.length) {
16942                         cfg.cls += ' fc-event-end';
16943                     }
16944
16945                     var ctr = _this.el.select('.fc-event-container',true).first();
16946                     var cg = ctr.createChild(cfg);
16947
16948                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16949                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16950
16951                     var r = (c.more.length) ? 1 : 0;
16952                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16953                     cg.setWidth(ebox.right - sbox.x -2);
16954
16955                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16956                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16957                     cg.on('click', _this.onEventClick, _this, ev);
16958
16959                     ev.els.push(cg);
16960                     
16961                 }
16962                 
16963             }
16964             
16965             
16966             if(c.more.length){
16967                 var  cfg = {
16968                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16969                     style : 'position: absolute',
16970                     unselectable : "on",
16971                     cn : [
16972                         {
16973                             cls: 'fc-event-inner',
16974                             cn : [
16975                                 {
16976                                   tag:'span',
16977                                   cls: 'fc-event-title',
16978                                   html : 'More'
16979                                 }
16980
16981
16982                             ]
16983                         },
16984                         {
16985                             cls: 'ui-resizable-handle ui-resizable-e',
16986                             html : '&nbsp;&nbsp;&nbsp'
16987                         }
16988
16989                     ]
16990                 };
16991
16992                 var ctr = _this.el.select('.fc-event-container',true).first();
16993                 var cg = ctr.createChild(cfg);
16994
16995                 var sbox = c.select('.fc-day-content',true).first().getBox();
16996                 var ebox = c.select('.fc-day-content',true).first().getBox();
16997                 //Roo.log(cg);
16998                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16999                 cg.setWidth(ebox.right - sbox.x -2);
17000
17001                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17002                 
17003             }
17004             
17005         });
17006         
17007         
17008         
17009     },
17010     
17011     onEventEnter: function (e, el,event,d) {
17012         this.fireEvent('evententer', this, el, event);
17013     },
17014     
17015     onEventLeave: function (e, el,event,d) {
17016         this.fireEvent('eventleave', this, el, event);
17017     },
17018     
17019     onEventClick: function (e, el,event,d) {
17020         this.fireEvent('eventclick', this, el, event);
17021     },
17022     
17023     onMonthChange: function () {
17024         this.store.load();
17025     },
17026     
17027     onMoreEventClick: function(e, el, more)
17028     {
17029         var _this = this;
17030         
17031         this.calpopover.placement = 'right';
17032         this.calpopover.setTitle('More');
17033         
17034         this.calpopover.setContent('');
17035         
17036         var ctr = this.calpopover.el.select('.popover-content', true).first();
17037         
17038         Roo.each(more, function(m){
17039             var cfg = {
17040                 cls : 'fc-event-hori fc-event-draggable',
17041                 html : m.title
17042             };
17043             var cg = ctr.createChild(cfg);
17044             
17045             cg.on('click', _this.onEventClick, _this, m);
17046         });
17047         
17048         this.calpopover.show(el);
17049         
17050         
17051     },
17052     
17053     onLoad: function () 
17054     {   
17055         this.calevents = [];
17056         var cal = this;
17057         
17058         if(this.store.getCount() > 0){
17059             this.store.data.each(function(d){
17060                cal.addItem({
17061                     id : d.data.id,
17062                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17063                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17064                     time : d.data.start_time,
17065                     title : d.data.title,
17066                     description : d.data.description,
17067                     venue : d.data.venue
17068                 });
17069             });
17070         }
17071         
17072         this.renderEvents();
17073         
17074         if(this.calevents.length && this.loadMask){
17075             this.maskEl.hide();
17076         }
17077     },
17078     
17079     onBeforeLoad: function()
17080     {
17081         this.clearEvents();
17082         if(this.loadMask){
17083             this.maskEl.show();
17084         }
17085     }
17086 });
17087
17088  
17089  /*
17090  * - LGPL
17091  *
17092  * element
17093  * 
17094  */
17095
17096 /**
17097  * @class Roo.bootstrap.Popover
17098  * @extends Roo.bootstrap.Component
17099  * Bootstrap Popover class
17100  * @cfg {String} html contents of the popover   (or false to use children..)
17101  * @cfg {String} title of popover (or false to hide)
17102  * @cfg {String} placement how it is placed
17103  * @cfg {String} trigger click || hover (or false to trigger manually)
17104  * @cfg {String} over what (parent or false to trigger manually.)
17105  * @cfg {Number} delay - delay before showing
17106  
17107  * @constructor
17108  * Create a new Popover
17109  * @param {Object} config The config object
17110  */
17111
17112 Roo.bootstrap.Popover = function(config){
17113     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17114     
17115     this.addEvents({
17116         // raw events
17117          /**
17118          * @event show
17119          * After the popover show
17120          * 
17121          * @param {Roo.bootstrap.Popover} this
17122          */
17123         "show" : true,
17124         /**
17125          * @event hide
17126          * After the popover hide
17127          * 
17128          * @param {Roo.bootstrap.Popover} this
17129          */
17130         "hide" : true
17131     });
17132 };
17133
17134 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17135     
17136     title: 'Fill in a title',
17137     html: false,
17138     
17139     placement : 'right',
17140     trigger : 'hover', // hover
17141     
17142     delay : 0,
17143     
17144     over: 'parent',
17145     
17146     can_build_overlaid : false,
17147     
17148     getChildContainer : function()
17149     {
17150         return this.el.select('.popover-content',true).first();
17151     },
17152     
17153     getAutoCreate : function(){
17154          
17155         var cfg = {
17156            cls : 'popover roo-dynamic',
17157            style: 'display:block',
17158            cn : [
17159                 {
17160                     cls : 'arrow'
17161                 },
17162                 {
17163                     cls : 'popover-inner',
17164                     cn : [
17165                         {
17166                             tag: 'h3',
17167                             cls: 'popover-title',
17168                             html : this.title
17169                         },
17170                         {
17171                             cls : 'popover-content',
17172                             html : this.html
17173                         }
17174                     ]
17175                     
17176                 }
17177            ]
17178         };
17179         
17180         return cfg;
17181     },
17182     setTitle: function(str)
17183     {
17184         this.title = str;
17185         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17186     },
17187     setContent: function(str)
17188     {
17189         this.html = str;
17190         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17191     },
17192     // as it get's added to the bottom of the page.
17193     onRender : function(ct, position)
17194     {
17195         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17196         if(!this.el){
17197             var cfg = Roo.apply({},  this.getAutoCreate());
17198             cfg.id = Roo.id();
17199             
17200             if (this.cls) {
17201                 cfg.cls += ' ' + this.cls;
17202             }
17203             if (this.style) {
17204                 cfg.style = this.style;
17205             }
17206             //Roo.log("adding to ");
17207             this.el = Roo.get(document.body).createChild(cfg, position);
17208 //            Roo.log(this.el);
17209         }
17210         this.initEvents();
17211     },
17212     
17213     initEvents : function()
17214     {
17215         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17216         this.el.enableDisplayMode('block');
17217         this.el.hide();
17218         if (this.over === false) {
17219             return; 
17220         }
17221         if (this.triggers === false) {
17222             return;
17223         }
17224         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17225         var triggers = this.trigger ? this.trigger.split(' ') : [];
17226         Roo.each(triggers, function(trigger) {
17227         
17228             if (trigger == 'click') {
17229                 on_el.on('click', this.toggle, this);
17230             } else if (trigger != 'manual') {
17231                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17232                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17233       
17234                 on_el.on(eventIn  ,this.enter, this);
17235                 on_el.on(eventOut, this.leave, this);
17236             }
17237         }, this);
17238         
17239     },
17240     
17241     
17242     // private
17243     timeout : null,
17244     hoverState : null,
17245     
17246     toggle : function () {
17247         this.hoverState == 'in' ? this.leave() : this.enter();
17248     },
17249     
17250     enter : function () {
17251         
17252         clearTimeout(this.timeout);
17253     
17254         this.hoverState = 'in';
17255     
17256         if (!this.delay || !this.delay.show) {
17257             this.show();
17258             return;
17259         }
17260         var _t = this;
17261         this.timeout = setTimeout(function () {
17262             if (_t.hoverState == 'in') {
17263                 _t.show();
17264             }
17265         }, this.delay.show)
17266     },
17267     
17268     leave : function() {
17269         clearTimeout(this.timeout);
17270     
17271         this.hoverState = 'out';
17272     
17273         if (!this.delay || !this.delay.hide) {
17274             this.hide();
17275             return;
17276         }
17277         var _t = this;
17278         this.timeout = setTimeout(function () {
17279             if (_t.hoverState == 'out') {
17280                 _t.hide();
17281             }
17282         }, this.delay.hide)
17283     },
17284     
17285     show : function (on_el)
17286     {
17287         if (!on_el) {
17288             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17289         }
17290         
17291         // set content.
17292         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17293         if (this.html !== false) {
17294             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17295         }
17296         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17297         if (!this.title.length) {
17298             this.el.select('.popover-title',true).hide();
17299         }
17300         
17301         var placement = typeof this.placement == 'function' ?
17302             this.placement.call(this, this.el, on_el) :
17303             this.placement;
17304             
17305         var autoToken = /\s?auto?\s?/i;
17306         var autoPlace = autoToken.test(placement);
17307         if (autoPlace) {
17308             placement = placement.replace(autoToken, '') || 'top';
17309         }
17310         
17311         //this.el.detach()
17312         //this.el.setXY([0,0]);
17313         this.el.show();
17314         this.el.dom.style.display='block';
17315         this.el.addClass(placement);
17316         
17317         //this.el.appendTo(on_el);
17318         
17319         var p = this.getPosition();
17320         var box = this.el.getBox();
17321         
17322         if (autoPlace) {
17323             // fixme..
17324         }
17325         var align = Roo.bootstrap.Popover.alignment[placement];
17326         this.el.alignTo(on_el, align[0],align[1]);
17327         //var arrow = this.el.select('.arrow',true).first();
17328         //arrow.set(align[2], 
17329         
17330         this.el.addClass('in');
17331         
17332         
17333         if (this.el.hasClass('fade')) {
17334             // fade it?
17335         }
17336         
17337         this.hoverState = 'in';
17338         
17339         this.fireEvent('show', this);
17340         
17341     },
17342     hide : function()
17343     {
17344         this.el.setXY([0,0]);
17345         this.el.removeClass('in');
17346         this.el.hide();
17347         this.hoverState = null;
17348         
17349         this.fireEvent('hide', this);
17350     }
17351     
17352 });
17353
17354 Roo.bootstrap.Popover.alignment = {
17355     'left' : ['r-l', [-10,0], 'right'],
17356     'right' : ['l-r', [10,0], 'left'],
17357     'bottom' : ['t-b', [0,10], 'top'],
17358     'top' : [ 'b-t', [0,-10], 'bottom']
17359 };
17360
17361  /*
17362  * - LGPL
17363  *
17364  * Progress
17365  * 
17366  */
17367
17368 /**
17369  * @class Roo.bootstrap.Progress
17370  * @extends Roo.bootstrap.Component
17371  * Bootstrap Progress class
17372  * @cfg {Boolean} striped striped of the progress bar
17373  * @cfg {Boolean} active animated of the progress bar
17374  * 
17375  * 
17376  * @constructor
17377  * Create a new Progress
17378  * @param {Object} config The config object
17379  */
17380
17381 Roo.bootstrap.Progress = function(config){
17382     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17383 };
17384
17385 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17386     
17387     striped : false,
17388     active: false,
17389     
17390     getAutoCreate : function(){
17391         var cfg = {
17392             tag: 'div',
17393             cls: 'progress'
17394         };
17395         
17396         
17397         if(this.striped){
17398             cfg.cls += ' progress-striped';
17399         }
17400       
17401         if(this.active){
17402             cfg.cls += ' active';
17403         }
17404         
17405         
17406         return cfg;
17407     }
17408    
17409 });
17410
17411  
17412
17413  /*
17414  * - LGPL
17415  *
17416  * ProgressBar
17417  * 
17418  */
17419
17420 /**
17421  * @class Roo.bootstrap.ProgressBar
17422  * @extends Roo.bootstrap.Component
17423  * Bootstrap ProgressBar class
17424  * @cfg {Number} aria_valuenow aria-value now
17425  * @cfg {Number} aria_valuemin aria-value min
17426  * @cfg {Number} aria_valuemax aria-value max
17427  * @cfg {String} label label for the progress bar
17428  * @cfg {String} panel (success | info | warning | danger )
17429  * @cfg {String} role role of the progress bar
17430  * @cfg {String} sr_only text
17431  * 
17432  * 
17433  * @constructor
17434  * Create a new ProgressBar
17435  * @param {Object} config The config object
17436  */
17437
17438 Roo.bootstrap.ProgressBar = function(config){
17439     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17440 };
17441
17442 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17443     
17444     aria_valuenow : 0,
17445     aria_valuemin : 0,
17446     aria_valuemax : 100,
17447     label : false,
17448     panel : false,
17449     role : false,
17450     sr_only: false,
17451     
17452     getAutoCreate : function()
17453     {
17454         
17455         var cfg = {
17456             tag: 'div',
17457             cls: 'progress-bar',
17458             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17459         };
17460         
17461         if(this.sr_only){
17462             cfg.cn = {
17463                 tag: 'span',
17464                 cls: 'sr-only',
17465                 html: this.sr_only
17466             }
17467         }
17468         
17469         if(this.role){
17470             cfg.role = this.role;
17471         }
17472         
17473         if(this.aria_valuenow){
17474             cfg['aria-valuenow'] = this.aria_valuenow;
17475         }
17476         
17477         if(this.aria_valuemin){
17478             cfg['aria-valuemin'] = this.aria_valuemin;
17479         }
17480         
17481         if(this.aria_valuemax){
17482             cfg['aria-valuemax'] = this.aria_valuemax;
17483         }
17484         
17485         if(this.label && !this.sr_only){
17486             cfg.html = this.label;
17487         }
17488         
17489         if(this.panel){
17490             cfg.cls += ' progress-bar-' + this.panel;
17491         }
17492         
17493         return cfg;
17494     },
17495     
17496     update : function(aria_valuenow)
17497     {
17498         this.aria_valuenow = aria_valuenow;
17499         
17500         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17501     }
17502    
17503 });
17504
17505  
17506
17507  /*
17508  * - LGPL
17509  *
17510  * column
17511  * 
17512  */
17513
17514 /**
17515  * @class Roo.bootstrap.TabGroup
17516  * @extends Roo.bootstrap.Column
17517  * Bootstrap Column class
17518  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17519  * @cfg {Boolean} carousel true to make the group behave like a carousel
17520  * @cfg {Boolean} bullets show bullets for the panels
17521  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17522  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17523  * @cfg {Boolean} showarrow (true|false) show arrow default true
17524  * 
17525  * @constructor
17526  * Create a new TabGroup
17527  * @param {Object} config The config object
17528  */
17529
17530 Roo.bootstrap.TabGroup = function(config){
17531     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17532     if (!this.navId) {
17533         this.navId = Roo.id();
17534     }
17535     this.tabs = [];
17536     Roo.bootstrap.TabGroup.register(this);
17537     
17538 };
17539
17540 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17541     
17542     carousel : false,
17543     transition : false,
17544     bullets : 0,
17545     timer : 0,
17546     autoslide : false,
17547     slideFn : false,
17548     slideOnTouch : false,
17549     showarrow : true,
17550     
17551     getAutoCreate : function()
17552     {
17553         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17554         
17555         cfg.cls += ' tab-content';
17556         
17557         if (this.carousel) {
17558             cfg.cls += ' carousel slide';
17559             
17560             cfg.cn = [{
17561                cls : 'carousel-inner',
17562                cn : []
17563             }];
17564         
17565             if(this.bullets  && !Roo.isTouch){
17566                 
17567                 var bullets = {
17568                     cls : 'carousel-bullets',
17569                     cn : []
17570                 };
17571                
17572                 if(this.bullets_cls){
17573                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17574                 }
17575                 
17576                 bullets.cn.push({
17577                     cls : 'clear'
17578                 });
17579                 
17580                 cfg.cn[0].cn.push(bullets);
17581             }
17582             
17583             if(this.showarrow){
17584                 cfg.cn[0].cn.push({
17585                     tag : 'div',
17586                     class : 'carousel-arrow',
17587                     cn : [
17588                         {
17589                             tag : 'div',
17590                             class : 'carousel-prev',
17591                             cn : [
17592                                 {
17593                                     tag : 'i',
17594                                     class : 'fa fa-chevron-left'
17595                                 }
17596                             ]
17597                         },
17598                         {
17599                             tag : 'div',
17600                             class : 'carousel-next',
17601                             cn : [
17602                                 {
17603                                     tag : 'i',
17604                                     class : 'fa fa-chevron-right'
17605                                 }
17606                             ]
17607                         }
17608                     ]
17609                 });
17610             }
17611             
17612         }
17613         
17614         return cfg;
17615     },
17616     
17617     initEvents:  function()
17618     {
17619 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17620 //            this.el.on("touchstart", this.onTouchStart, this);
17621 //        }
17622         
17623         if(this.autoslide){
17624             var _this = this;
17625             
17626             this.slideFn = window.setInterval(function() {
17627                 _this.showPanelNext();
17628             }, this.timer);
17629         }
17630         
17631         if(this.showarrow){
17632             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17633             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17634         }
17635         
17636         
17637     },
17638     
17639 //    onTouchStart : function(e, el, o)
17640 //    {
17641 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17642 //            return;
17643 //        }
17644 //        
17645 //        this.showPanelNext();
17646 //    },
17647     
17648     
17649     getChildContainer : function()
17650     {
17651         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17652     },
17653     
17654     /**
17655     * register a Navigation item
17656     * @param {Roo.bootstrap.NavItem} the navitem to add
17657     */
17658     register : function(item)
17659     {
17660         this.tabs.push( item);
17661         item.navId = this.navId; // not really needed..
17662         this.addBullet();
17663     
17664     },
17665     
17666     getActivePanel : function()
17667     {
17668         var r = false;
17669         Roo.each(this.tabs, function(t) {
17670             if (t.active) {
17671                 r = t;
17672                 return false;
17673             }
17674             return null;
17675         });
17676         return r;
17677         
17678     },
17679     getPanelByName : function(n)
17680     {
17681         var r = false;
17682         Roo.each(this.tabs, function(t) {
17683             if (t.tabId == n) {
17684                 r = t;
17685                 return false;
17686             }
17687             return null;
17688         });
17689         return r;
17690     },
17691     indexOfPanel : function(p)
17692     {
17693         var r = false;
17694         Roo.each(this.tabs, function(t,i) {
17695             if (t.tabId == p.tabId) {
17696                 r = i;
17697                 return false;
17698             }
17699             return null;
17700         });
17701         return r;
17702     },
17703     /**
17704      * show a specific panel
17705      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17706      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17707      */
17708     showPanel : function (pan)
17709     {
17710         if(this.transition || typeof(pan) == 'undefined'){
17711             Roo.log("waiting for the transitionend");
17712             return;
17713         }
17714         
17715         if (typeof(pan) == 'number') {
17716             pan = this.tabs[pan];
17717         }
17718         
17719         if (typeof(pan) == 'string') {
17720             pan = this.getPanelByName(pan);
17721         }
17722         
17723         var cur = this.getActivePanel();
17724         
17725         if(!pan || !cur){
17726             Roo.log('pan or acitve pan is undefined');
17727             return false;
17728         }
17729         
17730         if (pan.tabId == this.getActivePanel().tabId) {
17731             return true;
17732         }
17733         
17734         if (false === cur.fireEvent('beforedeactivate')) {
17735             return false;
17736         }
17737         
17738         if(this.bullets > 0 && !Roo.isTouch){
17739             this.setActiveBullet(this.indexOfPanel(pan));
17740         }
17741         
17742         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17743             
17744             this.transition = true;
17745             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17746             var lr = dir == 'next' ? 'left' : 'right';
17747             pan.el.addClass(dir); // or prev
17748             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17749             cur.el.addClass(lr); // or right
17750             pan.el.addClass(lr);
17751             
17752             var _this = this;
17753             cur.el.on('transitionend', function() {
17754                 Roo.log("trans end?");
17755                 
17756                 pan.el.removeClass([lr,dir]);
17757                 pan.setActive(true);
17758                 
17759                 cur.el.removeClass([lr]);
17760                 cur.setActive(false);
17761                 
17762                 _this.transition = false;
17763                 
17764             }, this, { single:  true } );
17765             
17766             return true;
17767         }
17768         
17769         cur.setActive(false);
17770         pan.setActive(true);
17771         
17772         return true;
17773         
17774     },
17775     showPanelNext : function()
17776     {
17777         var i = this.indexOfPanel(this.getActivePanel());
17778         
17779         if (i >= this.tabs.length - 1 && !this.autoslide) {
17780             return;
17781         }
17782         
17783         if (i >= this.tabs.length - 1 && this.autoslide) {
17784             i = -1;
17785         }
17786         
17787         this.showPanel(this.tabs[i+1]);
17788     },
17789     
17790     showPanelPrev : function()
17791     {
17792         var i = this.indexOfPanel(this.getActivePanel());
17793         
17794         if (i  < 1 && !this.autoslide) {
17795             return;
17796         }
17797         
17798         if (i < 1 && this.autoslide) {
17799             i = this.tabs.length;
17800         }
17801         
17802         this.showPanel(this.tabs[i-1]);
17803     },
17804     
17805     
17806     addBullet: function()
17807     {
17808         if(!this.bullets || Roo.isTouch){
17809             return;
17810         }
17811         var ctr = this.el.select('.carousel-bullets',true).first();
17812         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17813         var bullet = ctr.createChild({
17814             cls : 'bullet bullet-' + i
17815         },ctr.dom.lastChild);
17816         
17817         
17818         var _this = this;
17819         
17820         bullet.on('click', (function(e, el, o, ii, t){
17821
17822             e.preventDefault();
17823
17824             this.showPanel(ii);
17825
17826             if(this.autoslide && this.slideFn){
17827                 clearInterval(this.slideFn);
17828                 this.slideFn = window.setInterval(function() {
17829                     _this.showPanelNext();
17830                 }, this.timer);
17831             }
17832
17833         }).createDelegate(this, [i, bullet], true));
17834                 
17835         
17836     },
17837      
17838     setActiveBullet : function(i)
17839     {
17840         if(Roo.isTouch){
17841             return;
17842         }
17843         
17844         Roo.each(this.el.select('.bullet', true).elements, function(el){
17845             el.removeClass('selected');
17846         });
17847
17848         var bullet = this.el.select('.bullet-' + i, true).first();
17849         
17850         if(!bullet){
17851             return;
17852         }
17853         
17854         bullet.addClass('selected');
17855     }
17856     
17857     
17858   
17859 });
17860
17861  
17862
17863  
17864  
17865 Roo.apply(Roo.bootstrap.TabGroup, {
17866     
17867     groups: {},
17868      /**
17869     * register a Navigation Group
17870     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17871     */
17872     register : function(navgrp)
17873     {
17874         this.groups[navgrp.navId] = navgrp;
17875         
17876     },
17877     /**
17878     * fetch a Navigation Group based on the navigation ID
17879     * if one does not exist , it will get created.
17880     * @param {string} the navgroup to add
17881     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17882     */
17883     get: function(navId) {
17884         if (typeof(this.groups[navId]) == 'undefined') {
17885             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17886         }
17887         return this.groups[navId] ;
17888     }
17889     
17890     
17891     
17892 });
17893
17894  /*
17895  * - LGPL
17896  *
17897  * TabPanel
17898  * 
17899  */
17900
17901 /**
17902  * @class Roo.bootstrap.TabPanel
17903  * @extends Roo.bootstrap.Component
17904  * Bootstrap TabPanel class
17905  * @cfg {Boolean} active panel active
17906  * @cfg {String} html panel content
17907  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17908  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17909  * @cfg {String} href click to link..
17910  * 
17911  * 
17912  * @constructor
17913  * Create a new TabPanel
17914  * @param {Object} config The config object
17915  */
17916
17917 Roo.bootstrap.TabPanel = function(config){
17918     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17919     this.addEvents({
17920         /**
17921              * @event changed
17922              * Fires when the active status changes
17923              * @param {Roo.bootstrap.TabPanel} this
17924              * @param {Boolean} state the new state
17925             
17926          */
17927         'changed': true,
17928         /**
17929              * @event beforedeactivate
17930              * Fires before a tab is de-activated - can be used to do validation on a form.
17931              * @param {Roo.bootstrap.TabPanel} this
17932              * @return {Boolean} false if there is an error
17933             
17934          */
17935         'beforedeactivate': true
17936      });
17937     
17938     this.tabId = this.tabId || Roo.id();
17939   
17940 };
17941
17942 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17943     
17944     active: false,
17945     html: false,
17946     tabId: false,
17947     navId : false,
17948     href : '',
17949     
17950     getAutoCreate : function(){
17951         var cfg = {
17952             tag: 'div',
17953             // item is needed for carousel - not sure if it has any effect otherwise
17954             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17955             html: this.html || ''
17956         };
17957         
17958         if(this.active){
17959             cfg.cls += ' active';
17960         }
17961         
17962         if(this.tabId){
17963             cfg.tabId = this.tabId;
17964         }
17965         
17966         
17967         return cfg;
17968     },
17969     
17970     initEvents:  function()
17971     {
17972         var p = this.parent();
17973         
17974         this.navId = this.navId || p.navId;
17975         
17976         if (typeof(this.navId) != 'undefined') {
17977             // not really needed.. but just in case.. parent should be a NavGroup.
17978             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17979             
17980             tg.register(this);
17981             
17982             var i = tg.tabs.length - 1;
17983             
17984             if(this.active && tg.bullets > 0 && i < tg.bullets){
17985                 tg.setActiveBullet(i);
17986             }
17987         }
17988         
17989         this.el.on('click', this.onClick, this);
17990         
17991         if(Roo.isTouch){
17992             this.el.on("touchstart", this.onTouchStart, this);
17993             this.el.on("touchmove", this.onTouchMove, this);
17994             this.el.on("touchend", this.onTouchEnd, this);
17995         }
17996         
17997     },
17998     
17999     onRender : function(ct, position)
18000     {
18001         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18002     },
18003     
18004     setActive : function(state)
18005     {
18006         Roo.log("panel - set active " + this.tabId + "=" + state);
18007         
18008         this.active = state;
18009         if (!state) {
18010             this.el.removeClass('active');
18011             
18012         } else  if (!this.el.hasClass('active')) {
18013             this.el.addClass('active');
18014         }
18015         
18016         this.fireEvent('changed', this, state);
18017     },
18018     
18019     onClick : function(e)
18020     {
18021         e.preventDefault();
18022         
18023         if(!this.href.length){
18024             return;
18025         }
18026         
18027         window.location.href = this.href;
18028     },
18029     
18030     startX : 0,
18031     startY : 0,
18032     endX : 0,
18033     endY : 0,
18034     swiping : false,
18035     
18036     onTouchStart : function(e)
18037     {
18038         this.swiping = false;
18039         
18040         this.startX = e.browserEvent.touches[0].clientX;
18041         this.startY = e.browserEvent.touches[0].clientY;
18042     },
18043     
18044     onTouchMove : function(e)
18045     {
18046         this.swiping = true;
18047         
18048         this.endX = e.browserEvent.touches[0].clientX;
18049         this.endY = e.browserEvent.touches[0].clientY;
18050     },
18051     
18052     onTouchEnd : function(e)
18053     {
18054         if(!this.swiping){
18055             this.onClick(e);
18056             return;
18057         }
18058         
18059         var tabGroup = this.parent();
18060         
18061         if(this.endX > this.startX){ // swiping right
18062             tabGroup.showPanelPrev();
18063             return;
18064         }
18065         
18066         if(this.startX > this.endX){ // swiping left
18067             tabGroup.showPanelNext();
18068             return;
18069         }
18070     }
18071     
18072     
18073 });
18074  
18075
18076  
18077
18078  /*
18079  * - LGPL
18080  *
18081  * DateField
18082  * 
18083  */
18084
18085 /**
18086  * @class Roo.bootstrap.DateField
18087  * @extends Roo.bootstrap.Input
18088  * Bootstrap DateField class
18089  * @cfg {Number} weekStart default 0
18090  * @cfg {String} viewMode default empty, (months|years)
18091  * @cfg {String} minViewMode default empty, (months|years)
18092  * @cfg {Number} startDate default -Infinity
18093  * @cfg {Number} endDate default Infinity
18094  * @cfg {Boolean} todayHighlight default false
18095  * @cfg {Boolean} todayBtn default false
18096  * @cfg {Boolean} calendarWeeks default false
18097  * @cfg {Object} daysOfWeekDisabled default empty
18098  * @cfg {Boolean} singleMode default false (true | false)
18099  * 
18100  * @cfg {Boolean} keyboardNavigation default true
18101  * @cfg {String} language default en
18102  * 
18103  * @constructor
18104  * Create a new DateField
18105  * @param {Object} config The config object
18106  */
18107
18108 Roo.bootstrap.DateField = function(config){
18109     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18110      this.addEvents({
18111             /**
18112              * @event show
18113              * Fires when this field show.
18114              * @param {Roo.bootstrap.DateField} this
18115              * @param {Mixed} date The date value
18116              */
18117             show : true,
18118             /**
18119              * @event show
18120              * Fires when this field hide.
18121              * @param {Roo.bootstrap.DateField} this
18122              * @param {Mixed} date The date value
18123              */
18124             hide : true,
18125             /**
18126              * @event select
18127              * Fires when select a date.
18128              * @param {Roo.bootstrap.DateField} this
18129              * @param {Mixed} date The date value
18130              */
18131             select : true,
18132             /**
18133              * @event beforeselect
18134              * Fires when before select a date.
18135              * @param {Roo.bootstrap.DateField} this
18136              * @param {Mixed} date The date value
18137              */
18138             beforeselect : true
18139         });
18140 };
18141
18142 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18143     
18144     /**
18145      * @cfg {String} format
18146      * The default date format string which can be overriden for localization support.  The format must be
18147      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18148      */
18149     format : "m/d/y",
18150     /**
18151      * @cfg {String} altFormats
18152      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18153      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18154      */
18155     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18156     
18157     weekStart : 0,
18158     
18159     viewMode : '',
18160     
18161     minViewMode : '',
18162     
18163     todayHighlight : false,
18164     
18165     todayBtn: false,
18166     
18167     language: 'en',
18168     
18169     keyboardNavigation: true,
18170     
18171     calendarWeeks: false,
18172     
18173     startDate: -Infinity,
18174     
18175     endDate: Infinity,
18176     
18177     daysOfWeekDisabled: [],
18178     
18179     _events: [],
18180     
18181     singleMode : false,
18182     
18183     UTCDate: function()
18184     {
18185         return new Date(Date.UTC.apply(Date, arguments));
18186     },
18187     
18188     UTCToday: function()
18189     {
18190         var today = new Date();
18191         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18192     },
18193     
18194     getDate: function() {
18195             var d = this.getUTCDate();
18196             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18197     },
18198     
18199     getUTCDate: function() {
18200             return this.date;
18201     },
18202     
18203     setDate: function(d) {
18204             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18205     },
18206     
18207     setUTCDate: function(d) {
18208             this.date = d;
18209             this.setValue(this.formatDate(this.date));
18210     },
18211         
18212     onRender: function(ct, position)
18213     {
18214         
18215         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18216         
18217         this.language = this.language || 'en';
18218         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18219         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18220         
18221         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18222         this.format = this.format || 'm/d/y';
18223         this.isInline = false;
18224         this.isInput = true;
18225         this.component = this.el.select('.add-on', true).first() || false;
18226         this.component = (this.component && this.component.length === 0) ? false : this.component;
18227         this.hasInput = this.component && this.inputEl().length;
18228         
18229         if (typeof(this.minViewMode === 'string')) {
18230             switch (this.minViewMode) {
18231                 case 'months':
18232                     this.minViewMode = 1;
18233                     break;
18234                 case 'years':
18235                     this.minViewMode = 2;
18236                     break;
18237                 default:
18238                     this.minViewMode = 0;
18239                     break;
18240             }
18241         }
18242         
18243         if (typeof(this.viewMode === 'string')) {
18244             switch (this.viewMode) {
18245                 case 'months':
18246                     this.viewMode = 1;
18247                     break;
18248                 case 'years':
18249                     this.viewMode = 2;
18250                     break;
18251                 default:
18252                     this.viewMode = 0;
18253                     break;
18254             }
18255         }
18256                 
18257         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18258         
18259 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18260         
18261         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18262         
18263         this.picker().on('mousedown', this.onMousedown, this);
18264         this.picker().on('click', this.onClick, this);
18265         
18266         this.picker().addClass('datepicker-dropdown');
18267         
18268         this.startViewMode = this.viewMode;
18269         
18270         if(this.singleMode){
18271             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18272                 v.setVisibilityMode(Roo.Element.DISPLAY);
18273                 v.hide();
18274             });
18275             
18276             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18277                 v.setStyle('width', '189px');
18278             });
18279         }
18280         
18281         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18282             if(!this.calendarWeeks){
18283                 v.remove();
18284                 return;
18285             }
18286             
18287             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18288             v.attr('colspan', function(i, val){
18289                 return parseInt(val) + 1;
18290             });
18291         });
18292                         
18293         
18294         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18295         
18296         this.setStartDate(this.startDate);
18297         this.setEndDate(this.endDate);
18298         
18299         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18300         
18301         this.fillDow();
18302         this.fillMonths();
18303         this.update();
18304         this.showMode();
18305         
18306         if(this.isInline) {
18307             this.show();
18308         }
18309     },
18310     
18311     picker : function()
18312     {
18313         return this.pickerEl;
18314 //        return this.el.select('.datepicker', true).first();
18315     },
18316     
18317     fillDow: function()
18318     {
18319         var dowCnt = this.weekStart;
18320         
18321         var dow = {
18322             tag: 'tr',
18323             cn: [
18324                 
18325             ]
18326         };
18327         
18328         if(this.calendarWeeks){
18329             dow.cn.push({
18330                 tag: 'th',
18331                 cls: 'cw',
18332                 html: '&nbsp;'
18333             })
18334         }
18335         
18336         while (dowCnt < this.weekStart + 7) {
18337             dow.cn.push({
18338                 tag: 'th',
18339                 cls: 'dow',
18340                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18341             });
18342         }
18343         
18344         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18345     },
18346     
18347     fillMonths: function()
18348     {    
18349         var i = 0;
18350         var months = this.picker().select('>.datepicker-months td', true).first();
18351         
18352         months.dom.innerHTML = '';
18353         
18354         while (i < 12) {
18355             var month = {
18356                 tag: 'span',
18357                 cls: 'month',
18358                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18359             };
18360             
18361             months.createChild(month);
18362         }
18363         
18364     },
18365     
18366     update: function()
18367     {
18368         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;
18369         
18370         if (this.date < this.startDate) {
18371             this.viewDate = new Date(this.startDate);
18372         } else if (this.date > this.endDate) {
18373             this.viewDate = new Date(this.endDate);
18374         } else {
18375             this.viewDate = new Date(this.date);
18376         }
18377         
18378         this.fill();
18379     },
18380     
18381     fill: function() 
18382     {
18383         var d = new Date(this.viewDate),
18384                 year = d.getUTCFullYear(),
18385                 month = d.getUTCMonth(),
18386                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18387                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18388                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18389                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18390                 currentDate = this.date && this.date.valueOf(),
18391                 today = this.UTCToday();
18392         
18393         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18394         
18395 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18396         
18397 //        this.picker.select('>tfoot th.today').
18398 //                                              .text(dates[this.language].today)
18399 //                                              .toggle(this.todayBtn !== false);
18400     
18401         this.updateNavArrows();
18402         this.fillMonths();
18403                                                 
18404         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18405         
18406         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18407          
18408         prevMonth.setUTCDate(day);
18409         
18410         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18411         
18412         var nextMonth = new Date(prevMonth);
18413         
18414         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18415         
18416         nextMonth = nextMonth.valueOf();
18417         
18418         var fillMonths = false;
18419         
18420         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18421         
18422         while(prevMonth.valueOf() < nextMonth) {
18423             var clsName = '';
18424             
18425             if (prevMonth.getUTCDay() === this.weekStart) {
18426                 if(fillMonths){
18427                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18428                 }
18429                     
18430                 fillMonths = {
18431                     tag: 'tr',
18432                     cn: []
18433                 };
18434                 
18435                 if(this.calendarWeeks){
18436                     // ISO 8601: First week contains first thursday.
18437                     // ISO also states week starts on Monday, but we can be more abstract here.
18438                     var
18439                     // Start of current week: based on weekstart/current date
18440                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18441                     // Thursday of this week
18442                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18443                     // First Thursday of year, year from thursday
18444                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18445                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18446                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18447                     
18448                     fillMonths.cn.push({
18449                         tag: 'td',
18450                         cls: 'cw',
18451                         html: calWeek
18452                     });
18453                 }
18454             }
18455             
18456             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18457                 clsName += ' old';
18458             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18459                 clsName += ' new';
18460             }
18461             if (this.todayHighlight &&
18462                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18463                 prevMonth.getUTCMonth() == today.getMonth() &&
18464                 prevMonth.getUTCDate() == today.getDate()) {
18465                 clsName += ' today';
18466             }
18467             
18468             if (currentDate && prevMonth.valueOf() === currentDate) {
18469                 clsName += ' active';
18470             }
18471             
18472             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18473                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18474                     clsName += ' disabled';
18475             }
18476             
18477             fillMonths.cn.push({
18478                 tag: 'td',
18479                 cls: 'day ' + clsName,
18480                 html: prevMonth.getDate()
18481             });
18482             
18483             prevMonth.setDate(prevMonth.getDate()+1);
18484         }
18485           
18486         var currentYear = this.date && this.date.getUTCFullYear();
18487         var currentMonth = this.date && this.date.getUTCMonth();
18488         
18489         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18490         
18491         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18492             v.removeClass('active');
18493             
18494             if(currentYear === year && k === currentMonth){
18495                 v.addClass('active');
18496             }
18497             
18498             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18499                 v.addClass('disabled');
18500             }
18501             
18502         });
18503         
18504         
18505         year = parseInt(year/10, 10) * 10;
18506         
18507         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18508         
18509         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18510         
18511         year -= 1;
18512         for (var i = -1; i < 11; i++) {
18513             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18514                 tag: 'span',
18515                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18516                 html: year
18517             });
18518             
18519             year += 1;
18520         }
18521     },
18522     
18523     showMode: function(dir) 
18524     {
18525         if (dir) {
18526             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18527         }
18528         
18529         Roo.each(this.picker().select('>div',true).elements, function(v){
18530             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18531             v.hide();
18532         });
18533         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18534     },
18535     
18536     place: function()
18537     {
18538         if(this.isInline) {
18539             return;
18540         }
18541         
18542         this.picker().removeClass(['bottom', 'top']);
18543         
18544         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18545             /*
18546              * place to the top of element!
18547              *
18548              */
18549             
18550             this.picker().addClass('top');
18551             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18552             
18553             return;
18554         }
18555         
18556         this.picker().addClass('bottom');
18557         
18558         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18559     },
18560     
18561     parseDate : function(value)
18562     {
18563         if(!value || value instanceof Date){
18564             return value;
18565         }
18566         var v = Date.parseDate(value, this.format);
18567         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18568             v = Date.parseDate(value, 'Y-m-d');
18569         }
18570         if(!v && this.altFormats){
18571             if(!this.altFormatsArray){
18572                 this.altFormatsArray = this.altFormats.split("|");
18573             }
18574             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18575                 v = Date.parseDate(value, this.altFormatsArray[i]);
18576             }
18577         }
18578         return v;
18579     },
18580     
18581     formatDate : function(date, fmt)
18582     {   
18583         return (!date || !(date instanceof Date)) ?
18584         date : date.dateFormat(fmt || this.format);
18585     },
18586     
18587     onFocus : function()
18588     {
18589         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18590         this.show();
18591     },
18592     
18593     onBlur : function()
18594     {
18595         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18596         
18597         var d = this.inputEl().getValue();
18598         
18599         this.setValue(d);
18600                 
18601         this.hide();
18602     },
18603     
18604     show : function()
18605     {
18606         this.picker().show();
18607         this.update();
18608         this.place();
18609         
18610         this.fireEvent('show', this, this.date);
18611     },
18612     
18613     hide : function()
18614     {
18615         if(this.isInline) {
18616             return;
18617         }
18618         this.picker().hide();
18619         this.viewMode = this.startViewMode;
18620         this.showMode();
18621         
18622         this.fireEvent('hide', this, this.date);
18623         
18624     },
18625     
18626     onMousedown: function(e)
18627     {
18628         e.stopPropagation();
18629         e.preventDefault();
18630     },
18631     
18632     keyup: function(e)
18633     {
18634         Roo.bootstrap.DateField.superclass.keyup.call(this);
18635         this.update();
18636     },
18637
18638     setValue: function(v)
18639     {
18640         if(this.fireEvent('beforeselect', this, v) !== false){
18641             var d = new Date(this.parseDate(v) ).clearTime();
18642         
18643             if(isNaN(d.getTime())){
18644                 this.date = this.viewDate = '';
18645                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18646                 return;
18647             }
18648
18649             v = this.formatDate(d);
18650
18651             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18652
18653             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18654
18655             this.update();
18656
18657             this.fireEvent('select', this, this.date);
18658         }
18659     },
18660     
18661     getValue: function()
18662     {
18663         return this.formatDate(this.date);
18664     },
18665     
18666     fireKey: function(e)
18667     {
18668         if (!this.picker().isVisible()){
18669             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18670                 this.show();
18671             }
18672             return;
18673         }
18674         
18675         var dateChanged = false,
18676         dir, day, month,
18677         newDate, newViewDate;
18678         
18679         switch(e.keyCode){
18680             case 27: // escape
18681                 this.hide();
18682                 e.preventDefault();
18683                 break;
18684             case 37: // left
18685             case 39: // right
18686                 if (!this.keyboardNavigation) {
18687                     break;
18688                 }
18689                 dir = e.keyCode == 37 ? -1 : 1;
18690                 
18691                 if (e.ctrlKey){
18692                     newDate = this.moveYear(this.date, dir);
18693                     newViewDate = this.moveYear(this.viewDate, dir);
18694                 } else if (e.shiftKey){
18695                     newDate = this.moveMonth(this.date, dir);
18696                     newViewDate = this.moveMonth(this.viewDate, dir);
18697                 } else {
18698                     newDate = new Date(this.date);
18699                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18700                     newViewDate = new Date(this.viewDate);
18701                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18702                 }
18703                 if (this.dateWithinRange(newDate)){
18704                     this.date = newDate;
18705                     this.viewDate = newViewDate;
18706                     this.setValue(this.formatDate(this.date));
18707 //                    this.update();
18708                     e.preventDefault();
18709                     dateChanged = true;
18710                 }
18711                 break;
18712             case 38: // up
18713             case 40: // down
18714                 if (!this.keyboardNavigation) {
18715                     break;
18716                 }
18717                 dir = e.keyCode == 38 ? -1 : 1;
18718                 if (e.ctrlKey){
18719                     newDate = this.moveYear(this.date, dir);
18720                     newViewDate = this.moveYear(this.viewDate, dir);
18721                 } else if (e.shiftKey){
18722                     newDate = this.moveMonth(this.date, dir);
18723                     newViewDate = this.moveMonth(this.viewDate, dir);
18724                 } else {
18725                     newDate = new Date(this.date);
18726                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18727                     newViewDate = new Date(this.viewDate);
18728                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18729                 }
18730                 if (this.dateWithinRange(newDate)){
18731                     this.date = newDate;
18732                     this.viewDate = newViewDate;
18733                     this.setValue(this.formatDate(this.date));
18734 //                    this.update();
18735                     e.preventDefault();
18736                     dateChanged = true;
18737                 }
18738                 break;
18739             case 13: // enter
18740                 this.setValue(this.formatDate(this.date));
18741                 this.hide();
18742                 e.preventDefault();
18743                 break;
18744             case 9: // tab
18745                 this.setValue(this.formatDate(this.date));
18746                 this.hide();
18747                 break;
18748             case 16: // shift
18749             case 17: // ctrl
18750             case 18: // alt
18751                 break;
18752             default :
18753                 this.hide();
18754                 
18755         }
18756     },
18757     
18758     
18759     onClick: function(e) 
18760     {
18761         e.stopPropagation();
18762         e.preventDefault();
18763         
18764         var target = e.getTarget();
18765         
18766         if(target.nodeName.toLowerCase() === 'i'){
18767             target = Roo.get(target).dom.parentNode;
18768         }
18769         
18770         var nodeName = target.nodeName;
18771         var className = target.className;
18772         var html = target.innerHTML;
18773         //Roo.log(nodeName);
18774         
18775         switch(nodeName.toLowerCase()) {
18776             case 'th':
18777                 switch(className) {
18778                     case 'switch':
18779                         this.showMode(1);
18780                         break;
18781                     case 'prev':
18782                     case 'next':
18783                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18784                         switch(this.viewMode){
18785                                 case 0:
18786                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18787                                         break;
18788                                 case 1:
18789                                 case 2:
18790                                         this.viewDate = this.moveYear(this.viewDate, dir);
18791                                         break;
18792                         }
18793                         this.fill();
18794                         break;
18795                     case 'today':
18796                         var date = new Date();
18797                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18798 //                        this.fill()
18799                         this.setValue(this.formatDate(this.date));
18800                         
18801                         this.hide();
18802                         break;
18803                 }
18804                 break;
18805             case 'span':
18806                 if (className.indexOf('disabled') < 0) {
18807                     this.viewDate.setUTCDate(1);
18808                     if (className.indexOf('month') > -1) {
18809                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18810                     } else {
18811                         var year = parseInt(html, 10) || 0;
18812                         this.viewDate.setUTCFullYear(year);
18813                         
18814                     }
18815                     
18816                     if(this.singleMode){
18817                         this.setValue(this.formatDate(this.viewDate));
18818                         this.hide();
18819                         return;
18820                     }
18821                     
18822                     this.showMode(-1);
18823                     this.fill();
18824                 }
18825                 break;
18826                 
18827             case 'td':
18828                 //Roo.log(className);
18829                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18830                     var day = parseInt(html, 10) || 1;
18831                     var year = this.viewDate.getUTCFullYear(),
18832                         month = this.viewDate.getUTCMonth();
18833
18834                     if (className.indexOf('old') > -1) {
18835                         if(month === 0 ){
18836                             month = 11;
18837                             year -= 1;
18838                         }else{
18839                             month -= 1;
18840                         }
18841                     } else if (className.indexOf('new') > -1) {
18842                         if (month == 11) {
18843                             month = 0;
18844                             year += 1;
18845                         } else {
18846                             month += 1;
18847                         }
18848                     }
18849                     //Roo.log([year,month,day]);
18850                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18851                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18852 //                    this.fill();
18853                     //Roo.log(this.formatDate(this.date));
18854                     this.setValue(this.formatDate(this.date));
18855                     this.hide();
18856                 }
18857                 break;
18858         }
18859     },
18860     
18861     setStartDate: function(startDate)
18862     {
18863         this.startDate = startDate || -Infinity;
18864         if (this.startDate !== -Infinity) {
18865             this.startDate = this.parseDate(this.startDate);
18866         }
18867         this.update();
18868         this.updateNavArrows();
18869     },
18870
18871     setEndDate: function(endDate)
18872     {
18873         this.endDate = endDate || Infinity;
18874         if (this.endDate !== Infinity) {
18875             this.endDate = this.parseDate(this.endDate);
18876         }
18877         this.update();
18878         this.updateNavArrows();
18879     },
18880     
18881     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18882     {
18883         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18884         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18885             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18886         }
18887         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18888             return parseInt(d, 10);
18889         });
18890         this.update();
18891         this.updateNavArrows();
18892     },
18893     
18894     updateNavArrows: function() 
18895     {
18896         if(this.singleMode){
18897             return;
18898         }
18899         
18900         var d = new Date(this.viewDate),
18901         year = d.getUTCFullYear(),
18902         month = d.getUTCMonth();
18903         
18904         Roo.each(this.picker().select('.prev', true).elements, function(v){
18905             v.show();
18906             switch (this.viewMode) {
18907                 case 0:
18908
18909                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18910                         v.hide();
18911                     }
18912                     break;
18913                 case 1:
18914                 case 2:
18915                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18916                         v.hide();
18917                     }
18918                     break;
18919             }
18920         });
18921         
18922         Roo.each(this.picker().select('.next', true).elements, function(v){
18923             v.show();
18924             switch (this.viewMode) {
18925                 case 0:
18926
18927                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18928                         v.hide();
18929                     }
18930                     break;
18931                 case 1:
18932                 case 2:
18933                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18934                         v.hide();
18935                     }
18936                     break;
18937             }
18938         })
18939     },
18940     
18941     moveMonth: function(date, dir)
18942     {
18943         if (!dir) {
18944             return date;
18945         }
18946         var new_date = new Date(date.valueOf()),
18947         day = new_date.getUTCDate(),
18948         month = new_date.getUTCMonth(),
18949         mag = Math.abs(dir),
18950         new_month, test;
18951         dir = dir > 0 ? 1 : -1;
18952         if (mag == 1){
18953             test = dir == -1
18954             // If going back one month, make sure month is not current month
18955             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18956             ? function(){
18957                 return new_date.getUTCMonth() == month;
18958             }
18959             // If going forward one month, make sure month is as expected
18960             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18961             : function(){
18962                 return new_date.getUTCMonth() != new_month;
18963             };
18964             new_month = month + dir;
18965             new_date.setUTCMonth(new_month);
18966             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18967             if (new_month < 0 || new_month > 11) {
18968                 new_month = (new_month + 12) % 12;
18969             }
18970         } else {
18971             // For magnitudes >1, move one month at a time...
18972             for (var i=0; i<mag; i++) {
18973                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18974                 new_date = this.moveMonth(new_date, dir);
18975             }
18976             // ...then reset the day, keeping it in the new month
18977             new_month = new_date.getUTCMonth();
18978             new_date.setUTCDate(day);
18979             test = function(){
18980                 return new_month != new_date.getUTCMonth();
18981             };
18982         }
18983         // Common date-resetting loop -- if date is beyond end of month, make it
18984         // end of month
18985         while (test()){
18986             new_date.setUTCDate(--day);
18987             new_date.setUTCMonth(new_month);
18988         }
18989         return new_date;
18990     },
18991
18992     moveYear: function(date, dir)
18993     {
18994         return this.moveMonth(date, dir*12);
18995     },
18996
18997     dateWithinRange: function(date)
18998     {
18999         return date >= this.startDate && date <= this.endDate;
19000     },
19001
19002     
19003     remove: function() 
19004     {
19005         this.picker().remove();
19006     },
19007     
19008     validateValue : function(value)
19009     {
19010         if(value.length < 1)  {
19011             if(this.allowBlank){
19012                 return true;
19013             }
19014             return false;
19015         }
19016         
19017         if(value.length < this.minLength){
19018             return false;
19019         }
19020         if(value.length > this.maxLength){
19021             return false;
19022         }
19023         if(this.vtype){
19024             var vt = Roo.form.VTypes;
19025             if(!vt[this.vtype](value, this)){
19026                 return false;
19027             }
19028         }
19029         if(typeof this.validator == "function"){
19030             var msg = this.validator(value);
19031             if(msg !== true){
19032                 return false;
19033             }
19034         }
19035         
19036         if(this.regex && !this.regex.test(value)){
19037             return false;
19038         }
19039         
19040         if(typeof(this.parseDate(value)) == 'undefined'){
19041             return false;
19042         }
19043         
19044         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19045             return false;
19046         }      
19047         
19048         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19049             return false;
19050         } 
19051         
19052         
19053         return true;
19054     }
19055    
19056 });
19057
19058 Roo.apply(Roo.bootstrap.DateField,  {
19059     
19060     head : {
19061         tag: 'thead',
19062         cn: [
19063         {
19064             tag: 'tr',
19065             cn: [
19066             {
19067                 tag: 'th',
19068                 cls: 'prev',
19069                 html: '<i class="fa fa-arrow-left"/>'
19070             },
19071             {
19072                 tag: 'th',
19073                 cls: 'switch',
19074                 colspan: '5'
19075             },
19076             {
19077                 tag: 'th',
19078                 cls: 'next',
19079                 html: '<i class="fa fa-arrow-right"/>'
19080             }
19081
19082             ]
19083         }
19084         ]
19085     },
19086     
19087     content : {
19088         tag: 'tbody',
19089         cn: [
19090         {
19091             tag: 'tr',
19092             cn: [
19093             {
19094                 tag: 'td',
19095                 colspan: '7'
19096             }
19097             ]
19098         }
19099         ]
19100     },
19101     
19102     footer : {
19103         tag: 'tfoot',
19104         cn: [
19105         {
19106             tag: 'tr',
19107             cn: [
19108             {
19109                 tag: 'th',
19110                 colspan: '7',
19111                 cls: 'today'
19112             }
19113                     
19114             ]
19115         }
19116         ]
19117     },
19118     
19119     dates:{
19120         en: {
19121             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19122             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19123             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19124             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19125             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19126             today: "Today"
19127         }
19128     },
19129     
19130     modes: [
19131     {
19132         clsName: 'days',
19133         navFnc: 'Month',
19134         navStep: 1
19135     },
19136     {
19137         clsName: 'months',
19138         navFnc: 'FullYear',
19139         navStep: 1
19140     },
19141     {
19142         clsName: 'years',
19143         navFnc: 'FullYear',
19144         navStep: 10
19145     }]
19146 });
19147
19148 Roo.apply(Roo.bootstrap.DateField,  {
19149   
19150     template : {
19151         tag: 'div',
19152         cls: 'datepicker dropdown-menu roo-dynamic',
19153         cn: [
19154         {
19155             tag: 'div',
19156             cls: 'datepicker-days',
19157             cn: [
19158             {
19159                 tag: 'table',
19160                 cls: 'table-condensed',
19161                 cn:[
19162                 Roo.bootstrap.DateField.head,
19163                 {
19164                     tag: 'tbody'
19165                 },
19166                 Roo.bootstrap.DateField.footer
19167                 ]
19168             }
19169             ]
19170         },
19171         {
19172             tag: 'div',
19173             cls: 'datepicker-months',
19174             cn: [
19175             {
19176                 tag: 'table',
19177                 cls: 'table-condensed',
19178                 cn:[
19179                 Roo.bootstrap.DateField.head,
19180                 Roo.bootstrap.DateField.content,
19181                 Roo.bootstrap.DateField.footer
19182                 ]
19183             }
19184             ]
19185         },
19186         {
19187             tag: 'div',
19188             cls: 'datepicker-years',
19189             cn: [
19190             {
19191                 tag: 'table',
19192                 cls: 'table-condensed',
19193                 cn:[
19194                 Roo.bootstrap.DateField.head,
19195                 Roo.bootstrap.DateField.content,
19196                 Roo.bootstrap.DateField.footer
19197                 ]
19198             }
19199             ]
19200         }
19201         ]
19202     }
19203 });
19204
19205  
19206
19207  /*
19208  * - LGPL
19209  *
19210  * TimeField
19211  * 
19212  */
19213
19214 /**
19215  * @class Roo.bootstrap.TimeField
19216  * @extends Roo.bootstrap.Input
19217  * Bootstrap DateField class
19218  * 
19219  * 
19220  * @constructor
19221  * Create a new TimeField
19222  * @param {Object} config The config object
19223  */
19224
19225 Roo.bootstrap.TimeField = function(config){
19226     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19227     this.addEvents({
19228             /**
19229              * @event show
19230              * Fires when this field show.
19231              * @param {Roo.bootstrap.DateField} thisthis
19232              * @param {Mixed} date The date value
19233              */
19234             show : true,
19235             /**
19236              * @event show
19237              * Fires when this field hide.
19238              * @param {Roo.bootstrap.DateField} this
19239              * @param {Mixed} date The date value
19240              */
19241             hide : true,
19242             /**
19243              * @event select
19244              * Fires when select a date.
19245              * @param {Roo.bootstrap.DateField} this
19246              * @param {Mixed} date The date value
19247              */
19248             select : true
19249         });
19250 };
19251
19252 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19253     
19254     /**
19255      * @cfg {String} format
19256      * The default time format string which can be overriden for localization support.  The format must be
19257      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19258      */
19259     format : "H:i",
19260        
19261     onRender: function(ct, position)
19262     {
19263         
19264         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19265                 
19266         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19267         
19268         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19269         
19270         this.pop = this.picker().select('>.datepicker-time',true).first();
19271         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19272         
19273         this.picker().on('mousedown', this.onMousedown, this);
19274         this.picker().on('click', this.onClick, this);
19275         
19276         this.picker().addClass('datepicker-dropdown');
19277     
19278         this.fillTime();
19279         this.update();
19280             
19281         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19282         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19283         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19284         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19285         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19286         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19287
19288     },
19289     
19290     fireKey: function(e){
19291         if (!this.picker().isVisible()){
19292             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19293                 this.show();
19294             }
19295             return;
19296         }
19297
19298         e.preventDefault();
19299         
19300         switch(e.keyCode){
19301             case 27: // escape
19302                 this.hide();
19303                 break;
19304             case 37: // left
19305             case 39: // right
19306                 this.onTogglePeriod();
19307                 break;
19308             case 38: // up
19309                 this.onIncrementMinutes();
19310                 break;
19311             case 40: // down
19312                 this.onDecrementMinutes();
19313                 break;
19314             case 13: // enter
19315             case 9: // tab
19316                 this.setTime();
19317                 break;
19318         }
19319     },
19320     
19321     onClick: function(e) {
19322         e.stopPropagation();
19323         e.preventDefault();
19324     },
19325     
19326     picker : function()
19327     {
19328         return this.el.select('.datepicker', true).first();
19329     },
19330     
19331     fillTime: function()
19332     {    
19333         var time = this.pop.select('tbody', true).first();
19334         
19335         time.dom.innerHTML = '';
19336         
19337         time.createChild({
19338             tag: 'tr',
19339             cn: [
19340                 {
19341                     tag: 'td',
19342                     cn: [
19343                         {
19344                             tag: 'a',
19345                             href: '#',
19346                             cls: 'btn',
19347                             cn: [
19348                                 {
19349                                     tag: 'span',
19350                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19351                                 }
19352                             ]
19353                         } 
19354                     ]
19355                 },
19356                 {
19357                     tag: 'td',
19358                     cls: 'separator'
19359                 },
19360                 {
19361                     tag: 'td',
19362                     cn: [
19363                         {
19364                             tag: 'a',
19365                             href: '#',
19366                             cls: 'btn',
19367                             cn: [
19368                                 {
19369                                     tag: 'span',
19370                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19371                                 }
19372                             ]
19373                         }
19374                     ]
19375                 },
19376                 {
19377                     tag: 'td',
19378                     cls: 'separator'
19379                 }
19380             ]
19381         });
19382         
19383         time.createChild({
19384             tag: 'tr',
19385             cn: [
19386                 {
19387                     tag: 'td',
19388                     cn: [
19389                         {
19390                             tag: 'span',
19391                             cls: 'timepicker-hour',
19392                             html: '00'
19393                         }  
19394                     ]
19395                 },
19396                 {
19397                     tag: 'td',
19398                     cls: 'separator',
19399                     html: ':'
19400                 },
19401                 {
19402                     tag: 'td',
19403                     cn: [
19404                         {
19405                             tag: 'span',
19406                             cls: 'timepicker-minute',
19407                             html: '00'
19408                         }  
19409                     ]
19410                 },
19411                 {
19412                     tag: 'td',
19413                     cls: 'separator'
19414                 },
19415                 {
19416                     tag: 'td',
19417                     cn: [
19418                         {
19419                             tag: 'button',
19420                             type: 'button',
19421                             cls: 'btn btn-primary period',
19422                             html: 'AM'
19423                             
19424                         }
19425                     ]
19426                 }
19427             ]
19428         });
19429         
19430         time.createChild({
19431             tag: 'tr',
19432             cn: [
19433                 {
19434                     tag: 'td',
19435                     cn: [
19436                         {
19437                             tag: 'a',
19438                             href: '#',
19439                             cls: 'btn',
19440                             cn: [
19441                                 {
19442                                     tag: 'span',
19443                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19444                                 }
19445                             ]
19446                         }
19447                     ]
19448                 },
19449                 {
19450                     tag: 'td',
19451                     cls: 'separator'
19452                 },
19453                 {
19454                     tag: 'td',
19455                     cn: [
19456                         {
19457                             tag: 'a',
19458                             href: '#',
19459                             cls: 'btn',
19460                             cn: [
19461                                 {
19462                                     tag: 'span',
19463                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19464                                 }
19465                             ]
19466                         }
19467                     ]
19468                 },
19469                 {
19470                     tag: 'td',
19471                     cls: 'separator'
19472                 }
19473             ]
19474         });
19475         
19476     },
19477     
19478     update: function()
19479     {
19480         
19481         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19482         
19483         this.fill();
19484     },
19485     
19486     fill: function() 
19487     {
19488         var hours = this.time.getHours();
19489         var minutes = this.time.getMinutes();
19490         var period = 'AM';
19491         
19492         if(hours > 11){
19493             period = 'PM';
19494         }
19495         
19496         if(hours == 0){
19497             hours = 12;
19498         }
19499         
19500         
19501         if(hours > 12){
19502             hours = hours - 12;
19503         }
19504         
19505         if(hours < 10){
19506             hours = '0' + hours;
19507         }
19508         
19509         if(minutes < 10){
19510             minutes = '0' + minutes;
19511         }
19512         
19513         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19514         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19515         this.pop.select('button', true).first().dom.innerHTML = period;
19516         
19517     },
19518     
19519     place: function()
19520     {   
19521         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19522         
19523         var cls = ['bottom'];
19524         
19525         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19526             cls.pop();
19527             cls.push('top');
19528         }
19529         
19530         cls.push('right');
19531         
19532         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19533             cls.pop();
19534             cls.push('left');
19535         }
19536         
19537         this.picker().addClass(cls.join('-'));
19538         
19539         var _this = this;
19540         
19541         Roo.each(cls, function(c){
19542             if(c == 'bottom'){
19543                 _this.picker().setTop(_this.inputEl().getHeight());
19544                 return;
19545             }
19546             if(c == 'top'){
19547                 _this.picker().setTop(0 - _this.picker().getHeight());
19548                 return;
19549             }
19550             
19551             if(c == 'left'){
19552                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19553                 return;
19554             }
19555             if(c == 'right'){
19556                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19557                 return;
19558             }
19559         });
19560         
19561     },
19562   
19563     onFocus : function()
19564     {
19565         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19566         this.show();
19567     },
19568     
19569     onBlur : function()
19570     {
19571         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19572         this.hide();
19573     },
19574     
19575     show : function()
19576     {
19577         this.picker().show();
19578         this.pop.show();
19579         this.update();
19580         this.place();
19581         
19582         this.fireEvent('show', this, this.date);
19583     },
19584     
19585     hide : function()
19586     {
19587         this.picker().hide();
19588         this.pop.hide();
19589         
19590         this.fireEvent('hide', this, this.date);
19591     },
19592     
19593     setTime : function()
19594     {
19595         this.hide();
19596         this.setValue(this.time.format(this.format));
19597         
19598         this.fireEvent('select', this, this.date);
19599         
19600         
19601     },
19602     
19603     onMousedown: function(e){
19604         e.stopPropagation();
19605         e.preventDefault();
19606     },
19607     
19608     onIncrementHours: function()
19609     {
19610         Roo.log('onIncrementHours');
19611         this.time = this.time.add(Date.HOUR, 1);
19612         this.update();
19613         
19614     },
19615     
19616     onDecrementHours: function()
19617     {
19618         Roo.log('onDecrementHours');
19619         this.time = this.time.add(Date.HOUR, -1);
19620         this.update();
19621     },
19622     
19623     onIncrementMinutes: function()
19624     {
19625         Roo.log('onIncrementMinutes');
19626         this.time = this.time.add(Date.MINUTE, 1);
19627         this.update();
19628     },
19629     
19630     onDecrementMinutes: function()
19631     {
19632         Roo.log('onDecrementMinutes');
19633         this.time = this.time.add(Date.MINUTE, -1);
19634         this.update();
19635     },
19636     
19637     onTogglePeriod: function()
19638     {
19639         Roo.log('onTogglePeriod');
19640         this.time = this.time.add(Date.HOUR, 12);
19641         this.update();
19642     }
19643     
19644    
19645 });
19646
19647 Roo.apply(Roo.bootstrap.TimeField,  {
19648     
19649     content : {
19650         tag: 'tbody',
19651         cn: [
19652             {
19653                 tag: 'tr',
19654                 cn: [
19655                 {
19656                     tag: 'td',
19657                     colspan: '7'
19658                 }
19659                 ]
19660             }
19661         ]
19662     },
19663     
19664     footer : {
19665         tag: 'tfoot',
19666         cn: [
19667             {
19668                 tag: 'tr',
19669                 cn: [
19670                 {
19671                     tag: 'th',
19672                     colspan: '7',
19673                     cls: '',
19674                     cn: [
19675                         {
19676                             tag: 'button',
19677                             cls: 'btn btn-info ok',
19678                             html: 'OK'
19679                         }
19680                     ]
19681                 }
19682
19683                 ]
19684             }
19685         ]
19686     }
19687 });
19688
19689 Roo.apply(Roo.bootstrap.TimeField,  {
19690   
19691     template : {
19692         tag: 'div',
19693         cls: 'datepicker dropdown-menu',
19694         cn: [
19695             {
19696                 tag: 'div',
19697                 cls: 'datepicker-time',
19698                 cn: [
19699                 {
19700                     tag: 'table',
19701                     cls: 'table-condensed',
19702                     cn:[
19703                     Roo.bootstrap.TimeField.content,
19704                     Roo.bootstrap.TimeField.footer
19705                     ]
19706                 }
19707                 ]
19708             }
19709         ]
19710     }
19711 });
19712
19713  
19714
19715  /*
19716  * - LGPL
19717  *
19718  * MonthField
19719  * 
19720  */
19721
19722 /**
19723  * @class Roo.bootstrap.MonthField
19724  * @extends Roo.bootstrap.Input
19725  * Bootstrap MonthField class
19726  * 
19727  * @cfg {String} language default en
19728  * 
19729  * @constructor
19730  * Create a new MonthField
19731  * @param {Object} config The config object
19732  */
19733
19734 Roo.bootstrap.MonthField = function(config){
19735     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19736     
19737     this.addEvents({
19738         /**
19739          * @event show
19740          * Fires when this field show.
19741          * @param {Roo.bootstrap.MonthField} this
19742          * @param {Mixed} date The date value
19743          */
19744         show : true,
19745         /**
19746          * @event show
19747          * Fires when this field hide.
19748          * @param {Roo.bootstrap.MonthField} this
19749          * @param {Mixed} date The date value
19750          */
19751         hide : true,
19752         /**
19753          * @event select
19754          * Fires when select a date.
19755          * @param {Roo.bootstrap.MonthField} this
19756          * @param {String} oldvalue The old value
19757          * @param {String} newvalue The new value
19758          */
19759         select : true
19760     });
19761 };
19762
19763 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19764     
19765     onRender: function(ct, position)
19766     {
19767         
19768         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19769         
19770         this.language = this.language || 'en';
19771         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19772         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19773         
19774         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19775         this.isInline = false;
19776         this.isInput = true;
19777         this.component = this.el.select('.add-on', true).first() || false;
19778         this.component = (this.component && this.component.length === 0) ? false : this.component;
19779         this.hasInput = this.component && this.inputEL().length;
19780         
19781         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19782         
19783         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19784         
19785         this.picker().on('mousedown', this.onMousedown, this);
19786         this.picker().on('click', this.onClick, this);
19787         
19788         this.picker().addClass('datepicker-dropdown');
19789         
19790         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19791             v.setStyle('width', '189px');
19792         });
19793         
19794         this.fillMonths();
19795         
19796         this.update();
19797         
19798         if(this.isInline) {
19799             this.show();
19800         }
19801         
19802     },
19803     
19804     setValue: function(v, suppressEvent)
19805     {   
19806         var o = this.getValue();
19807         
19808         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19809         
19810         this.update();
19811
19812         if(suppressEvent !== true){
19813             this.fireEvent('select', this, o, v);
19814         }
19815         
19816     },
19817     
19818     getValue: function()
19819     {
19820         return this.value;
19821     },
19822     
19823     onClick: function(e) 
19824     {
19825         e.stopPropagation();
19826         e.preventDefault();
19827         
19828         var target = e.getTarget();
19829         
19830         if(target.nodeName.toLowerCase() === 'i'){
19831             target = Roo.get(target).dom.parentNode;
19832         }
19833         
19834         var nodeName = target.nodeName;
19835         var className = target.className;
19836         var html = target.innerHTML;
19837         
19838         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19839             return;
19840         }
19841         
19842         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19843         
19844         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19845         
19846         this.hide();
19847                         
19848     },
19849     
19850     picker : function()
19851     {
19852         return this.pickerEl;
19853     },
19854     
19855     fillMonths: function()
19856     {    
19857         var i = 0;
19858         var months = this.picker().select('>.datepicker-months td', true).first();
19859         
19860         months.dom.innerHTML = '';
19861         
19862         while (i < 12) {
19863             var month = {
19864                 tag: 'span',
19865                 cls: 'month',
19866                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19867             };
19868             
19869             months.createChild(month);
19870         }
19871         
19872     },
19873     
19874     update: function()
19875     {
19876         var _this = this;
19877         
19878         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19879             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19880         }
19881         
19882         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19883             e.removeClass('active');
19884             
19885             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19886                 e.addClass('active');
19887             }
19888         })
19889     },
19890     
19891     place: function()
19892     {
19893         if(this.isInline) {
19894             return;
19895         }
19896         
19897         this.picker().removeClass(['bottom', 'top']);
19898         
19899         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19900             /*
19901              * place to the top of element!
19902              *
19903              */
19904             
19905             this.picker().addClass('top');
19906             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19907             
19908             return;
19909         }
19910         
19911         this.picker().addClass('bottom');
19912         
19913         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19914     },
19915     
19916     onFocus : function()
19917     {
19918         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19919         this.show();
19920     },
19921     
19922     onBlur : function()
19923     {
19924         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19925         
19926         var d = this.inputEl().getValue();
19927         
19928         this.setValue(d);
19929                 
19930         this.hide();
19931     },
19932     
19933     show : function()
19934     {
19935         this.picker().show();
19936         this.picker().select('>.datepicker-months', true).first().show();
19937         this.update();
19938         this.place();
19939         
19940         this.fireEvent('show', this, this.date);
19941     },
19942     
19943     hide : function()
19944     {
19945         if(this.isInline) {
19946             return;
19947         }
19948         this.picker().hide();
19949         this.fireEvent('hide', this, this.date);
19950         
19951     },
19952     
19953     onMousedown: function(e)
19954     {
19955         e.stopPropagation();
19956         e.preventDefault();
19957     },
19958     
19959     keyup: function(e)
19960     {
19961         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19962         this.update();
19963     },
19964
19965     fireKey: function(e)
19966     {
19967         if (!this.picker().isVisible()){
19968             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19969                 this.show();
19970             }
19971             return;
19972         }
19973         
19974         var dir;
19975         
19976         switch(e.keyCode){
19977             case 27: // escape
19978                 this.hide();
19979                 e.preventDefault();
19980                 break;
19981             case 37: // left
19982             case 39: // right
19983                 dir = e.keyCode == 37 ? -1 : 1;
19984                 
19985                 this.vIndex = this.vIndex + dir;
19986                 
19987                 if(this.vIndex < 0){
19988                     this.vIndex = 0;
19989                 }
19990                 
19991                 if(this.vIndex > 11){
19992                     this.vIndex = 11;
19993                 }
19994                 
19995                 if(isNaN(this.vIndex)){
19996                     this.vIndex = 0;
19997                 }
19998                 
19999                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20000                 
20001                 break;
20002             case 38: // up
20003             case 40: // down
20004                 
20005                 dir = e.keyCode == 38 ? -1 : 1;
20006                 
20007                 this.vIndex = this.vIndex + dir * 4;
20008                 
20009                 if(this.vIndex < 0){
20010                     this.vIndex = 0;
20011                 }
20012                 
20013                 if(this.vIndex > 11){
20014                     this.vIndex = 11;
20015                 }
20016                 
20017                 if(isNaN(this.vIndex)){
20018                     this.vIndex = 0;
20019                 }
20020                 
20021                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20022                 break;
20023                 
20024             case 13: // enter
20025                 
20026                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20027                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20028                 }
20029                 
20030                 this.hide();
20031                 e.preventDefault();
20032                 break;
20033             case 9: // tab
20034                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20035                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20036                 }
20037                 this.hide();
20038                 break;
20039             case 16: // shift
20040             case 17: // ctrl
20041             case 18: // alt
20042                 break;
20043             default :
20044                 this.hide();
20045                 
20046         }
20047     },
20048     
20049     remove: function() 
20050     {
20051         this.picker().remove();
20052     }
20053    
20054 });
20055
20056 Roo.apply(Roo.bootstrap.MonthField,  {
20057     
20058     content : {
20059         tag: 'tbody',
20060         cn: [
20061         {
20062             tag: 'tr',
20063             cn: [
20064             {
20065                 tag: 'td',
20066                 colspan: '7'
20067             }
20068             ]
20069         }
20070         ]
20071     },
20072     
20073     dates:{
20074         en: {
20075             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20076             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20077         }
20078     }
20079 });
20080
20081 Roo.apply(Roo.bootstrap.MonthField,  {
20082   
20083     template : {
20084         tag: 'div',
20085         cls: 'datepicker dropdown-menu roo-dynamic',
20086         cn: [
20087             {
20088                 tag: 'div',
20089                 cls: 'datepicker-months',
20090                 cn: [
20091                 {
20092                     tag: 'table',
20093                     cls: 'table-condensed',
20094                     cn:[
20095                         Roo.bootstrap.DateField.content
20096                     ]
20097                 }
20098                 ]
20099             }
20100         ]
20101     }
20102 });
20103
20104  
20105
20106  
20107  /*
20108  * - LGPL
20109  *
20110  * CheckBox
20111  * 
20112  */
20113
20114 /**
20115  * @class Roo.bootstrap.CheckBox
20116  * @extends Roo.bootstrap.Input
20117  * Bootstrap CheckBox class
20118  * 
20119  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20120  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20121  * @cfg {String} boxLabel The text that appears beside the checkbox
20122  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20123  * @cfg {Boolean} checked initnal the element
20124  * @cfg {Boolean} inline inline the element (default false)
20125  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20126  * 
20127  * @constructor
20128  * Create a new CheckBox
20129  * @param {Object} config The config object
20130  */
20131
20132 Roo.bootstrap.CheckBox = function(config){
20133     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20134    
20135     this.addEvents({
20136         /**
20137         * @event check
20138         * Fires when the element is checked or unchecked.
20139         * @param {Roo.bootstrap.CheckBox} this This input
20140         * @param {Boolean} checked The new checked value
20141         */
20142        check : true
20143     });
20144     
20145 };
20146
20147 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20148   
20149     inputType: 'checkbox',
20150     inputValue: 1,
20151     valueOff: 0,
20152     boxLabel: false,
20153     checked: false,
20154     weight : false,
20155     inline: false,
20156     
20157     getAutoCreate : function()
20158     {
20159         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20160         
20161         var id = Roo.id();
20162         
20163         var cfg = {};
20164         
20165         cfg.cls = 'form-group ' + this.inputType; //input-group
20166         
20167         if(this.inline){
20168             cfg.cls += ' ' + this.inputType + '-inline';
20169         }
20170         
20171         var input =  {
20172             tag: 'input',
20173             id : id,
20174             type : this.inputType,
20175             value : this.inputValue,
20176             cls : 'roo-' + this.inputType, //'form-box',
20177             placeholder : this.placeholder || ''
20178             
20179         };
20180         
20181         if(this.inputType != 'radio'){
20182             var hidden =  {
20183                 tag: 'input',
20184                 type : 'hidden',
20185                 cls : 'roo-hidden-value',
20186                 value : this.checked ? this.valueOff : this.inputValue
20187             };
20188         }
20189         
20190             
20191         if (this.weight) { // Validity check?
20192             cfg.cls += " " + this.inputType + "-" + this.weight;
20193         }
20194         
20195         if (this.disabled) {
20196             input.disabled=true;
20197         }
20198         
20199         if(this.checked){
20200             input.checked = this.checked;
20201             
20202         }
20203         
20204         
20205         if (this.name) {
20206             
20207             input.name = this.name;
20208             
20209             if(this.inputType != 'radio'){
20210                 hidden.name = this.name;
20211                 input.name = '_hidden_' + this.name;
20212             }
20213         }
20214         
20215         if (this.size) {
20216             input.cls += ' input-' + this.size;
20217         }
20218         
20219         var settings=this;
20220         
20221         ['xs','sm','md','lg'].map(function(size){
20222             if (settings[size]) {
20223                 cfg.cls += ' col-' + size + '-' + settings[size];
20224             }
20225         });
20226         
20227         var inputblock = input;
20228          
20229         if (this.before || this.after) {
20230             
20231             inputblock = {
20232                 cls : 'input-group',
20233                 cn :  [] 
20234             };
20235             
20236             if (this.before) {
20237                 inputblock.cn.push({
20238                     tag :'span',
20239                     cls : 'input-group-addon',
20240                     html : this.before
20241                 });
20242             }
20243             
20244             inputblock.cn.push(input);
20245             
20246             if(this.inputType != 'radio'){
20247                 inputblock.cn.push(hidden);
20248             }
20249             
20250             if (this.after) {
20251                 inputblock.cn.push({
20252                     tag :'span',
20253                     cls : 'input-group-addon',
20254                     html : this.after
20255                 });
20256             }
20257             
20258         }
20259         
20260         if (align ==='left' && this.fieldLabel.length) {
20261 //                Roo.log("left and has label");
20262             cfg.cn = [
20263                 {
20264                     tag: 'label',
20265                     'for' :  id,
20266                     cls : 'control-label',
20267                     html : this.fieldLabel
20268
20269                 },
20270                 {
20271                     cls : "", 
20272                     cn: [
20273                         inputblock
20274                     ]
20275                 }
20276             ];
20277             
20278             if(this.labelWidth > 12){
20279                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20280             }
20281             
20282             if(this.labelWidth < 13 && this.labelmd == 0){
20283                 this.labelmd = this.labelWidth;
20284             }
20285             
20286             if(this.labellg > 0){
20287                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20288                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20289             }
20290             
20291             if(this.labelmd > 0){
20292                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20293                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20294             }
20295             
20296             if(this.labelsm > 0){
20297                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20298                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20299             }
20300             
20301             if(this.labelxs > 0){
20302                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20303                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20304             }
20305             
20306         } else if ( this.fieldLabel.length) {
20307 //                Roo.log(" label");
20308                 cfg.cn = [
20309                    
20310                     {
20311                         tag: this.boxLabel ? 'span' : 'label',
20312                         'for': id,
20313                         cls: 'control-label box-input-label',
20314                         //cls : 'input-group-addon',
20315                         html : this.fieldLabel
20316                         
20317                     },
20318                     
20319                     inputblock
20320                     
20321                 ];
20322
20323         } else {
20324             
20325 //                Roo.log(" no label && no align");
20326                 cfg.cn = [  inputblock ] ;
20327                 
20328                 
20329         }
20330         
20331         if(this.boxLabel){
20332              var boxLabelCfg = {
20333                 tag: 'label',
20334                 //'for': id, // box label is handled by onclick - so no for...
20335                 cls: 'box-label',
20336                 html: this.boxLabel
20337             };
20338             
20339             if(this.tooltip){
20340                 boxLabelCfg.tooltip = this.tooltip;
20341             }
20342              
20343             cfg.cn.push(boxLabelCfg);
20344         }
20345         
20346         if(this.inputType != 'radio'){
20347             cfg.cn.push(hidden);
20348         }
20349         
20350         return cfg;
20351         
20352     },
20353     
20354     /**
20355      * return the real input element.
20356      */
20357     inputEl: function ()
20358     {
20359         return this.el.select('input.roo-' + this.inputType,true).first();
20360     },
20361     hiddenEl: function ()
20362     {
20363         return this.el.select('input.roo-hidden-value',true).first();
20364     },
20365     
20366     labelEl: function()
20367     {
20368         return this.el.select('label.control-label',true).first();
20369     },
20370     /* depricated... */
20371     
20372     label: function()
20373     {
20374         return this.labelEl();
20375     },
20376     
20377     boxLabelEl: function()
20378     {
20379         return this.el.select('label.box-label',true).first();
20380     },
20381     
20382     initEvents : function()
20383     {
20384 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20385         
20386         this.inputEl().on('click', this.onClick,  this);
20387         
20388         if (this.boxLabel) { 
20389             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20390         }
20391         
20392         this.startValue = this.getValue();
20393         
20394         if(this.groupId){
20395             Roo.bootstrap.CheckBox.register(this);
20396         }
20397     },
20398     
20399     onClick : function()
20400     {   
20401         this.setChecked(!this.checked);
20402     },
20403     
20404     setChecked : function(state,suppressEvent)
20405     {
20406         this.startValue = this.getValue();
20407
20408         if(this.inputType == 'radio'){
20409             
20410             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20411                 e.dom.checked = false;
20412             });
20413             
20414             this.inputEl().dom.checked = true;
20415             
20416             this.inputEl().dom.value = this.inputValue;
20417             
20418             if(suppressEvent !== true){
20419                 this.fireEvent('check', this, true);
20420             }
20421             
20422             this.validate();
20423             
20424             return;
20425         }
20426         
20427         this.checked = state;
20428         
20429         this.inputEl().dom.checked = state;
20430         
20431         
20432         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20433         
20434         if(suppressEvent !== true){
20435             this.fireEvent('check', this, state);
20436         }
20437         
20438         this.validate();
20439     },
20440     
20441     getValue : function()
20442     {
20443         if(this.inputType == 'radio'){
20444             return this.getGroupValue();
20445         }
20446         
20447         return this.hiddenEl().dom.value;
20448         
20449     },
20450     
20451     getGroupValue : function()
20452     {
20453         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20454             return '';
20455         }
20456         
20457         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20458     },
20459     
20460     setValue : function(v,suppressEvent)
20461     {
20462         if(this.inputType == 'radio'){
20463             this.setGroupValue(v, suppressEvent);
20464             return;
20465         }
20466         
20467         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20468         
20469         this.validate();
20470     },
20471     
20472     setGroupValue : function(v, suppressEvent)
20473     {
20474         this.startValue = this.getValue();
20475         
20476         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20477             e.dom.checked = false;
20478             
20479             if(e.dom.value == v){
20480                 e.dom.checked = true;
20481             }
20482         });
20483         
20484         if(suppressEvent !== true){
20485             this.fireEvent('check', this, true);
20486         }
20487
20488         this.validate();
20489         
20490         return;
20491     },
20492     
20493     validate : function()
20494     {
20495         if(
20496                 this.disabled || 
20497                 (this.inputType == 'radio' && this.validateRadio()) ||
20498                 (this.inputType == 'checkbox' && this.validateCheckbox())
20499         ){
20500             this.markValid();
20501             return true;
20502         }
20503         
20504         this.markInvalid();
20505         return false;
20506     },
20507     
20508     validateRadio : function()
20509     {
20510         if(this.allowBlank){
20511             return true;
20512         }
20513         
20514         var valid = false;
20515         
20516         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20517             if(!e.dom.checked){
20518                 return;
20519             }
20520             
20521             valid = true;
20522             
20523             return false;
20524         });
20525         
20526         return valid;
20527     },
20528     
20529     validateCheckbox : function()
20530     {
20531         if(!this.groupId){
20532             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20533             //return (this.getValue() == this.inputValue) ? true : false;
20534         }
20535         
20536         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20537         
20538         if(!group){
20539             return false;
20540         }
20541         
20542         var r = false;
20543         
20544         for(var i in group){
20545             if(r){
20546                 break;
20547             }
20548             
20549             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20550         }
20551         
20552         return r;
20553     },
20554     
20555     /**
20556      * Mark this field as valid
20557      */
20558     markValid : function()
20559     {
20560         var _this = this;
20561         
20562         this.fireEvent('valid', this);
20563         
20564         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20565         
20566         if(this.groupId){
20567             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20568         }
20569         
20570         if(label){
20571             label.markValid();
20572         }
20573
20574         if(this.inputType == 'radio'){
20575             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20576                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20577                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20578             });
20579             
20580             return;
20581         }
20582
20583         if(!this.groupId){
20584             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20585             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20586             return;
20587         }
20588         
20589         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20590         
20591         if(!group){
20592             return;
20593         }
20594         
20595         for(var i in group){
20596             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20597             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20598         }
20599     },
20600     
20601      /**
20602      * Mark this field as invalid
20603      * @param {String} msg The validation message
20604      */
20605     markInvalid : function(msg)
20606     {
20607         if(this.allowBlank){
20608             return;
20609         }
20610         
20611         var _this = this;
20612         
20613         this.fireEvent('invalid', this, msg);
20614         
20615         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20616         
20617         if(this.groupId){
20618             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20619         }
20620         
20621         if(label){
20622             label.markInvalid();
20623         }
20624             
20625         if(this.inputType == 'radio'){
20626             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20627                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20628                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20629             });
20630             
20631             return;
20632         }
20633         
20634         if(!this.groupId){
20635             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20636             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20637             return;
20638         }
20639         
20640         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20641         
20642         if(!group){
20643             return;
20644         }
20645         
20646         for(var i in group){
20647             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20648             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20649         }
20650         
20651     },
20652     
20653     clearInvalid : function()
20654     {
20655         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20656         
20657         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20658         
20659         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20660         
20661         if (label) {
20662             label.iconEl.removeClass(label.validClass);
20663             label.iconEl.removeClass(label.invalidClass);
20664         }
20665     },
20666     
20667     disable : function()
20668     {
20669         if(this.inputType != 'radio'){
20670             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20671             return;
20672         }
20673         
20674         var _this = this;
20675         
20676         if(this.rendered){
20677             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20678                 _this.getActionEl().addClass(this.disabledClass);
20679                 e.dom.disabled = true;
20680             });
20681         }
20682         
20683         this.disabled = true;
20684         this.fireEvent("disable", this);
20685         return this;
20686     },
20687
20688     enable : function()
20689     {
20690         if(this.inputType != 'radio'){
20691             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20692             return;
20693         }
20694         
20695         var _this = this;
20696         
20697         if(this.rendered){
20698             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20699                 _this.getActionEl().removeClass(this.disabledClass);
20700                 e.dom.disabled = false;
20701             });
20702         }
20703         
20704         this.disabled = false;
20705         this.fireEvent("enable", this);
20706         return this;
20707     }
20708
20709 });
20710
20711 Roo.apply(Roo.bootstrap.CheckBox, {
20712     
20713     groups: {},
20714     
20715      /**
20716     * register a CheckBox Group
20717     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20718     */
20719     register : function(checkbox)
20720     {
20721         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20722             this.groups[checkbox.groupId] = {};
20723         }
20724         
20725         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20726             return;
20727         }
20728         
20729         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20730         
20731     },
20732     /**
20733     * fetch a CheckBox Group based on the group ID
20734     * @param {string} the group ID
20735     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20736     */
20737     get: function(groupId) {
20738         if (typeof(this.groups[groupId]) == 'undefined') {
20739             return false;
20740         }
20741         
20742         return this.groups[groupId] ;
20743     }
20744     
20745     
20746 });
20747 /*
20748  * - LGPL
20749  *
20750  * RadioItem
20751  * 
20752  */
20753
20754 /**
20755  * @class Roo.bootstrap.Radio
20756  * @extends Roo.bootstrap.Component
20757  * Bootstrap Radio class
20758  * @cfg {String} boxLabel - the label associated
20759  * @cfg {String} value - the value of radio
20760  * 
20761  * @constructor
20762  * Create a new Radio
20763  * @param {Object} config The config object
20764  */
20765 Roo.bootstrap.Radio = function(config){
20766     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20767     
20768 };
20769
20770 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20771     
20772     boxLabel : '',
20773     
20774     value : '',
20775     
20776     getAutoCreate : function()
20777     {
20778         var cfg = {
20779             tag : 'div',
20780             cls : 'form-group radio',
20781             cn : [
20782                 {
20783                     tag : 'label',
20784                     cls : 'box-label',
20785                     html : this.boxLabel
20786                 }
20787             ]
20788         };
20789         
20790         return cfg;
20791     },
20792     
20793     initEvents : function() 
20794     {
20795         this.parent().register(this);
20796         
20797         this.el.on('click', this.onClick, this);
20798         
20799     },
20800     
20801     onClick : function()
20802     {
20803         this.setChecked(true);
20804     },
20805     
20806     setChecked : function(state, suppressEvent)
20807     {
20808         this.parent().setValue(this.value, suppressEvent);
20809         
20810     },
20811     
20812     setBoxLabel : function(v)
20813     {
20814         this.boxLabel = v;
20815         
20816         if(this.rendered){
20817             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20818         }
20819     }
20820     
20821 });
20822  
20823
20824  /*
20825  * - LGPL
20826  *
20827  * Input
20828  * 
20829  */
20830
20831 /**
20832  * @class Roo.bootstrap.SecurePass
20833  * @extends Roo.bootstrap.Input
20834  * Bootstrap SecurePass class
20835  *
20836  * 
20837  * @constructor
20838  * Create a new SecurePass
20839  * @param {Object} config The config object
20840  */
20841  
20842 Roo.bootstrap.SecurePass = function (config) {
20843     // these go here, so the translation tool can replace them..
20844     this.errors = {
20845         PwdEmpty: "Please type a password, and then retype it to confirm.",
20846         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20847         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20848         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20849         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20850         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20851         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20852         TooWeak: "Your password is Too Weak."
20853     },
20854     this.meterLabel = "Password strength:";
20855     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20856     this.meterClass = [
20857         "roo-password-meter-tooweak", 
20858         "roo-password-meter-weak", 
20859         "roo-password-meter-medium", 
20860         "roo-password-meter-strong", 
20861         "roo-password-meter-grey"
20862     ];
20863     
20864     this.errors = {};
20865     
20866     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20867 }
20868
20869 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20870     /**
20871      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20872      * {
20873      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20874      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20875      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20876      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20877      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20878      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20879      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20880      * })
20881      */
20882     // private
20883     
20884     meterWidth: 300,
20885     errorMsg :'',    
20886     errors: false,
20887     imageRoot: '/',
20888     /**
20889      * @cfg {String/Object} Label for the strength meter (defaults to
20890      * 'Password strength:')
20891      */
20892     // private
20893     meterLabel: '',
20894     /**
20895      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20896      * ['Weak', 'Medium', 'Strong'])
20897      */
20898     // private    
20899     pwdStrengths: false,    
20900     // private
20901     strength: 0,
20902     // private
20903     _lastPwd: null,
20904     // private
20905     kCapitalLetter: 0,
20906     kSmallLetter: 1,
20907     kDigit: 2,
20908     kPunctuation: 3,
20909     
20910     insecure: false,
20911     // private
20912     initEvents: function ()
20913     {
20914         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20915
20916         if (this.el.is('input[type=password]') && Roo.isSafari) {
20917             this.el.on('keydown', this.SafariOnKeyDown, this);
20918         }
20919
20920         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20921     },
20922     // private
20923     onRender: function (ct, position)
20924     {
20925         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20926         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20927         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20928
20929         this.trigger.createChild({
20930                    cn: [
20931                     {
20932                     //id: 'PwdMeter',
20933                     tag: 'div',
20934                     cls: 'roo-password-meter-grey col-xs-12',
20935                     style: {
20936                         //width: 0,
20937                         //width: this.meterWidth + 'px'                                                
20938                         }
20939                     },
20940                     {                            
20941                          cls: 'roo-password-meter-text'                          
20942                     }
20943                 ]            
20944         });
20945
20946          
20947         if (this.hideTrigger) {
20948             this.trigger.setDisplayed(false);
20949         }
20950         this.setSize(this.width || '', this.height || '');
20951     },
20952     // private
20953     onDestroy: function ()
20954     {
20955         if (this.trigger) {
20956             this.trigger.removeAllListeners();
20957             this.trigger.remove();
20958         }
20959         if (this.wrap) {
20960             this.wrap.remove();
20961         }
20962         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20963     },
20964     // private
20965     checkStrength: function ()
20966     {
20967         var pwd = this.inputEl().getValue();
20968         if (pwd == this._lastPwd) {
20969             return;
20970         }
20971
20972         var strength;
20973         if (this.ClientSideStrongPassword(pwd)) {
20974             strength = 3;
20975         } else if (this.ClientSideMediumPassword(pwd)) {
20976             strength = 2;
20977         } else if (this.ClientSideWeakPassword(pwd)) {
20978             strength = 1;
20979         } else {
20980             strength = 0;
20981         }
20982         
20983         Roo.log('strength1: ' + strength);
20984         
20985         //var pm = this.trigger.child('div/div/div').dom;
20986         var pm = this.trigger.child('div/div');
20987         pm.removeClass(this.meterClass);
20988         pm.addClass(this.meterClass[strength]);
20989                 
20990         
20991         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20992                 
20993         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20994         
20995         this._lastPwd = pwd;
20996     },
20997     reset: function ()
20998     {
20999         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21000         
21001         this._lastPwd = '';
21002         
21003         var pm = this.trigger.child('div/div');
21004         pm.removeClass(this.meterClass);
21005         pm.addClass('roo-password-meter-grey');        
21006         
21007         
21008         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21009         
21010         pt.innerHTML = '';
21011         this.inputEl().dom.type='password';
21012     },
21013     // private
21014     validateValue: function (value)
21015     {
21016         
21017         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21018             return false;
21019         }
21020         if (value.length == 0) {
21021             if (this.allowBlank) {
21022                 this.clearInvalid();
21023                 return true;
21024             }
21025
21026             this.markInvalid(this.errors.PwdEmpty);
21027             this.errorMsg = this.errors.PwdEmpty;
21028             return false;
21029         }
21030         
21031         if(this.insecure){
21032             return true;
21033         }
21034         
21035         if ('[\x21-\x7e]*'.match(value)) {
21036             this.markInvalid(this.errors.PwdBadChar);
21037             this.errorMsg = this.errors.PwdBadChar;
21038             return false;
21039         }
21040         if (value.length < 6) {
21041             this.markInvalid(this.errors.PwdShort);
21042             this.errorMsg = this.errors.PwdShort;
21043             return false;
21044         }
21045         if (value.length > 16) {
21046             this.markInvalid(this.errors.PwdLong);
21047             this.errorMsg = this.errors.PwdLong;
21048             return false;
21049         }
21050         var strength;
21051         if (this.ClientSideStrongPassword(value)) {
21052             strength = 3;
21053         } else if (this.ClientSideMediumPassword(value)) {
21054             strength = 2;
21055         } else if (this.ClientSideWeakPassword(value)) {
21056             strength = 1;
21057         } else {
21058             strength = 0;
21059         }
21060
21061         
21062         if (strength < 2) {
21063             //this.markInvalid(this.errors.TooWeak);
21064             this.errorMsg = this.errors.TooWeak;
21065             //return false;
21066         }
21067         
21068         
21069         console.log('strength2: ' + strength);
21070         
21071         //var pm = this.trigger.child('div/div/div').dom;
21072         
21073         var pm = this.trigger.child('div/div');
21074         pm.removeClass(this.meterClass);
21075         pm.addClass(this.meterClass[strength]);
21076                 
21077         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21078                 
21079         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21080         
21081         this.errorMsg = ''; 
21082         return true;
21083     },
21084     // private
21085     CharacterSetChecks: function (type)
21086     {
21087         this.type = type;
21088         this.fResult = false;
21089     },
21090     // private
21091     isctype: function (character, type)
21092     {
21093         switch (type) {  
21094             case this.kCapitalLetter:
21095                 if (character >= 'A' && character <= 'Z') {
21096                     return true;
21097                 }
21098                 break;
21099             
21100             case this.kSmallLetter:
21101                 if (character >= 'a' && character <= 'z') {
21102                     return true;
21103                 }
21104                 break;
21105             
21106             case this.kDigit:
21107                 if (character >= '0' && character <= '9') {
21108                     return true;
21109                 }
21110                 break;
21111             
21112             case this.kPunctuation:
21113                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21114                     return true;
21115                 }
21116                 break;
21117             
21118             default:
21119                 return false;
21120         }
21121
21122     },
21123     // private
21124     IsLongEnough: function (pwd, size)
21125     {
21126         return !(pwd == null || isNaN(size) || pwd.length < size);
21127     },
21128     // private
21129     SpansEnoughCharacterSets: function (word, nb)
21130     {
21131         if (!this.IsLongEnough(word, nb))
21132         {
21133             return false;
21134         }
21135
21136         var characterSetChecks = new Array(
21137             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21138             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21139         );
21140         
21141         for (var index = 0; index < word.length; ++index) {
21142             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21143                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21144                     characterSetChecks[nCharSet].fResult = true;
21145                     break;
21146                 }
21147             }
21148         }
21149
21150         var nCharSets = 0;
21151         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21152             if (characterSetChecks[nCharSet].fResult) {
21153                 ++nCharSets;
21154             }
21155         }
21156
21157         if (nCharSets < nb) {
21158             return false;
21159         }
21160         return true;
21161     },
21162     // private
21163     ClientSideStrongPassword: function (pwd)
21164     {
21165         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21166     },
21167     // private
21168     ClientSideMediumPassword: function (pwd)
21169     {
21170         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21171     },
21172     // private
21173     ClientSideWeakPassword: function (pwd)
21174     {
21175         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21176     }
21177           
21178 })//<script type="text/javascript">
21179
21180 /*
21181  * Based  Ext JS Library 1.1.1
21182  * Copyright(c) 2006-2007, Ext JS, LLC.
21183  * LGPL
21184  *
21185  */
21186  
21187 /**
21188  * @class Roo.HtmlEditorCore
21189  * @extends Roo.Component
21190  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21191  *
21192  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21193  */
21194
21195 Roo.HtmlEditorCore = function(config){
21196     
21197     
21198     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21199     
21200     
21201     this.addEvents({
21202         /**
21203          * @event initialize
21204          * Fires when the editor is fully initialized (including the iframe)
21205          * @param {Roo.HtmlEditorCore} this
21206          */
21207         initialize: true,
21208         /**
21209          * @event activate
21210          * Fires when the editor is first receives the focus. Any insertion must wait
21211          * until after this event.
21212          * @param {Roo.HtmlEditorCore} this
21213          */
21214         activate: true,
21215          /**
21216          * @event beforesync
21217          * Fires before the textarea is updated with content from the editor iframe. Return false
21218          * to cancel the sync.
21219          * @param {Roo.HtmlEditorCore} this
21220          * @param {String} html
21221          */
21222         beforesync: true,
21223          /**
21224          * @event beforepush
21225          * Fires before the iframe editor is updated with content from the textarea. Return false
21226          * to cancel the push.
21227          * @param {Roo.HtmlEditorCore} this
21228          * @param {String} html
21229          */
21230         beforepush: true,
21231          /**
21232          * @event sync
21233          * Fires when the textarea is updated with content from the editor iframe.
21234          * @param {Roo.HtmlEditorCore} this
21235          * @param {String} html
21236          */
21237         sync: true,
21238          /**
21239          * @event push
21240          * Fires when the iframe editor is updated with content from the textarea.
21241          * @param {Roo.HtmlEditorCore} this
21242          * @param {String} html
21243          */
21244         push: true,
21245         
21246         /**
21247          * @event editorevent
21248          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21249          * @param {Roo.HtmlEditorCore} this
21250          */
21251         editorevent: true
21252         
21253     });
21254     
21255     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21256     
21257     // defaults : white / black...
21258     this.applyBlacklists();
21259     
21260     
21261     
21262 };
21263
21264
21265 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21266
21267
21268      /**
21269      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21270      */
21271     
21272     owner : false,
21273     
21274      /**
21275      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21276      *                        Roo.resizable.
21277      */
21278     resizable : false,
21279      /**
21280      * @cfg {Number} height (in pixels)
21281      */   
21282     height: 300,
21283    /**
21284      * @cfg {Number} width (in pixels)
21285      */   
21286     width: 500,
21287     
21288     /**
21289      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21290      * 
21291      */
21292     stylesheets: false,
21293     
21294     // id of frame..
21295     frameId: false,
21296     
21297     // private properties
21298     validationEvent : false,
21299     deferHeight: true,
21300     initialized : false,
21301     activated : false,
21302     sourceEditMode : false,
21303     onFocus : Roo.emptyFn,
21304     iframePad:3,
21305     hideMode:'offsets',
21306     
21307     clearUp: true,
21308     
21309     // blacklist + whitelisted elements..
21310     black: false,
21311     white: false,
21312      
21313     bodyCls : '',
21314
21315     /**
21316      * Protected method that will not generally be called directly. It
21317      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21318      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21319      */
21320     getDocMarkup : function(){
21321         // body styles..
21322         var st = '';
21323         
21324         // inherit styels from page...?? 
21325         if (this.stylesheets === false) {
21326             
21327             Roo.get(document.head).select('style').each(function(node) {
21328                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21329             });
21330             
21331             Roo.get(document.head).select('link').each(function(node) { 
21332                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21333             });
21334             
21335         } else if (!this.stylesheets.length) {
21336                 // simple..
21337                 st = '<style type="text/css">' +
21338                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21339                    '</style>';
21340         } else { 
21341             st = '<style type="text/css">' +
21342                     this.stylesheets +
21343                 '</style>';
21344         }
21345         
21346         st +=  '<style type="text/css">' +
21347             'IMG { cursor: pointer } ' +
21348         '</style>';
21349
21350         var cls = 'roo-htmleditor-body';
21351         
21352         if(this.bodyCls.length){
21353             cls += ' ' + this.bodyCls;
21354         }
21355         
21356         return '<html><head>' + st  +
21357             //<style type="text/css">' +
21358             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21359             //'</style>' +
21360             ' </head><body class="' +  cls + '"></body></html>';
21361     },
21362
21363     // private
21364     onRender : function(ct, position)
21365     {
21366         var _t = this;
21367         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21368         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21369         
21370         
21371         this.el.dom.style.border = '0 none';
21372         this.el.dom.setAttribute('tabIndex', -1);
21373         this.el.addClass('x-hidden hide');
21374         
21375         
21376         
21377         if(Roo.isIE){ // fix IE 1px bogus margin
21378             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21379         }
21380        
21381         
21382         this.frameId = Roo.id();
21383         
21384          
21385         
21386         var iframe = this.owner.wrap.createChild({
21387             tag: 'iframe',
21388             cls: 'form-control', // bootstrap..
21389             id: this.frameId,
21390             name: this.frameId,
21391             frameBorder : 'no',
21392             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21393         }, this.el
21394         );
21395         
21396         
21397         this.iframe = iframe.dom;
21398
21399          this.assignDocWin();
21400         
21401         this.doc.designMode = 'on';
21402        
21403         this.doc.open();
21404         this.doc.write(this.getDocMarkup());
21405         this.doc.close();
21406
21407         
21408         var task = { // must defer to wait for browser to be ready
21409             run : function(){
21410                 //console.log("run task?" + this.doc.readyState);
21411                 this.assignDocWin();
21412                 if(this.doc.body || this.doc.readyState == 'complete'){
21413                     try {
21414                         this.doc.designMode="on";
21415                     } catch (e) {
21416                         return;
21417                     }
21418                     Roo.TaskMgr.stop(task);
21419                     this.initEditor.defer(10, this);
21420                 }
21421             },
21422             interval : 10,
21423             duration: 10000,
21424             scope: this
21425         };
21426         Roo.TaskMgr.start(task);
21427
21428     },
21429
21430     // private
21431     onResize : function(w, h)
21432     {
21433          Roo.log('resize: ' +w + ',' + h );
21434         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21435         if(!this.iframe){
21436             return;
21437         }
21438         if(typeof w == 'number'){
21439             
21440             this.iframe.style.width = w + 'px';
21441         }
21442         if(typeof h == 'number'){
21443             
21444             this.iframe.style.height = h + 'px';
21445             if(this.doc){
21446                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21447             }
21448         }
21449         
21450     },
21451
21452     /**
21453      * Toggles the editor between standard and source edit mode.
21454      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21455      */
21456     toggleSourceEdit : function(sourceEditMode){
21457         
21458         this.sourceEditMode = sourceEditMode === true;
21459         
21460         if(this.sourceEditMode){
21461  
21462             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21463             
21464         }else{
21465             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21466             //this.iframe.className = '';
21467             this.deferFocus();
21468         }
21469         //this.setSize(this.owner.wrap.getSize());
21470         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21471     },
21472
21473     
21474   
21475
21476     /**
21477      * Protected method that will not generally be called directly. If you need/want
21478      * custom HTML cleanup, this is the method you should override.
21479      * @param {String} html The HTML to be cleaned
21480      * return {String} The cleaned HTML
21481      */
21482     cleanHtml : function(html){
21483         html = String(html);
21484         if(html.length > 5){
21485             if(Roo.isSafari){ // strip safari nonsense
21486                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21487             }
21488         }
21489         if(html == '&nbsp;'){
21490             html = '';
21491         }
21492         return html;
21493     },
21494
21495     /**
21496      * HTML Editor -> Textarea
21497      * Protected method that will not generally be called directly. Syncs the contents
21498      * of the editor iframe with the textarea.
21499      */
21500     syncValue : function(){
21501         if(this.initialized){
21502             var bd = (this.doc.body || this.doc.documentElement);
21503             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21504             var html = bd.innerHTML;
21505             if(Roo.isSafari){
21506                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21507                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21508                 if(m && m[1]){
21509                     html = '<div style="'+m[0]+'">' + html + '</div>';
21510                 }
21511             }
21512             html = this.cleanHtml(html);
21513             // fix up the special chars.. normaly like back quotes in word...
21514             // however we do not want to do this with chinese..
21515             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21516                 var cc = b.charCodeAt();
21517                 if (
21518                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21519                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21520                     (cc >= 0xf900 && cc < 0xfb00 )
21521                 ) {
21522                         return b;
21523                 }
21524                 return "&#"+cc+";" 
21525             });
21526             if(this.owner.fireEvent('beforesync', this, html) !== false){
21527                 this.el.dom.value = html;
21528                 this.owner.fireEvent('sync', this, html);
21529             }
21530         }
21531     },
21532
21533     /**
21534      * Protected method that will not generally be called directly. Pushes the value of the textarea
21535      * into the iframe editor.
21536      */
21537     pushValue : function(){
21538         if(this.initialized){
21539             var v = this.el.dom.value.trim();
21540             
21541 //            if(v.length < 1){
21542 //                v = '&#160;';
21543 //            }
21544             
21545             if(this.owner.fireEvent('beforepush', this, v) !== false){
21546                 var d = (this.doc.body || this.doc.documentElement);
21547                 d.innerHTML = v;
21548                 this.cleanUpPaste();
21549                 this.el.dom.value = d.innerHTML;
21550                 this.owner.fireEvent('push', this, v);
21551             }
21552         }
21553     },
21554
21555     // private
21556     deferFocus : function(){
21557         this.focus.defer(10, this);
21558     },
21559
21560     // doc'ed in Field
21561     focus : function(){
21562         if(this.win && !this.sourceEditMode){
21563             this.win.focus();
21564         }else{
21565             this.el.focus();
21566         }
21567     },
21568     
21569     assignDocWin: function()
21570     {
21571         var iframe = this.iframe;
21572         
21573          if(Roo.isIE){
21574             this.doc = iframe.contentWindow.document;
21575             this.win = iframe.contentWindow;
21576         } else {
21577 //            if (!Roo.get(this.frameId)) {
21578 //                return;
21579 //            }
21580 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21581 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21582             
21583             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21584                 return;
21585             }
21586             
21587             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21588             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21589         }
21590     },
21591     
21592     // private
21593     initEditor : function(){
21594         //console.log("INIT EDITOR");
21595         this.assignDocWin();
21596         
21597         
21598         
21599         this.doc.designMode="on";
21600         this.doc.open();
21601         this.doc.write(this.getDocMarkup());
21602         this.doc.close();
21603         
21604         var dbody = (this.doc.body || this.doc.documentElement);
21605         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21606         // this copies styles from the containing element into thsi one..
21607         // not sure why we need all of this..
21608         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21609         
21610         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21611         //ss['background-attachment'] = 'fixed'; // w3c
21612         dbody.bgProperties = 'fixed'; // ie
21613         //Roo.DomHelper.applyStyles(dbody, ss);
21614         Roo.EventManager.on(this.doc, {
21615             //'mousedown': this.onEditorEvent,
21616             'mouseup': this.onEditorEvent,
21617             'dblclick': this.onEditorEvent,
21618             'click': this.onEditorEvent,
21619             'keyup': this.onEditorEvent,
21620             buffer:100,
21621             scope: this
21622         });
21623         if(Roo.isGecko){
21624             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21625         }
21626         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21627             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21628         }
21629         this.initialized = true;
21630
21631         this.owner.fireEvent('initialize', this);
21632         this.pushValue();
21633     },
21634
21635     // private
21636     onDestroy : function(){
21637         
21638         
21639         
21640         if(this.rendered){
21641             
21642             //for (var i =0; i < this.toolbars.length;i++) {
21643             //    // fixme - ask toolbars for heights?
21644             //    this.toolbars[i].onDestroy();
21645            // }
21646             
21647             //this.wrap.dom.innerHTML = '';
21648             //this.wrap.remove();
21649         }
21650     },
21651
21652     // private
21653     onFirstFocus : function(){
21654         
21655         this.assignDocWin();
21656         
21657         
21658         this.activated = true;
21659          
21660     
21661         if(Roo.isGecko){ // prevent silly gecko errors
21662             this.win.focus();
21663             var s = this.win.getSelection();
21664             if(!s.focusNode || s.focusNode.nodeType != 3){
21665                 var r = s.getRangeAt(0);
21666                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21667                 r.collapse(true);
21668                 this.deferFocus();
21669             }
21670             try{
21671                 this.execCmd('useCSS', true);
21672                 this.execCmd('styleWithCSS', false);
21673             }catch(e){}
21674         }
21675         this.owner.fireEvent('activate', this);
21676     },
21677
21678     // private
21679     adjustFont: function(btn){
21680         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21681         //if(Roo.isSafari){ // safari
21682         //    adjust *= 2;
21683        // }
21684         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21685         if(Roo.isSafari){ // safari
21686             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21687             v =  (v < 10) ? 10 : v;
21688             v =  (v > 48) ? 48 : v;
21689             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21690             
21691         }
21692         
21693         
21694         v = Math.max(1, v+adjust);
21695         
21696         this.execCmd('FontSize', v  );
21697     },
21698
21699     onEditorEvent : function(e)
21700     {
21701         this.owner.fireEvent('editorevent', this, e);
21702       //  this.updateToolbar();
21703         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21704     },
21705
21706     insertTag : function(tg)
21707     {
21708         // could be a bit smarter... -> wrap the current selected tRoo..
21709         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21710             
21711             range = this.createRange(this.getSelection());
21712             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21713             wrappingNode.appendChild(range.extractContents());
21714             range.insertNode(wrappingNode);
21715
21716             return;
21717             
21718             
21719             
21720         }
21721         this.execCmd("formatblock",   tg);
21722         
21723     },
21724     
21725     insertText : function(txt)
21726     {
21727         
21728         
21729         var range = this.createRange();
21730         range.deleteContents();
21731                //alert(Sender.getAttribute('label'));
21732                
21733         range.insertNode(this.doc.createTextNode(txt));
21734     } ,
21735     
21736      
21737
21738     /**
21739      * Executes a Midas editor command on the editor document and performs necessary focus and
21740      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21741      * @param {String} cmd The Midas command
21742      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21743      */
21744     relayCmd : function(cmd, value){
21745         this.win.focus();
21746         this.execCmd(cmd, value);
21747         this.owner.fireEvent('editorevent', this);
21748         //this.updateToolbar();
21749         this.owner.deferFocus();
21750     },
21751
21752     /**
21753      * Executes a Midas editor command directly on the editor document.
21754      * For visual commands, you should use {@link #relayCmd} instead.
21755      * <b>This should only be called after the editor is initialized.</b>
21756      * @param {String} cmd The Midas command
21757      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21758      */
21759     execCmd : function(cmd, value){
21760         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21761         this.syncValue();
21762     },
21763  
21764  
21765    
21766     /**
21767      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21768      * to insert tRoo.
21769      * @param {String} text | dom node.. 
21770      */
21771     insertAtCursor : function(text)
21772     {
21773         
21774         if(!this.activated){
21775             return;
21776         }
21777         /*
21778         if(Roo.isIE){
21779             this.win.focus();
21780             var r = this.doc.selection.createRange();
21781             if(r){
21782                 r.collapse(true);
21783                 r.pasteHTML(text);
21784                 this.syncValue();
21785                 this.deferFocus();
21786             
21787             }
21788             return;
21789         }
21790         */
21791         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21792             this.win.focus();
21793             
21794             
21795             // from jquery ui (MIT licenced)
21796             var range, node;
21797             var win = this.win;
21798             
21799             if (win.getSelection && win.getSelection().getRangeAt) {
21800                 range = win.getSelection().getRangeAt(0);
21801                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21802                 range.insertNode(node);
21803             } else if (win.document.selection && win.document.selection.createRange) {
21804                 // no firefox support
21805                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21806                 win.document.selection.createRange().pasteHTML(txt);
21807             } else {
21808                 // no firefox support
21809                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21810                 this.execCmd('InsertHTML', txt);
21811             } 
21812             
21813             this.syncValue();
21814             
21815             this.deferFocus();
21816         }
21817     },
21818  // private
21819     mozKeyPress : function(e){
21820         if(e.ctrlKey){
21821             var c = e.getCharCode(), cmd;
21822           
21823             if(c > 0){
21824                 c = String.fromCharCode(c).toLowerCase();
21825                 switch(c){
21826                     case 'b':
21827                         cmd = 'bold';
21828                         break;
21829                     case 'i':
21830                         cmd = 'italic';
21831                         break;
21832                     
21833                     case 'u':
21834                         cmd = 'underline';
21835                         break;
21836                     
21837                     case 'v':
21838                         this.cleanUpPaste.defer(100, this);
21839                         return;
21840                         
21841                 }
21842                 if(cmd){
21843                     this.win.focus();
21844                     this.execCmd(cmd);
21845                     this.deferFocus();
21846                     e.preventDefault();
21847                 }
21848                 
21849             }
21850         }
21851     },
21852
21853     // private
21854     fixKeys : function(){ // load time branching for fastest keydown performance
21855         if(Roo.isIE){
21856             return function(e){
21857                 var k = e.getKey(), r;
21858                 if(k == e.TAB){
21859                     e.stopEvent();
21860                     r = this.doc.selection.createRange();
21861                     if(r){
21862                         r.collapse(true);
21863                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21864                         this.deferFocus();
21865                     }
21866                     return;
21867                 }
21868                 
21869                 if(k == e.ENTER){
21870                     r = this.doc.selection.createRange();
21871                     if(r){
21872                         var target = r.parentElement();
21873                         if(!target || target.tagName.toLowerCase() != 'li'){
21874                             e.stopEvent();
21875                             r.pasteHTML('<br />');
21876                             r.collapse(false);
21877                             r.select();
21878                         }
21879                     }
21880                 }
21881                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21882                     this.cleanUpPaste.defer(100, this);
21883                     return;
21884                 }
21885                 
21886                 
21887             };
21888         }else if(Roo.isOpera){
21889             return function(e){
21890                 var k = e.getKey();
21891                 if(k == e.TAB){
21892                     e.stopEvent();
21893                     this.win.focus();
21894                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21895                     this.deferFocus();
21896                 }
21897                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21898                     this.cleanUpPaste.defer(100, this);
21899                     return;
21900                 }
21901                 
21902             };
21903         }else if(Roo.isSafari){
21904             return function(e){
21905                 var k = e.getKey();
21906                 
21907                 if(k == e.TAB){
21908                     e.stopEvent();
21909                     this.execCmd('InsertText','\t');
21910                     this.deferFocus();
21911                     return;
21912                 }
21913                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21914                     this.cleanUpPaste.defer(100, this);
21915                     return;
21916                 }
21917                 
21918              };
21919         }
21920     }(),
21921     
21922     getAllAncestors: function()
21923     {
21924         var p = this.getSelectedNode();
21925         var a = [];
21926         if (!p) {
21927             a.push(p); // push blank onto stack..
21928             p = this.getParentElement();
21929         }
21930         
21931         
21932         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21933             a.push(p);
21934             p = p.parentNode;
21935         }
21936         a.push(this.doc.body);
21937         return a;
21938     },
21939     lastSel : false,
21940     lastSelNode : false,
21941     
21942     
21943     getSelection : function() 
21944     {
21945         this.assignDocWin();
21946         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21947     },
21948     
21949     getSelectedNode: function() 
21950     {
21951         // this may only work on Gecko!!!
21952         
21953         // should we cache this!!!!
21954         
21955         
21956         
21957          
21958         var range = this.createRange(this.getSelection()).cloneRange();
21959         
21960         if (Roo.isIE) {
21961             var parent = range.parentElement();
21962             while (true) {
21963                 var testRange = range.duplicate();
21964                 testRange.moveToElementText(parent);
21965                 if (testRange.inRange(range)) {
21966                     break;
21967                 }
21968                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21969                     break;
21970                 }
21971                 parent = parent.parentElement;
21972             }
21973             return parent;
21974         }
21975         
21976         // is ancestor a text element.
21977         var ac =  range.commonAncestorContainer;
21978         if (ac.nodeType == 3) {
21979             ac = ac.parentNode;
21980         }
21981         
21982         var ar = ac.childNodes;
21983          
21984         var nodes = [];
21985         var other_nodes = [];
21986         var has_other_nodes = false;
21987         for (var i=0;i<ar.length;i++) {
21988             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21989                 continue;
21990             }
21991             // fullly contained node.
21992             
21993             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21994                 nodes.push(ar[i]);
21995                 continue;
21996             }
21997             
21998             // probably selected..
21999             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22000                 other_nodes.push(ar[i]);
22001                 continue;
22002             }
22003             // outer..
22004             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22005                 continue;
22006             }
22007             
22008             
22009             has_other_nodes = true;
22010         }
22011         if (!nodes.length && other_nodes.length) {
22012             nodes= other_nodes;
22013         }
22014         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22015             return false;
22016         }
22017         
22018         return nodes[0];
22019     },
22020     createRange: function(sel)
22021     {
22022         // this has strange effects when using with 
22023         // top toolbar - not sure if it's a great idea.
22024         //this.editor.contentWindow.focus();
22025         if (typeof sel != "undefined") {
22026             try {
22027                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22028             } catch(e) {
22029                 return this.doc.createRange();
22030             }
22031         } else {
22032             return this.doc.createRange();
22033         }
22034     },
22035     getParentElement: function()
22036     {
22037         
22038         this.assignDocWin();
22039         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22040         
22041         var range = this.createRange(sel);
22042          
22043         try {
22044             var p = range.commonAncestorContainer;
22045             while (p.nodeType == 3) { // text node
22046                 p = p.parentNode;
22047             }
22048             return p;
22049         } catch (e) {
22050             return null;
22051         }
22052     
22053     },
22054     /***
22055      *
22056      * Range intersection.. the hard stuff...
22057      *  '-1' = before
22058      *  '0' = hits..
22059      *  '1' = after.
22060      *         [ -- selected range --- ]
22061      *   [fail]                        [fail]
22062      *
22063      *    basically..
22064      *      if end is before start or  hits it. fail.
22065      *      if start is after end or hits it fail.
22066      *
22067      *   if either hits (but other is outside. - then it's not 
22068      *   
22069      *    
22070      **/
22071     
22072     
22073     // @see http://www.thismuchiknow.co.uk/?p=64.
22074     rangeIntersectsNode : function(range, node)
22075     {
22076         var nodeRange = node.ownerDocument.createRange();
22077         try {
22078             nodeRange.selectNode(node);
22079         } catch (e) {
22080             nodeRange.selectNodeContents(node);
22081         }
22082     
22083         var rangeStartRange = range.cloneRange();
22084         rangeStartRange.collapse(true);
22085     
22086         var rangeEndRange = range.cloneRange();
22087         rangeEndRange.collapse(false);
22088     
22089         var nodeStartRange = nodeRange.cloneRange();
22090         nodeStartRange.collapse(true);
22091     
22092         var nodeEndRange = nodeRange.cloneRange();
22093         nodeEndRange.collapse(false);
22094     
22095         return rangeStartRange.compareBoundaryPoints(
22096                  Range.START_TO_START, nodeEndRange) == -1 &&
22097                rangeEndRange.compareBoundaryPoints(
22098                  Range.START_TO_START, nodeStartRange) == 1;
22099         
22100          
22101     },
22102     rangeCompareNode : function(range, node)
22103     {
22104         var nodeRange = node.ownerDocument.createRange();
22105         try {
22106             nodeRange.selectNode(node);
22107         } catch (e) {
22108             nodeRange.selectNodeContents(node);
22109         }
22110         
22111         
22112         range.collapse(true);
22113     
22114         nodeRange.collapse(true);
22115      
22116         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22117         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22118          
22119         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22120         
22121         var nodeIsBefore   =  ss == 1;
22122         var nodeIsAfter    = ee == -1;
22123         
22124         if (nodeIsBefore && nodeIsAfter) {
22125             return 0; // outer
22126         }
22127         if (!nodeIsBefore && nodeIsAfter) {
22128             return 1; //right trailed.
22129         }
22130         
22131         if (nodeIsBefore && !nodeIsAfter) {
22132             return 2;  // left trailed.
22133         }
22134         // fully contined.
22135         return 3;
22136     },
22137
22138     // private? - in a new class?
22139     cleanUpPaste :  function()
22140     {
22141         // cleans up the whole document..
22142         Roo.log('cleanuppaste');
22143         
22144         this.cleanUpChildren(this.doc.body);
22145         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22146         if (clean != this.doc.body.innerHTML) {
22147             this.doc.body.innerHTML = clean;
22148         }
22149         
22150     },
22151     
22152     cleanWordChars : function(input) {// change the chars to hex code
22153         var he = Roo.HtmlEditorCore;
22154         
22155         var output = input;
22156         Roo.each(he.swapCodes, function(sw) { 
22157             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22158             
22159             output = output.replace(swapper, sw[1]);
22160         });
22161         
22162         return output;
22163     },
22164     
22165     
22166     cleanUpChildren : function (n)
22167     {
22168         if (!n.childNodes.length) {
22169             return;
22170         }
22171         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22172            this.cleanUpChild(n.childNodes[i]);
22173         }
22174     },
22175     
22176     
22177         
22178     
22179     cleanUpChild : function (node)
22180     {
22181         var ed = this;
22182         //console.log(node);
22183         if (node.nodeName == "#text") {
22184             // clean up silly Windows -- stuff?
22185             return; 
22186         }
22187         if (node.nodeName == "#comment") {
22188             node.parentNode.removeChild(node);
22189             // clean up silly Windows -- stuff?
22190             return; 
22191         }
22192         var lcname = node.tagName.toLowerCase();
22193         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22194         // whitelist of tags..
22195         
22196         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22197             // remove node.
22198             node.parentNode.removeChild(node);
22199             return;
22200             
22201         }
22202         
22203         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22204         
22205         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22206         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22207         
22208         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22209         //    remove_keep_children = true;
22210         //}
22211         
22212         if (remove_keep_children) {
22213             this.cleanUpChildren(node);
22214             // inserts everything just before this node...
22215             while (node.childNodes.length) {
22216                 var cn = node.childNodes[0];
22217                 node.removeChild(cn);
22218                 node.parentNode.insertBefore(cn, node);
22219             }
22220             node.parentNode.removeChild(node);
22221             return;
22222         }
22223         
22224         if (!node.attributes || !node.attributes.length) {
22225             this.cleanUpChildren(node);
22226             return;
22227         }
22228         
22229         function cleanAttr(n,v)
22230         {
22231             
22232             if (v.match(/^\./) || v.match(/^\//)) {
22233                 return;
22234             }
22235             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22236                 return;
22237             }
22238             if (v.match(/^#/)) {
22239                 return;
22240             }
22241 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22242             node.removeAttribute(n);
22243             
22244         }
22245         
22246         var cwhite = this.cwhite;
22247         var cblack = this.cblack;
22248             
22249         function cleanStyle(n,v)
22250         {
22251             if (v.match(/expression/)) { //XSS?? should we even bother..
22252                 node.removeAttribute(n);
22253                 return;
22254             }
22255             
22256             var parts = v.split(/;/);
22257             var clean = [];
22258             
22259             Roo.each(parts, function(p) {
22260                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22261                 if (!p.length) {
22262                     return true;
22263                 }
22264                 var l = p.split(':').shift().replace(/\s+/g,'');
22265                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22266                 
22267                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22268 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22269                     //node.removeAttribute(n);
22270                     return true;
22271                 }
22272                 //Roo.log()
22273                 // only allow 'c whitelisted system attributes'
22274                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22275 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22276                     //node.removeAttribute(n);
22277                     return true;
22278                 }
22279                 
22280                 
22281                  
22282                 
22283                 clean.push(p);
22284                 return true;
22285             });
22286             if (clean.length) { 
22287                 node.setAttribute(n, clean.join(';'));
22288             } else {
22289                 node.removeAttribute(n);
22290             }
22291             
22292         }
22293         
22294         
22295         for (var i = node.attributes.length-1; i > -1 ; i--) {
22296             var a = node.attributes[i];
22297             //console.log(a);
22298             
22299             if (a.name.toLowerCase().substr(0,2)=='on')  {
22300                 node.removeAttribute(a.name);
22301                 continue;
22302             }
22303             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22304                 node.removeAttribute(a.name);
22305                 continue;
22306             }
22307             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22308                 cleanAttr(a.name,a.value); // fixme..
22309                 continue;
22310             }
22311             if (a.name == 'style') {
22312                 cleanStyle(a.name,a.value);
22313                 continue;
22314             }
22315             /// clean up MS crap..
22316             // tecnically this should be a list of valid class'es..
22317             
22318             
22319             if (a.name == 'class') {
22320                 if (a.value.match(/^Mso/)) {
22321                     node.className = '';
22322                 }
22323                 
22324                 if (a.value.match(/^body$/)) {
22325                     node.className = '';
22326                 }
22327                 continue;
22328             }
22329             
22330             // style cleanup!?
22331             // class cleanup?
22332             
22333         }
22334         
22335         
22336         this.cleanUpChildren(node);
22337         
22338         
22339     },
22340     
22341     /**
22342      * Clean up MS wordisms...
22343      */
22344     cleanWord : function(node)
22345     {
22346         
22347         
22348         if (!node) {
22349             this.cleanWord(this.doc.body);
22350             return;
22351         }
22352         if (node.nodeName == "#text") {
22353             // clean up silly Windows -- stuff?
22354             return; 
22355         }
22356         if (node.nodeName == "#comment") {
22357             node.parentNode.removeChild(node);
22358             // clean up silly Windows -- stuff?
22359             return; 
22360         }
22361         
22362         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22363             node.parentNode.removeChild(node);
22364             return;
22365         }
22366         
22367         // remove - but keep children..
22368         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22369             while (node.childNodes.length) {
22370                 var cn = node.childNodes[0];
22371                 node.removeChild(cn);
22372                 node.parentNode.insertBefore(cn, node);
22373             }
22374             node.parentNode.removeChild(node);
22375             this.iterateChildren(node, this.cleanWord);
22376             return;
22377         }
22378         // clean styles
22379         if (node.className.length) {
22380             
22381             var cn = node.className.split(/\W+/);
22382             var cna = [];
22383             Roo.each(cn, function(cls) {
22384                 if (cls.match(/Mso[a-zA-Z]+/)) {
22385                     return;
22386                 }
22387                 cna.push(cls);
22388             });
22389             node.className = cna.length ? cna.join(' ') : '';
22390             if (!cna.length) {
22391                 node.removeAttribute("class");
22392             }
22393         }
22394         
22395         if (node.hasAttribute("lang")) {
22396             node.removeAttribute("lang");
22397         }
22398         
22399         if (node.hasAttribute("style")) {
22400             
22401             var styles = node.getAttribute("style").split(";");
22402             var nstyle = [];
22403             Roo.each(styles, function(s) {
22404                 if (!s.match(/:/)) {
22405                     return;
22406                 }
22407                 var kv = s.split(":");
22408                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22409                     return;
22410                 }
22411                 // what ever is left... we allow.
22412                 nstyle.push(s);
22413             });
22414             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22415             if (!nstyle.length) {
22416                 node.removeAttribute('style');
22417             }
22418         }
22419         this.iterateChildren(node, this.cleanWord);
22420         
22421         
22422         
22423     },
22424     /**
22425      * iterateChildren of a Node, calling fn each time, using this as the scole..
22426      * @param {DomNode} node node to iterate children of.
22427      * @param {Function} fn method of this class to call on each item.
22428      */
22429     iterateChildren : function(node, fn)
22430     {
22431         if (!node.childNodes.length) {
22432                 return;
22433         }
22434         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22435            fn.call(this, node.childNodes[i])
22436         }
22437     },
22438     
22439     
22440     /**
22441      * cleanTableWidths.
22442      *
22443      * Quite often pasting from word etc.. results in tables with column and widths.
22444      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22445      *
22446      */
22447     cleanTableWidths : function(node)
22448     {
22449          
22450          
22451         if (!node) {
22452             this.cleanTableWidths(this.doc.body);
22453             return;
22454         }
22455         
22456         // ignore list...
22457         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22458             return; 
22459         }
22460         Roo.log(node.tagName);
22461         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22462             this.iterateChildren(node, this.cleanTableWidths);
22463             return;
22464         }
22465         if (node.hasAttribute('width')) {
22466             node.removeAttribute('width');
22467         }
22468         
22469          
22470         if (node.hasAttribute("style")) {
22471             // pretty basic...
22472             
22473             var styles = node.getAttribute("style").split(";");
22474             var nstyle = [];
22475             Roo.each(styles, function(s) {
22476                 if (!s.match(/:/)) {
22477                     return;
22478                 }
22479                 var kv = s.split(":");
22480                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22481                     return;
22482                 }
22483                 // what ever is left... we allow.
22484                 nstyle.push(s);
22485             });
22486             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22487             if (!nstyle.length) {
22488                 node.removeAttribute('style');
22489             }
22490         }
22491         
22492         this.iterateChildren(node, this.cleanTableWidths);
22493         
22494         
22495     },
22496     
22497     
22498     
22499     
22500     domToHTML : function(currentElement, depth, nopadtext) {
22501         
22502         depth = depth || 0;
22503         nopadtext = nopadtext || false;
22504     
22505         if (!currentElement) {
22506             return this.domToHTML(this.doc.body);
22507         }
22508         
22509         //Roo.log(currentElement);
22510         var j;
22511         var allText = false;
22512         var nodeName = currentElement.nodeName;
22513         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22514         
22515         if  (nodeName == '#text') {
22516             
22517             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22518         }
22519         
22520         
22521         var ret = '';
22522         if (nodeName != 'BODY') {
22523              
22524             var i = 0;
22525             // Prints the node tagName, such as <A>, <IMG>, etc
22526             if (tagName) {
22527                 var attr = [];
22528                 for(i = 0; i < currentElement.attributes.length;i++) {
22529                     // quoting?
22530                     var aname = currentElement.attributes.item(i).name;
22531                     if (!currentElement.attributes.item(i).value.length) {
22532                         continue;
22533                     }
22534                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22535                 }
22536                 
22537                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22538             } 
22539             else {
22540                 
22541                 // eack
22542             }
22543         } else {
22544             tagName = false;
22545         }
22546         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22547             return ret;
22548         }
22549         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22550             nopadtext = true;
22551         }
22552         
22553         
22554         // Traverse the tree
22555         i = 0;
22556         var currentElementChild = currentElement.childNodes.item(i);
22557         var allText = true;
22558         var innerHTML  = '';
22559         lastnode = '';
22560         while (currentElementChild) {
22561             // Formatting code (indent the tree so it looks nice on the screen)
22562             var nopad = nopadtext;
22563             if (lastnode == 'SPAN') {
22564                 nopad  = true;
22565             }
22566             // text
22567             if  (currentElementChild.nodeName == '#text') {
22568                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22569                 toadd = nopadtext ? toadd : toadd.trim();
22570                 if (!nopad && toadd.length > 80) {
22571                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22572                 }
22573                 innerHTML  += toadd;
22574                 
22575                 i++;
22576                 currentElementChild = currentElement.childNodes.item(i);
22577                 lastNode = '';
22578                 continue;
22579             }
22580             allText = false;
22581             
22582             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22583                 
22584             // Recursively traverse the tree structure of the child node
22585             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22586             lastnode = currentElementChild.nodeName;
22587             i++;
22588             currentElementChild=currentElement.childNodes.item(i);
22589         }
22590         
22591         ret += innerHTML;
22592         
22593         if (!allText) {
22594                 // The remaining code is mostly for formatting the tree
22595             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22596         }
22597         
22598         
22599         if (tagName) {
22600             ret+= "</"+tagName+">";
22601         }
22602         return ret;
22603         
22604     },
22605         
22606     applyBlacklists : function()
22607     {
22608         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22609         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22610         
22611         this.white = [];
22612         this.black = [];
22613         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22614             if (b.indexOf(tag) > -1) {
22615                 return;
22616             }
22617             this.white.push(tag);
22618             
22619         }, this);
22620         
22621         Roo.each(w, function(tag) {
22622             if (b.indexOf(tag) > -1) {
22623                 return;
22624             }
22625             if (this.white.indexOf(tag) > -1) {
22626                 return;
22627             }
22628             this.white.push(tag);
22629             
22630         }, this);
22631         
22632         
22633         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22634             if (w.indexOf(tag) > -1) {
22635                 return;
22636             }
22637             this.black.push(tag);
22638             
22639         }, this);
22640         
22641         Roo.each(b, function(tag) {
22642             if (w.indexOf(tag) > -1) {
22643                 return;
22644             }
22645             if (this.black.indexOf(tag) > -1) {
22646                 return;
22647             }
22648             this.black.push(tag);
22649             
22650         }, this);
22651         
22652         
22653         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22654         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22655         
22656         this.cwhite = [];
22657         this.cblack = [];
22658         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22659             if (b.indexOf(tag) > -1) {
22660                 return;
22661             }
22662             this.cwhite.push(tag);
22663             
22664         }, this);
22665         
22666         Roo.each(w, function(tag) {
22667             if (b.indexOf(tag) > -1) {
22668                 return;
22669             }
22670             if (this.cwhite.indexOf(tag) > -1) {
22671                 return;
22672             }
22673             this.cwhite.push(tag);
22674             
22675         }, this);
22676         
22677         
22678         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22679             if (w.indexOf(tag) > -1) {
22680                 return;
22681             }
22682             this.cblack.push(tag);
22683             
22684         }, this);
22685         
22686         Roo.each(b, function(tag) {
22687             if (w.indexOf(tag) > -1) {
22688                 return;
22689             }
22690             if (this.cblack.indexOf(tag) > -1) {
22691                 return;
22692             }
22693             this.cblack.push(tag);
22694             
22695         }, this);
22696     },
22697     
22698     setStylesheets : function(stylesheets)
22699     {
22700         if(typeof(stylesheets) == 'string'){
22701             Roo.get(this.iframe.contentDocument.head).createChild({
22702                 tag : 'link',
22703                 rel : 'stylesheet',
22704                 type : 'text/css',
22705                 href : stylesheets
22706             });
22707             
22708             return;
22709         }
22710         var _this = this;
22711      
22712         Roo.each(stylesheets, function(s) {
22713             if(!s.length){
22714                 return;
22715             }
22716             
22717             Roo.get(_this.iframe.contentDocument.head).createChild({
22718                 tag : 'link',
22719                 rel : 'stylesheet',
22720                 type : 'text/css',
22721                 href : s
22722             });
22723         });
22724
22725         
22726     },
22727     
22728     removeStylesheets : function()
22729     {
22730         var _this = this;
22731         
22732         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22733             s.remove();
22734         });
22735     },
22736     
22737     setStyle : function(style)
22738     {
22739         Roo.get(this.iframe.contentDocument.head).createChild({
22740             tag : 'style',
22741             type : 'text/css',
22742             html : style
22743         });
22744
22745         return;
22746     }
22747     
22748     // hide stuff that is not compatible
22749     /**
22750      * @event blur
22751      * @hide
22752      */
22753     /**
22754      * @event change
22755      * @hide
22756      */
22757     /**
22758      * @event focus
22759      * @hide
22760      */
22761     /**
22762      * @event specialkey
22763      * @hide
22764      */
22765     /**
22766      * @cfg {String} fieldClass @hide
22767      */
22768     /**
22769      * @cfg {String} focusClass @hide
22770      */
22771     /**
22772      * @cfg {String} autoCreate @hide
22773      */
22774     /**
22775      * @cfg {String} inputType @hide
22776      */
22777     /**
22778      * @cfg {String} invalidClass @hide
22779      */
22780     /**
22781      * @cfg {String} invalidText @hide
22782      */
22783     /**
22784      * @cfg {String} msgFx @hide
22785      */
22786     /**
22787      * @cfg {String} validateOnBlur @hide
22788      */
22789 });
22790
22791 Roo.HtmlEditorCore.white = [
22792         'area', 'br', 'img', 'input', 'hr', 'wbr',
22793         
22794        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22795        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22796        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22797        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22798        'table',   'ul',         'xmp', 
22799        
22800        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22801       'thead',   'tr', 
22802      
22803       'dir', 'menu', 'ol', 'ul', 'dl',
22804        
22805       'embed',  'object'
22806 ];
22807
22808
22809 Roo.HtmlEditorCore.black = [
22810     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22811         'applet', // 
22812         'base',   'basefont', 'bgsound', 'blink',  'body', 
22813         'frame',  'frameset', 'head',    'html',   'ilayer', 
22814         'iframe', 'layer',  'link',     'meta',    'object',   
22815         'script', 'style' ,'title',  'xml' // clean later..
22816 ];
22817 Roo.HtmlEditorCore.clean = [
22818     'script', 'style', 'title', 'xml'
22819 ];
22820 Roo.HtmlEditorCore.remove = [
22821     'font'
22822 ];
22823 // attributes..
22824
22825 Roo.HtmlEditorCore.ablack = [
22826     'on'
22827 ];
22828     
22829 Roo.HtmlEditorCore.aclean = [ 
22830     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22831 ];
22832
22833 // protocols..
22834 Roo.HtmlEditorCore.pwhite= [
22835         'http',  'https',  'mailto'
22836 ];
22837
22838 // white listed style attributes.
22839 Roo.HtmlEditorCore.cwhite= [
22840       //  'text-align', /// default is to allow most things..
22841       
22842          
22843 //        'font-size'//??
22844 ];
22845
22846 // black listed style attributes.
22847 Roo.HtmlEditorCore.cblack= [
22848       //  'font-size' -- this can be set by the project 
22849 ];
22850
22851
22852 Roo.HtmlEditorCore.swapCodes   =[ 
22853     [    8211, "--" ], 
22854     [    8212, "--" ], 
22855     [    8216,  "'" ],  
22856     [    8217, "'" ],  
22857     [    8220, '"' ],  
22858     [    8221, '"' ],  
22859     [    8226, "*" ],  
22860     [    8230, "..." ]
22861 ]; 
22862
22863     /*
22864  * - LGPL
22865  *
22866  * HtmlEditor
22867  * 
22868  */
22869
22870 /**
22871  * @class Roo.bootstrap.HtmlEditor
22872  * @extends Roo.bootstrap.TextArea
22873  * Bootstrap HtmlEditor class
22874
22875  * @constructor
22876  * Create a new HtmlEditor
22877  * @param {Object} config The config object
22878  */
22879
22880 Roo.bootstrap.HtmlEditor = function(config){
22881     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22882     if (!this.toolbars) {
22883         this.toolbars = [];
22884     }
22885     
22886     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22887     this.addEvents({
22888             /**
22889              * @event initialize
22890              * Fires when the editor is fully initialized (including the iframe)
22891              * @param {HtmlEditor} this
22892              */
22893             initialize: true,
22894             /**
22895              * @event activate
22896              * Fires when the editor is first receives the focus. Any insertion must wait
22897              * until after this event.
22898              * @param {HtmlEditor} this
22899              */
22900             activate: true,
22901              /**
22902              * @event beforesync
22903              * Fires before the textarea is updated with content from the editor iframe. Return false
22904              * to cancel the sync.
22905              * @param {HtmlEditor} this
22906              * @param {String} html
22907              */
22908             beforesync: true,
22909              /**
22910              * @event beforepush
22911              * Fires before the iframe editor is updated with content from the textarea. Return false
22912              * to cancel the push.
22913              * @param {HtmlEditor} this
22914              * @param {String} html
22915              */
22916             beforepush: true,
22917              /**
22918              * @event sync
22919              * Fires when the textarea is updated with content from the editor iframe.
22920              * @param {HtmlEditor} this
22921              * @param {String} html
22922              */
22923             sync: true,
22924              /**
22925              * @event push
22926              * Fires when the iframe editor is updated with content from the textarea.
22927              * @param {HtmlEditor} this
22928              * @param {String} html
22929              */
22930             push: true,
22931              /**
22932              * @event editmodechange
22933              * Fires when the editor switches edit modes
22934              * @param {HtmlEditor} this
22935              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22936              */
22937             editmodechange: true,
22938             /**
22939              * @event editorevent
22940              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22941              * @param {HtmlEditor} this
22942              */
22943             editorevent: true,
22944             /**
22945              * @event firstfocus
22946              * Fires when on first focus - needed by toolbars..
22947              * @param {HtmlEditor} this
22948              */
22949             firstfocus: true,
22950             /**
22951              * @event autosave
22952              * Auto save the htmlEditor value as a file into Events
22953              * @param {HtmlEditor} this
22954              */
22955             autosave: true,
22956             /**
22957              * @event savedpreview
22958              * preview the saved version of htmlEditor
22959              * @param {HtmlEditor} this
22960              */
22961             savedpreview: true
22962         });
22963 };
22964
22965
22966 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22967     
22968     
22969       /**
22970      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22971      */
22972     toolbars : false,
22973     
22974      /**
22975     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22976     */
22977     btns : [],
22978    
22979      /**
22980      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22981      *                        Roo.resizable.
22982      */
22983     resizable : false,
22984      /**
22985      * @cfg {Number} height (in pixels)
22986      */   
22987     height: 300,
22988    /**
22989      * @cfg {Number} width (in pixels)
22990      */   
22991     width: false,
22992     
22993     /**
22994      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22995      * 
22996      */
22997     stylesheets: false,
22998     
22999     // id of frame..
23000     frameId: false,
23001     
23002     // private properties
23003     validationEvent : false,
23004     deferHeight: true,
23005     initialized : false,
23006     activated : false,
23007     
23008     onFocus : Roo.emptyFn,
23009     iframePad:3,
23010     hideMode:'offsets',
23011     
23012     tbContainer : false,
23013     
23014     bodyCls : '',
23015     
23016     toolbarContainer :function() {
23017         return this.wrap.select('.x-html-editor-tb',true).first();
23018     },
23019
23020     /**
23021      * Protected method that will not generally be called directly. It
23022      * is called when the editor creates its toolbar. Override this method if you need to
23023      * add custom toolbar buttons.
23024      * @param {HtmlEditor} editor
23025      */
23026     createToolbar : function(){
23027         Roo.log('renewing');
23028         Roo.log("create toolbars");
23029         
23030         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23031         this.toolbars[0].render(this.toolbarContainer());
23032         
23033         return;
23034         
23035 //        if (!editor.toolbars || !editor.toolbars.length) {
23036 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23037 //        }
23038 //        
23039 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23040 //            editor.toolbars[i] = Roo.factory(
23041 //                    typeof(editor.toolbars[i]) == 'string' ?
23042 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23043 //                Roo.bootstrap.HtmlEditor);
23044 //            editor.toolbars[i].init(editor);
23045 //        }
23046     },
23047
23048      
23049     // private
23050     onRender : function(ct, position)
23051     {
23052        // Roo.log("Call onRender: " + this.xtype);
23053         var _t = this;
23054         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23055       
23056         this.wrap = this.inputEl().wrap({
23057             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23058         });
23059         
23060         this.editorcore.onRender(ct, position);
23061          
23062         if (this.resizable) {
23063             this.resizeEl = new Roo.Resizable(this.wrap, {
23064                 pinned : true,
23065                 wrap: true,
23066                 dynamic : true,
23067                 minHeight : this.height,
23068                 height: this.height,
23069                 handles : this.resizable,
23070                 width: this.width,
23071                 listeners : {
23072                     resize : function(r, w, h) {
23073                         _t.onResize(w,h); // -something
23074                     }
23075                 }
23076             });
23077             
23078         }
23079         this.createToolbar(this);
23080        
23081         
23082         if(!this.width && this.resizable){
23083             this.setSize(this.wrap.getSize());
23084         }
23085         if (this.resizeEl) {
23086             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23087             // should trigger onReize..
23088         }
23089         
23090     },
23091
23092     // private
23093     onResize : function(w, h)
23094     {
23095         Roo.log('resize: ' +w + ',' + h );
23096         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23097         var ew = false;
23098         var eh = false;
23099         
23100         if(this.inputEl() ){
23101             if(typeof w == 'number'){
23102                 var aw = w - this.wrap.getFrameWidth('lr');
23103                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23104                 ew = aw;
23105             }
23106             if(typeof h == 'number'){
23107                  var tbh = -11;  // fixme it needs to tool bar size!
23108                 for (var i =0; i < this.toolbars.length;i++) {
23109                     // fixme - ask toolbars for heights?
23110                     tbh += this.toolbars[i].el.getHeight();
23111                     //if (this.toolbars[i].footer) {
23112                     //    tbh += this.toolbars[i].footer.el.getHeight();
23113                     //}
23114                 }
23115               
23116                 
23117                 
23118                 
23119                 
23120                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23121                 ah -= 5; // knock a few pixes off for look..
23122                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23123                 var eh = ah;
23124             }
23125         }
23126         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23127         this.editorcore.onResize(ew,eh);
23128         
23129     },
23130
23131     /**
23132      * Toggles the editor between standard and source edit mode.
23133      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23134      */
23135     toggleSourceEdit : function(sourceEditMode)
23136     {
23137         this.editorcore.toggleSourceEdit(sourceEditMode);
23138         
23139         if(this.editorcore.sourceEditMode){
23140             Roo.log('editor - showing textarea');
23141             
23142 //            Roo.log('in');
23143 //            Roo.log(this.syncValue());
23144             this.syncValue();
23145             this.inputEl().removeClass(['hide', 'x-hidden']);
23146             this.inputEl().dom.removeAttribute('tabIndex');
23147             this.inputEl().focus();
23148         }else{
23149             Roo.log('editor - hiding textarea');
23150 //            Roo.log('out')
23151 //            Roo.log(this.pushValue()); 
23152             this.pushValue();
23153             
23154             this.inputEl().addClass(['hide', 'x-hidden']);
23155             this.inputEl().dom.setAttribute('tabIndex', -1);
23156             //this.deferFocus();
23157         }
23158          
23159         if(this.resizable){
23160             this.setSize(this.wrap.getSize());
23161         }
23162         
23163         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23164     },
23165  
23166     // private (for BoxComponent)
23167     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23168
23169     // private (for BoxComponent)
23170     getResizeEl : function(){
23171         return this.wrap;
23172     },
23173
23174     // private (for BoxComponent)
23175     getPositionEl : function(){
23176         return this.wrap;
23177     },
23178
23179     // private
23180     initEvents : function(){
23181         this.originalValue = this.getValue();
23182     },
23183
23184 //    /**
23185 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23186 //     * @method
23187 //     */
23188 //    markInvalid : Roo.emptyFn,
23189 //    /**
23190 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23191 //     * @method
23192 //     */
23193 //    clearInvalid : Roo.emptyFn,
23194
23195     setValue : function(v){
23196         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23197         this.editorcore.pushValue();
23198     },
23199
23200      
23201     // private
23202     deferFocus : function(){
23203         this.focus.defer(10, this);
23204     },
23205
23206     // doc'ed in Field
23207     focus : function(){
23208         this.editorcore.focus();
23209         
23210     },
23211       
23212
23213     // private
23214     onDestroy : function(){
23215         
23216         
23217         
23218         if(this.rendered){
23219             
23220             for (var i =0; i < this.toolbars.length;i++) {
23221                 // fixme - ask toolbars for heights?
23222                 this.toolbars[i].onDestroy();
23223             }
23224             
23225             this.wrap.dom.innerHTML = '';
23226             this.wrap.remove();
23227         }
23228     },
23229
23230     // private
23231     onFirstFocus : function(){
23232         //Roo.log("onFirstFocus");
23233         this.editorcore.onFirstFocus();
23234          for (var i =0; i < this.toolbars.length;i++) {
23235             this.toolbars[i].onFirstFocus();
23236         }
23237         
23238     },
23239     
23240     // private
23241     syncValue : function()
23242     {   
23243         this.editorcore.syncValue();
23244     },
23245     
23246     pushValue : function()
23247     {   
23248         this.editorcore.pushValue();
23249     }
23250      
23251     
23252     // hide stuff that is not compatible
23253     /**
23254      * @event blur
23255      * @hide
23256      */
23257     /**
23258      * @event change
23259      * @hide
23260      */
23261     /**
23262      * @event focus
23263      * @hide
23264      */
23265     /**
23266      * @event specialkey
23267      * @hide
23268      */
23269     /**
23270      * @cfg {String} fieldClass @hide
23271      */
23272     /**
23273      * @cfg {String} focusClass @hide
23274      */
23275     /**
23276      * @cfg {String} autoCreate @hide
23277      */
23278     /**
23279      * @cfg {String} inputType @hide
23280      */
23281     /**
23282      * @cfg {String} invalidClass @hide
23283      */
23284     /**
23285      * @cfg {String} invalidText @hide
23286      */
23287     /**
23288      * @cfg {String} msgFx @hide
23289      */
23290     /**
23291      * @cfg {String} validateOnBlur @hide
23292      */
23293 });
23294  
23295     
23296    
23297    
23298    
23299       
23300 Roo.namespace('Roo.bootstrap.htmleditor');
23301 /**
23302  * @class Roo.bootstrap.HtmlEditorToolbar1
23303  * Basic Toolbar
23304  * 
23305  * Usage:
23306  *
23307  new Roo.bootstrap.HtmlEditor({
23308     ....
23309     toolbars : [
23310         new Roo.bootstrap.HtmlEditorToolbar1({
23311             disable : { fonts: 1 , format: 1, ..., ... , ...],
23312             btns : [ .... ]
23313         })
23314     }
23315      
23316  * 
23317  * @cfg {Object} disable List of elements to disable..
23318  * @cfg {Array} btns List of additional buttons.
23319  * 
23320  * 
23321  * NEEDS Extra CSS? 
23322  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23323  */
23324  
23325 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23326 {
23327     
23328     Roo.apply(this, config);
23329     
23330     // default disabled, based on 'good practice'..
23331     this.disable = this.disable || {};
23332     Roo.applyIf(this.disable, {
23333         fontSize : true,
23334         colors : true,
23335         specialElements : true
23336     });
23337     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23338     
23339     this.editor = config.editor;
23340     this.editorcore = config.editor.editorcore;
23341     
23342     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23343     
23344     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23345     // dont call parent... till later.
23346 }
23347 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23348      
23349     bar : true,
23350     
23351     editor : false,
23352     editorcore : false,
23353     
23354     
23355     formats : [
23356         "p" ,  
23357         "h1","h2","h3","h4","h5","h6", 
23358         "pre", "code", 
23359         "abbr", "acronym", "address", "cite", "samp", "var",
23360         'div','span'
23361     ],
23362     
23363     onRender : function(ct, position)
23364     {
23365        // Roo.log("Call onRender: " + this.xtype);
23366         
23367        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23368        Roo.log(this.el);
23369        this.el.dom.style.marginBottom = '0';
23370        var _this = this;
23371        var editorcore = this.editorcore;
23372        var editor= this.editor;
23373        
23374        var children = [];
23375        var btn = function(id,cmd , toggle, handler, html){
23376        
23377             var  event = toggle ? 'toggle' : 'click';
23378        
23379             var a = {
23380                 size : 'sm',
23381                 xtype: 'Button',
23382                 xns: Roo.bootstrap,
23383                 glyphicon : id,
23384                 cmd : id || cmd,
23385                 enableToggle:toggle !== false,
23386                 html : html || '',
23387                 pressed : toggle ? false : null,
23388                 listeners : {}
23389             };
23390             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23391                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23392             };
23393             children.push(a);
23394             return a;
23395        }
23396        
23397     //    var cb_box = function...
23398         
23399         var style = {
23400                 xtype: 'Button',
23401                 size : 'sm',
23402                 xns: Roo.bootstrap,
23403                 glyphicon : 'font',
23404                 //html : 'submit'
23405                 menu : {
23406                     xtype: 'Menu',
23407                     xns: Roo.bootstrap,
23408                     items:  []
23409                 }
23410         };
23411         Roo.each(this.formats, function(f) {
23412             style.menu.items.push({
23413                 xtype :'MenuItem',
23414                 xns: Roo.bootstrap,
23415                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23416                 tagname : f,
23417                 listeners : {
23418                     click : function()
23419                     {
23420                         editorcore.insertTag(this.tagname);
23421                         editor.focus();
23422                     }
23423                 }
23424                 
23425             });
23426         });
23427         children.push(style);   
23428         
23429         btn('bold',false,true);
23430         btn('italic',false,true);
23431         btn('align-left', 'justifyleft',true);
23432         btn('align-center', 'justifycenter',true);
23433         btn('align-right' , 'justifyright',true);
23434         btn('link', false, false, function(btn) {
23435             //Roo.log("create link?");
23436             var url = prompt(this.createLinkText, this.defaultLinkValue);
23437             if(url && url != 'http:/'+'/'){
23438                 this.editorcore.relayCmd('createlink', url);
23439             }
23440         }),
23441         btn('list','insertunorderedlist',true);
23442         btn('pencil', false,true, function(btn){
23443                 Roo.log(this);
23444                 this.toggleSourceEdit(btn.pressed);
23445         });
23446         
23447         if (this.editor.btns.length > 0) {
23448             for (var i = 0; i<this.editor.btns.length; i++) {
23449                 children.push(this.editor.btns[i]);
23450             }
23451         }
23452         
23453         /*
23454         var cog = {
23455                 xtype: 'Button',
23456                 size : 'sm',
23457                 xns: Roo.bootstrap,
23458                 glyphicon : 'cog',
23459                 //html : 'submit'
23460                 menu : {
23461                     xtype: 'Menu',
23462                     xns: Roo.bootstrap,
23463                     items:  []
23464                 }
23465         };
23466         
23467         cog.menu.items.push({
23468             xtype :'MenuItem',
23469             xns: Roo.bootstrap,
23470             html : Clean styles,
23471             tagname : f,
23472             listeners : {
23473                 click : function()
23474                 {
23475                     editorcore.insertTag(this.tagname);
23476                     editor.focus();
23477                 }
23478             }
23479             
23480         });
23481        */
23482         
23483          
23484        this.xtype = 'NavSimplebar';
23485         
23486         for(var i=0;i< children.length;i++) {
23487             
23488             this.buttons.add(this.addxtypeChild(children[i]));
23489             
23490         }
23491         
23492         editor.on('editorevent', this.updateToolbar, this);
23493     },
23494     onBtnClick : function(id)
23495     {
23496        this.editorcore.relayCmd(id);
23497        this.editorcore.focus();
23498     },
23499     
23500     /**
23501      * Protected method that will not generally be called directly. It triggers
23502      * a toolbar update by reading the markup state of the current selection in the editor.
23503      */
23504     updateToolbar: function(){
23505
23506         if(!this.editorcore.activated){
23507             this.editor.onFirstFocus(); // is this neeed?
23508             return;
23509         }
23510
23511         var btns = this.buttons; 
23512         var doc = this.editorcore.doc;
23513         btns.get('bold').setActive(doc.queryCommandState('bold'));
23514         btns.get('italic').setActive(doc.queryCommandState('italic'));
23515         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23516         
23517         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23518         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23519         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23520         
23521         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23522         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23523          /*
23524         
23525         var ans = this.editorcore.getAllAncestors();
23526         if (this.formatCombo) {
23527             
23528             
23529             var store = this.formatCombo.store;
23530             this.formatCombo.setValue("");
23531             for (var i =0; i < ans.length;i++) {
23532                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23533                     // select it..
23534                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23535                     break;
23536                 }
23537             }
23538         }
23539         
23540         
23541         
23542         // hides menus... - so this cant be on a menu...
23543         Roo.bootstrap.MenuMgr.hideAll();
23544         */
23545         Roo.bootstrap.MenuMgr.hideAll();
23546         //this.editorsyncValue();
23547     },
23548     onFirstFocus: function() {
23549         this.buttons.each(function(item){
23550            item.enable();
23551         });
23552     },
23553     toggleSourceEdit : function(sourceEditMode){
23554         
23555           
23556         if(sourceEditMode){
23557             Roo.log("disabling buttons");
23558            this.buttons.each( function(item){
23559                 if(item.cmd != 'pencil'){
23560                     item.disable();
23561                 }
23562             });
23563           
23564         }else{
23565             Roo.log("enabling buttons");
23566             if(this.editorcore.initialized){
23567                 this.buttons.each( function(item){
23568                     item.enable();
23569                 });
23570             }
23571             
23572         }
23573         Roo.log("calling toggole on editor");
23574         // tell the editor that it's been pressed..
23575         this.editor.toggleSourceEdit(sourceEditMode);
23576        
23577     }
23578 });
23579
23580
23581
23582
23583
23584 /**
23585  * @class Roo.bootstrap.Table.AbstractSelectionModel
23586  * @extends Roo.util.Observable
23587  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23588  * implemented by descendant classes.  This class should not be directly instantiated.
23589  * @constructor
23590  */
23591 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23592     this.locked = false;
23593     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23594 };
23595
23596
23597 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23598     /** @ignore Called by the grid automatically. Do not call directly. */
23599     init : function(grid){
23600         this.grid = grid;
23601         this.initEvents();
23602     },
23603
23604     /**
23605      * Locks the selections.
23606      */
23607     lock : function(){
23608         this.locked = true;
23609     },
23610
23611     /**
23612      * Unlocks the selections.
23613      */
23614     unlock : function(){
23615         this.locked = false;
23616     },
23617
23618     /**
23619      * Returns true if the selections are locked.
23620      * @return {Boolean}
23621      */
23622     isLocked : function(){
23623         return this.locked;
23624     }
23625 });
23626 /**
23627  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23628  * @class Roo.bootstrap.Table.RowSelectionModel
23629  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23630  * It supports multiple selections and keyboard selection/navigation. 
23631  * @constructor
23632  * @param {Object} config
23633  */
23634
23635 Roo.bootstrap.Table.RowSelectionModel = function(config){
23636     Roo.apply(this, config);
23637     this.selections = new Roo.util.MixedCollection(false, function(o){
23638         return o.id;
23639     });
23640
23641     this.last = false;
23642     this.lastActive = false;
23643
23644     this.addEvents({
23645         /**
23646              * @event selectionchange
23647              * Fires when the selection changes
23648              * @param {SelectionModel} this
23649              */
23650             "selectionchange" : true,
23651         /**
23652              * @event afterselectionchange
23653              * Fires after the selection changes (eg. by key press or clicking)
23654              * @param {SelectionModel} this
23655              */
23656             "afterselectionchange" : true,
23657         /**
23658              * @event beforerowselect
23659              * Fires when a row is selected being selected, return false to cancel.
23660              * @param {SelectionModel} this
23661              * @param {Number} rowIndex The selected index
23662              * @param {Boolean} keepExisting False if other selections will be cleared
23663              */
23664             "beforerowselect" : true,
23665         /**
23666              * @event rowselect
23667              * Fires when a row is selected.
23668              * @param {SelectionModel} this
23669              * @param {Number} rowIndex The selected index
23670              * @param {Roo.data.Record} r The record
23671              */
23672             "rowselect" : true,
23673         /**
23674              * @event rowdeselect
23675              * Fires when a row is deselected.
23676              * @param {SelectionModel} this
23677              * @param {Number} rowIndex The selected index
23678              */
23679         "rowdeselect" : true
23680     });
23681     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23682     this.locked = false;
23683  };
23684
23685 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23686     /**
23687      * @cfg {Boolean} singleSelect
23688      * True to allow selection of only one row at a time (defaults to false)
23689      */
23690     singleSelect : false,
23691
23692     // private
23693     initEvents : function()
23694     {
23695
23696         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23697         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23698         //}else{ // allow click to work like normal
23699          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23700         //}
23701         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23702         this.grid.on("rowclick", this.handleMouseDown, this);
23703         
23704         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23705             "up" : function(e){
23706                 if(!e.shiftKey){
23707                     this.selectPrevious(e.shiftKey);
23708                 }else if(this.last !== false && this.lastActive !== false){
23709                     var last = this.last;
23710                     this.selectRange(this.last,  this.lastActive-1);
23711                     this.grid.getView().focusRow(this.lastActive);
23712                     if(last !== false){
23713                         this.last = last;
23714                     }
23715                 }else{
23716                     this.selectFirstRow();
23717                 }
23718                 this.fireEvent("afterselectionchange", this);
23719             },
23720             "down" : function(e){
23721                 if(!e.shiftKey){
23722                     this.selectNext(e.shiftKey);
23723                 }else if(this.last !== false && this.lastActive !== false){
23724                     var last = this.last;
23725                     this.selectRange(this.last,  this.lastActive+1);
23726                     this.grid.getView().focusRow(this.lastActive);
23727                     if(last !== false){
23728                         this.last = last;
23729                     }
23730                 }else{
23731                     this.selectFirstRow();
23732                 }
23733                 this.fireEvent("afterselectionchange", this);
23734             },
23735             scope: this
23736         });
23737         this.grid.store.on('load', function(){
23738             this.selections.clear();
23739         },this);
23740         /*
23741         var view = this.grid.view;
23742         view.on("refresh", this.onRefresh, this);
23743         view.on("rowupdated", this.onRowUpdated, this);
23744         view.on("rowremoved", this.onRemove, this);
23745         */
23746     },
23747
23748     // private
23749     onRefresh : function()
23750     {
23751         var ds = this.grid.store, i, v = this.grid.view;
23752         var s = this.selections;
23753         s.each(function(r){
23754             if((i = ds.indexOfId(r.id)) != -1){
23755                 v.onRowSelect(i);
23756             }else{
23757                 s.remove(r);
23758             }
23759         });
23760     },
23761
23762     // private
23763     onRemove : function(v, index, r){
23764         this.selections.remove(r);
23765     },
23766
23767     // private
23768     onRowUpdated : function(v, index, r){
23769         if(this.isSelected(r)){
23770             v.onRowSelect(index);
23771         }
23772     },
23773
23774     /**
23775      * Select records.
23776      * @param {Array} records The records to select
23777      * @param {Boolean} keepExisting (optional) True to keep existing selections
23778      */
23779     selectRecords : function(records, keepExisting)
23780     {
23781         if(!keepExisting){
23782             this.clearSelections();
23783         }
23784             var ds = this.grid.store;
23785         for(var i = 0, len = records.length; i < len; i++){
23786             this.selectRow(ds.indexOf(records[i]), true);
23787         }
23788     },
23789
23790     /**
23791      * Gets the number of selected rows.
23792      * @return {Number}
23793      */
23794     getCount : function(){
23795         return this.selections.length;
23796     },
23797
23798     /**
23799      * Selects the first row in the grid.
23800      */
23801     selectFirstRow : function(){
23802         this.selectRow(0);
23803     },
23804
23805     /**
23806      * Select the last row.
23807      * @param {Boolean} keepExisting (optional) True to keep existing selections
23808      */
23809     selectLastRow : function(keepExisting){
23810         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23811         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23812     },
23813
23814     /**
23815      * Selects the row immediately following the last selected row.
23816      * @param {Boolean} keepExisting (optional) True to keep existing selections
23817      */
23818     selectNext : function(keepExisting)
23819     {
23820             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23821             this.selectRow(this.last+1, keepExisting);
23822             this.grid.getView().focusRow(this.last);
23823         }
23824     },
23825
23826     /**
23827      * Selects the row that precedes the last selected row.
23828      * @param {Boolean} keepExisting (optional) True to keep existing selections
23829      */
23830     selectPrevious : function(keepExisting){
23831         if(this.last){
23832             this.selectRow(this.last-1, keepExisting);
23833             this.grid.getView().focusRow(this.last);
23834         }
23835     },
23836
23837     /**
23838      * Returns the selected records
23839      * @return {Array} Array of selected records
23840      */
23841     getSelections : function(){
23842         return [].concat(this.selections.items);
23843     },
23844
23845     /**
23846      * Returns the first selected record.
23847      * @return {Record}
23848      */
23849     getSelected : function(){
23850         return this.selections.itemAt(0);
23851     },
23852
23853
23854     /**
23855      * Clears all selections.
23856      */
23857     clearSelections : function(fast)
23858     {
23859         if(this.locked) {
23860             return;
23861         }
23862         if(fast !== true){
23863                 var ds = this.grid.store;
23864             var s = this.selections;
23865             s.each(function(r){
23866                 this.deselectRow(ds.indexOfId(r.id));
23867             }, this);
23868             s.clear();
23869         }else{
23870             this.selections.clear();
23871         }
23872         this.last = false;
23873     },
23874
23875
23876     /**
23877      * Selects all rows.
23878      */
23879     selectAll : function(){
23880         if(this.locked) {
23881             return;
23882         }
23883         this.selections.clear();
23884         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23885             this.selectRow(i, true);
23886         }
23887     },
23888
23889     /**
23890      * Returns True if there is a selection.
23891      * @return {Boolean}
23892      */
23893     hasSelection : function(){
23894         return this.selections.length > 0;
23895     },
23896
23897     /**
23898      * Returns True if the specified row is selected.
23899      * @param {Number/Record} record The record or index of the record to check
23900      * @return {Boolean}
23901      */
23902     isSelected : function(index){
23903             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23904         return (r && this.selections.key(r.id) ? true : false);
23905     },
23906
23907     /**
23908      * Returns True if the specified record id is selected.
23909      * @param {String} id The id of record to check
23910      * @return {Boolean}
23911      */
23912     isIdSelected : function(id){
23913         return (this.selections.key(id) ? true : false);
23914     },
23915
23916
23917     // private
23918     handleMouseDBClick : function(e, t){
23919         
23920     },
23921     // private
23922     handleMouseDown : function(e, t)
23923     {
23924             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23925         if(this.isLocked() || rowIndex < 0 ){
23926             return;
23927         };
23928         if(e.shiftKey && this.last !== false){
23929             var last = this.last;
23930             this.selectRange(last, rowIndex, e.ctrlKey);
23931             this.last = last; // reset the last
23932             t.focus();
23933     
23934         }else{
23935             var isSelected = this.isSelected(rowIndex);
23936             //Roo.log("select row:" + rowIndex);
23937             if(isSelected){
23938                 this.deselectRow(rowIndex);
23939             } else {
23940                         this.selectRow(rowIndex, true);
23941             }
23942     
23943             /*
23944                 if(e.button !== 0 && isSelected){
23945                 alert('rowIndex 2: ' + rowIndex);
23946                     view.focusRow(rowIndex);
23947                 }else if(e.ctrlKey && isSelected){
23948                     this.deselectRow(rowIndex);
23949                 }else if(!isSelected){
23950                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23951                     view.focusRow(rowIndex);
23952                 }
23953             */
23954         }
23955         this.fireEvent("afterselectionchange", this);
23956     },
23957     // private
23958     handleDragableRowClick :  function(grid, rowIndex, e) 
23959     {
23960         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23961             this.selectRow(rowIndex, false);
23962             grid.view.focusRow(rowIndex);
23963              this.fireEvent("afterselectionchange", this);
23964         }
23965     },
23966     
23967     /**
23968      * Selects multiple rows.
23969      * @param {Array} rows Array of the indexes of the row to select
23970      * @param {Boolean} keepExisting (optional) True to keep existing selections
23971      */
23972     selectRows : function(rows, keepExisting){
23973         if(!keepExisting){
23974             this.clearSelections();
23975         }
23976         for(var i = 0, len = rows.length; i < len; i++){
23977             this.selectRow(rows[i], true);
23978         }
23979     },
23980
23981     /**
23982      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23983      * @param {Number} startRow The index of the first row in the range
23984      * @param {Number} endRow The index of the last row in the range
23985      * @param {Boolean} keepExisting (optional) True to retain existing selections
23986      */
23987     selectRange : function(startRow, endRow, keepExisting){
23988         if(this.locked) {
23989             return;
23990         }
23991         if(!keepExisting){
23992             this.clearSelections();
23993         }
23994         if(startRow <= endRow){
23995             for(var i = startRow; i <= endRow; i++){
23996                 this.selectRow(i, true);
23997             }
23998         }else{
23999             for(var i = startRow; i >= endRow; i--){
24000                 this.selectRow(i, true);
24001             }
24002         }
24003     },
24004
24005     /**
24006      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24007      * @param {Number} startRow The index of the first row in the range
24008      * @param {Number} endRow The index of the last row in the range
24009      */
24010     deselectRange : function(startRow, endRow, preventViewNotify){
24011         if(this.locked) {
24012             return;
24013         }
24014         for(var i = startRow; i <= endRow; i++){
24015             this.deselectRow(i, preventViewNotify);
24016         }
24017     },
24018
24019     /**
24020      * Selects a row.
24021      * @param {Number} row The index of the row to select
24022      * @param {Boolean} keepExisting (optional) True to keep existing selections
24023      */
24024     selectRow : function(index, keepExisting, preventViewNotify)
24025     {
24026             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24027             return;
24028         }
24029         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24030             if(!keepExisting || this.singleSelect){
24031                 this.clearSelections();
24032             }
24033             
24034             var r = this.grid.store.getAt(index);
24035             //console.log('selectRow - record id :' + r.id);
24036             
24037             this.selections.add(r);
24038             this.last = this.lastActive = index;
24039             if(!preventViewNotify){
24040                 var proxy = new Roo.Element(
24041                                 this.grid.getRowDom(index)
24042                 );
24043                 proxy.addClass('bg-info info');
24044             }
24045             this.fireEvent("rowselect", this, index, r);
24046             this.fireEvent("selectionchange", this);
24047         }
24048     },
24049
24050     /**
24051      * Deselects a row.
24052      * @param {Number} row The index of the row to deselect
24053      */
24054     deselectRow : function(index, preventViewNotify)
24055     {
24056         if(this.locked) {
24057             return;
24058         }
24059         if(this.last == index){
24060             this.last = false;
24061         }
24062         if(this.lastActive == index){
24063             this.lastActive = false;
24064         }
24065         
24066         var r = this.grid.store.getAt(index);
24067         if (!r) {
24068             return;
24069         }
24070         
24071         this.selections.remove(r);
24072         //.console.log('deselectRow - record id :' + r.id);
24073         if(!preventViewNotify){
24074         
24075             var proxy = new Roo.Element(
24076                 this.grid.getRowDom(index)
24077             );
24078             proxy.removeClass('bg-info info');
24079         }
24080         this.fireEvent("rowdeselect", this, index);
24081         this.fireEvent("selectionchange", this);
24082     },
24083
24084     // private
24085     restoreLast : function(){
24086         if(this._last){
24087             this.last = this._last;
24088         }
24089     },
24090
24091     // private
24092     acceptsNav : function(row, col, cm){
24093         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24094     },
24095
24096     // private
24097     onEditorKey : function(field, e){
24098         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24099         if(k == e.TAB){
24100             e.stopEvent();
24101             ed.completeEdit();
24102             if(e.shiftKey){
24103                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24104             }else{
24105                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24106             }
24107         }else if(k == e.ENTER && !e.ctrlKey){
24108             e.stopEvent();
24109             ed.completeEdit();
24110             if(e.shiftKey){
24111                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24112             }else{
24113                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24114             }
24115         }else if(k == e.ESC){
24116             ed.cancelEdit();
24117         }
24118         if(newCell){
24119             g.startEditing(newCell[0], newCell[1]);
24120         }
24121     }
24122 });
24123 /*
24124  * Based on:
24125  * Ext JS Library 1.1.1
24126  * Copyright(c) 2006-2007, Ext JS, LLC.
24127  *
24128  * Originally Released Under LGPL - original licence link has changed is not relivant.
24129  *
24130  * Fork - LGPL
24131  * <script type="text/javascript">
24132  */
24133  
24134 /**
24135  * @class Roo.bootstrap.PagingToolbar
24136  * @extends Roo.bootstrap.NavSimplebar
24137  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24138  * @constructor
24139  * Create a new PagingToolbar
24140  * @param {Object} config The config object
24141  * @param {Roo.data.Store} store
24142  */
24143 Roo.bootstrap.PagingToolbar = function(config)
24144 {
24145     // old args format still supported... - xtype is prefered..
24146         // created from xtype...
24147     
24148     this.ds = config.dataSource;
24149     
24150     if (config.store && !this.ds) {
24151         this.store= Roo.factory(config.store, Roo.data);
24152         this.ds = this.store;
24153         this.ds.xmodule = this.xmodule || false;
24154     }
24155     
24156     this.toolbarItems = [];
24157     if (config.items) {
24158         this.toolbarItems = config.items;
24159     }
24160     
24161     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24162     
24163     this.cursor = 0;
24164     
24165     if (this.ds) { 
24166         this.bind(this.ds);
24167     }
24168     
24169     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24170     
24171 };
24172
24173 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24174     /**
24175      * @cfg {Roo.data.Store} dataSource
24176      * The underlying data store providing the paged data
24177      */
24178     /**
24179      * @cfg {String/HTMLElement/Element} container
24180      * container The id or element that will contain the toolbar
24181      */
24182     /**
24183      * @cfg {Boolean} displayInfo
24184      * True to display the displayMsg (defaults to false)
24185      */
24186     /**
24187      * @cfg {Number} pageSize
24188      * The number of records to display per page (defaults to 20)
24189      */
24190     pageSize: 20,
24191     /**
24192      * @cfg {String} displayMsg
24193      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24194      */
24195     displayMsg : 'Displaying {0} - {1} of {2}',
24196     /**
24197      * @cfg {String} emptyMsg
24198      * The message to display when no records are found (defaults to "No data to display")
24199      */
24200     emptyMsg : 'No data to display',
24201     /**
24202      * Customizable piece of the default paging text (defaults to "Page")
24203      * @type String
24204      */
24205     beforePageText : "Page",
24206     /**
24207      * Customizable piece of the default paging text (defaults to "of %0")
24208      * @type String
24209      */
24210     afterPageText : "of {0}",
24211     /**
24212      * Customizable piece of the default paging text (defaults to "First Page")
24213      * @type String
24214      */
24215     firstText : "First Page",
24216     /**
24217      * Customizable piece of the default paging text (defaults to "Previous Page")
24218      * @type String
24219      */
24220     prevText : "Previous Page",
24221     /**
24222      * Customizable piece of the default paging text (defaults to "Next Page")
24223      * @type String
24224      */
24225     nextText : "Next Page",
24226     /**
24227      * Customizable piece of the default paging text (defaults to "Last Page")
24228      * @type String
24229      */
24230     lastText : "Last Page",
24231     /**
24232      * Customizable piece of the default paging text (defaults to "Refresh")
24233      * @type String
24234      */
24235     refreshText : "Refresh",
24236
24237     buttons : false,
24238     // private
24239     onRender : function(ct, position) 
24240     {
24241         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24242         this.navgroup.parentId = this.id;
24243         this.navgroup.onRender(this.el, null);
24244         // add the buttons to the navgroup
24245         
24246         if(this.displayInfo){
24247             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24248             this.displayEl = this.el.select('.x-paging-info', true).first();
24249 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24250 //            this.displayEl = navel.el.select('span',true).first();
24251         }
24252         
24253         var _this = this;
24254         
24255         if(this.buttons){
24256             Roo.each(_this.buttons, function(e){ // this might need to use render????
24257                Roo.factory(e).onRender(_this.el, null);
24258             });
24259         }
24260             
24261         Roo.each(_this.toolbarItems, function(e) {
24262             _this.navgroup.addItem(e);
24263         });
24264         
24265         
24266         this.first = this.navgroup.addItem({
24267             tooltip: this.firstText,
24268             cls: "prev",
24269             icon : 'fa fa-backward',
24270             disabled: true,
24271             preventDefault: true,
24272             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24273         });
24274         
24275         this.prev =  this.navgroup.addItem({
24276             tooltip: this.prevText,
24277             cls: "prev",
24278             icon : 'fa fa-step-backward',
24279             disabled: true,
24280             preventDefault: true,
24281             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24282         });
24283     //this.addSeparator();
24284         
24285         
24286         var field = this.navgroup.addItem( {
24287             tagtype : 'span',
24288             cls : 'x-paging-position',
24289             
24290             html : this.beforePageText  +
24291                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24292                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24293          } ); //?? escaped?
24294         
24295         this.field = field.el.select('input', true).first();
24296         this.field.on("keydown", this.onPagingKeydown, this);
24297         this.field.on("focus", function(){this.dom.select();});
24298     
24299     
24300         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24301         //this.field.setHeight(18);
24302         //this.addSeparator();
24303         this.next = this.navgroup.addItem({
24304             tooltip: this.nextText,
24305             cls: "next",
24306             html : ' <i class="fa fa-step-forward">',
24307             disabled: true,
24308             preventDefault: true,
24309             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24310         });
24311         this.last = this.navgroup.addItem({
24312             tooltip: this.lastText,
24313             icon : 'fa fa-forward',
24314             cls: "next",
24315             disabled: true,
24316             preventDefault: true,
24317             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24318         });
24319     //this.addSeparator();
24320         this.loading = this.navgroup.addItem({
24321             tooltip: this.refreshText,
24322             icon: 'fa fa-refresh',
24323             preventDefault: true,
24324             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24325         });
24326         
24327     },
24328
24329     // private
24330     updateInfo : function(){
24331         if(this.displayEl){
24332             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24333             var msg = count == 0 ?
24334                 this.emptyMsg :
24335                 String.format(
24336                     this.displayMsg,
24337                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24338                 );
24339             this.displayEl.update(msg);
24340         }
24341     },
24342
24343     // private
24344     onLoad : function(ds, r, o)
24345     {
24346         this.cursor = o.params ? o.params.start : 0;
24347         var d = this.getPageData(),
24348             ap = d.activePage,
24349             ps = d.pages;
24350         
24351         
24352         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24353         this.field.dom.value = ap;
24354         this.first.setDisabled(ap == 1);
24355         this.prev.setDisabled(ap == 1);
24356         this.next.setDisabled(ap == ps);
24357         this.last.setDisabled(ap == ps);
24358         this.loading.enable();
24359         this.updateInfo();
24360     },
24361
24362     // private
24363     getPageData : function(){
24364         var total = this.ds.getTotalCount();
24365         return {
24366             total : total,
24367             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24368             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24369         };
24370     },
24371
24372     // private
24373     onLoadError : function(){
24374         this.loading.enable();
24375     },
24376
24377     // private
24378     onPagingKeydown : function(e){
24379         var k = e.getKey();
24380         var d = this.getPageData();
24381         if(k == e.RETURN){
24382             var v = this.field.dom.value, pageNum;
24383             if(!v || isNaN(pageNum = parseInt(v, 10))){
24384                 this.field.dom.value = d.activePage;
24385                 return;
24386             }
24387             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24388             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24389             e.stopEvent();
24390         }
24391         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))
24392         {
24393           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24394           this.field.dom.value = pageNum;
24395           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24396           e.stopEvent();
24397         }
24398         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24399         {
24400           var v = this.field.dom.value, pageNum; 
24401           var increment = (e.shiftKey) ? 10 : 1;
24402           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24403                 increment *= -1;
24404           }
24405           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24406             this.field.dom.value = d.activePage;
24407             return;
24408           }
24409           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24410           {
24411             this.field.dom.value = parseInt(v, 10) + increment;
24412             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24413             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24414           }
24415           e.stopEvent();
24416         }
24417     },
24418
24419     // private
24420     beforeLoad : function(){
24421         if(this.loading){
24422             this.loading.disable();
24423         }
24424     },
24425
24426     // private
24427     onClick : function(which){
24428         
24429         var ds = this.ds;
24430         if (!ds) {
24431             return;
24432         }
24433         
24434         switch(which){
24435             case "first":
24436                 ds.load({params:{start: 0, limit: this.pageSize}});
24437             break;
24438             case "prev":
24439                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24440             break;
24441             case "next":
24442                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24443             break;
24444             case "last":
24445                 var total = ds.getTotalCount();
24446                 var extra = total % this.pageSize;
24447                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24448                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24449             break;
24450             case "refresh":
24451                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24452             break;
24453         }
24454     },
24455
24456     /**
24457      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24458      * @param {Roo.data.Store} store The data store to unbind
24459      */
24460     unbind : function(ds){
24461         ds.un("beforeload", this.beforeLoad, this);
24462         ds.un("load", this.onLoad, this);
24463         ds.un("loadexception", this.onLoadError, this);
24464         ds.un("remove", this.updateInfo, this);
24465         ds.un("add", this.updateInfo, this);
24466         this.ds = undefined;
24467     },
24468
24469     /**
24470      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24471      * @param {Roo.data.Store} store The data store to bind
24472      */
24473     bind : function(ds){
24474         ds.on("beforeload", this.beforeLoad, this);
24475         ds.on("load", this.onLoad, this);
24476         ds.on("loadexception", this.onLoadError, this);
24477         ds.on("remove", this.updateInfo, this);
24478         ds.on("add", this.updateInfo, this);
24479         this.ds = ds;
24480     }
24481 });/*
24482  * - LGPL
24483  *
24484  * element
24485  * 
24486  */
24487
24488 /**
24489  * @class Roo.bootstrap.MessageBar
24490  * @extends Roo.bootstrap.Component
24491  * Bootstrap MessageBar class
24492  * @cfg {String} html contents of the MessageBar
24493  * @cfg {String} weight (info | success | warning | danger) default info
24494  * @cfg {String} beforeClass insert the bar before the given class
24495  * @cfg {Boolean} closable (true | false) default false
24496  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24497  * 
24498  * @constructor
24499  * Create a new Element
24500  * @param {Object} config The config object
24501  */
24502
24503 Roo.bootstrap.MessageBar = function(config){
24504     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24505 };
24506
24507 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24508     
24509     html: '',
24510     weight: 'info',
24511     closable: false,
24512     fixed: false,
24513     beforeClass: 'bootstrap-sticky-wrap',
24514     
24515     getAutoCreate : function(){
24516         
24517         var cfg = {
24518             tag: 'div',
24519             cls: 'alert alert-dismissable alert-' + this.weight,
24520             cn: [
24521                 {
24522                     tag: 'span',
24523                     cls: 'message',
24524                     html: this.html || ''
24525                 }
24526             ]
24527         };
24528         
24529         if(this.fixed){
24530             cfg.cls += ' alert-messages-fixed';
24531         }
24532         
24533         if(this.closable){
24534             cfg.cn.push({
24535                 tag: 'button',
24536                 cls: 'close',
24537                 html: 'x'
24538             });
24539         }
24540         
24541         return cfg;
24542     },
24543     
24544     onRender : function(ct, position)
24545     {
24546         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24547         
24548         if(!this.el){
24549             var cfg = Roo.apply({},  this.getAutoCreate());
24550             cfg.id = Roo.id();
24551             
24552             if (this.cls) {
24553                 cfg.cls += ' ' + this.cls;
24554             }
24555             if (this.style) {
24556                 cfg.style = this.style;
24557             }
24558             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24559             
24560             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24561         }
24562         
24563         this.el.select('>button.close').on('click', this.hide, this);
24564         
24565     },
24566     
24567     show : function()
24568     {
24569         if (!this.rendered) {
24570             this.render();
24571         }
24572         
24573         this.el.show();
24574         
24575         this.fireEvent('show', this);
24576         
24577     },
24578     
24579     hide : function()
24580     {
24581         if (!this.rendered) {
24582             this.render();
24583         }
24584         
24585         this.el.hide();
24586         
24587         this.fireEvent('hide', this);
24588     },
24589     
24590     update : function()
24591     {
24592 //        var e = this.el.dom.firstChild;
24593 //        
24594 //        if(this.closable){
24595 //            e = e.nextSibling;
24596 //        }
24597 //        
24598 //        e.data = this.html || '';
24599
24600         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24601     }
24602    
24603 });
24604
24605  
24606
24607      /*
24608  * - LGPL
24609  *
24610  * Graph
24611  * 
24612  */
24613
24614
24615 /**
24616  * @class Roo.bootstrap.Graph
24617  * @extends Roo.bootstrap.Component
24618  * Bootstrap Graph class
24619 > Prameters
24620  -sm {number} sm 4
24621  -md {number} md 5
24622  @cfg {String} graphtype  bar | vbar | pie
24623  @cfg {number} g_x coodinator | centre x (pie)
24624  @cfg {number} g_y coodinator | centre y (pie)
24625  @cfg {number} g_r radius (pie)
24626  @cfg {number} g_height height of the chart (respected by all elements in the set)
24627  @cfg {number} g_width width of the chart (respected by all elements in the set)
24628  @cfg {Object} title The title of the chart
24629     
24630  -{Array}  values
24631  -opts (object) options for the chart 
24632      o {
24633      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24634      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24635      o vgutter (number)
24636      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.
24637      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24638      o to
24639      o stretch (boolean)
24640      o }
24641  -opts (object) options for the pie
24642      o{
24643      o cut
24644      o startAngle (number)
24645      o endAngle (number)
24646      } 
24647  *
24648  * @constructor
24649  * Create a new Input
24650  * @param {Object} config The config object
24651  */
24652
24653 Roo.bootstrap.Graph = function(config){
24654     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24655     
24656     this.addEvents({
24657         // img events
24658         /**
24659          * @event click
24660          * The img click event for the img.
24661          * @param {Roo.EventObject} e
24662          */
24663         "click" : true
24664     });
24665 };
24666
24667 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24668     
24669     sm: 4,
24670     md: 5,
24671     graphtype: 'bar',
24672     g_height: 250,
24673     g_width: 400,
24674     g_x: 50,
24675     g_y: 50,
24676     g_r: 30,
24677     opts:{
24678         //g_colors: this.colors,
24679         g_type: 'soft',
24680         g_gutter: '20%'
24681
24682     },
24683     title : false,
24684
24685     getAutoCreate : function(){
24686         
24687         var cfg = {
24688             tag: 'div',
24689             html : null
24690         };
24691         
24692         
24693         return  cfg;
24694     },
24695
24696     onRender : function(ct,position){
24697         
24698         
24699         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24700         
24701         if (typeof(Raphael) == 'undefined') {
24702             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24703             return;
24704         }
24705         
24706         this.raphael = Raphael(this.el.dom);
24707         
24708                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24709                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24710                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24711                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24712                 /*
24713                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24714                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24715                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24716                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24717                 
24718                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24719                 r.barchart(330, 10, 300, 220, data1);
24720                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24721                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24722                 */
24723                 
24724                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24725                 // r.barchart(30, 30, 560, 250,  xdata, {
24726                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24727                 //     axis : "0 0 1 1",
24728                 //     axisxlabels :  xdata
24729                 //     //yvalues : cols,
24730                    
24731                 // });
24732 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24733 //        
24734 //        this.load(null,xdata,{
24735 //                axis : "0 0 1 1",
24736 //                axisxlabels :  xdata
24737 //                });
24738
24739     },
24740
24741     load : function(graphtype,xdata,opts)
24742     {
24743         this.raphael.clear();
24744         if(!graphtype) {
24745             graphtype = this.graphtype;
24746         }
24747         if(!opts){
24748             opts = this.opts;
24749         }
24750         var r = this.raphael,
24751             fin = function () {
24752                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24753             },
24754             fout = function () {
24755                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24756             },
24757             pfin = function() {
24758                 this.sector.stop();
24759                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24760
24761                 if (this.label) {
24762                     this.label[0].stop();
24763                     this.label[0].attr({ r: 7.5 });
24764                     this.label[1].attr({ "font-weight": 800 });
24765                 }
24766             },
24767             pfout = function() {
24768                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24769
24770                 if (this.label) {
24771                     this.label[0].animate({ r: 5 }, 500, "bounce");
24772                     this.label[1].attr({ "font-weight": 400 });
24773                 }
24774             };
24775
24776         switch(graphtype){
24777             case 'bar':
24778                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24779                 break;
24780             case 'hbar':
24781                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24782                 break;
24783             case 'pie':
24784 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24785 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24786 //            
24787                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24788                 
24789                 break;
24790
24791         }
24792         
24793         if(this.title){
24794             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24795         }
24796         
24797     },
24798     
24799     setTitle: function(o)
24800     {
24801         this.title = o;
24802     },
24803     
24804     initEvents: function() {
24805         
24806         if(!this.href){
24807             this.el.on('click', this.onClick, this);
24808         }
24809     },
24810     
24811     onClick : function(e)
24812     {
24813         Roo.log('img onclick');
24814         this.fireEvent('click', this, e);
24815     }
24816    
24817 });
24818
24819  
24820 /*
24821  * - LGPL
24822  *
24823  * numberBox
24824  * 
24825  */
24826 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24827
24828 /**
24829  * @class Roo.bootstrap.dash.NumberBox
24830  * @extends Roo.bootstrap.Component
24831  * Bootstrap NumberBox class
24832  * @cfg {String} headline Box headline
24833  * @cfg {String} content Box content
24834  * @cfg {String} icon Box icon
24835  * @cfg {String} footer Footer text
24836  * @cfg {String} fhref Footer href
24837  * 
24838  * @constructor
24839  * Create a new NumberBox
24840  * @param {Object} config The config object
24841  */
24842
24843
24844 Roo.bootstrap.dash.NumberBox = function(config){
24845     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24846     
24847 };
24848
24849 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24850     
24851     headline : '',
24852     content : '',
24853     icon : '',
24854     footer : '',
24855     fhref : '',
24856     ficon : '',
24857     
24858     getAutoCreate : function(){
24859         
24860         var cfg = {
24861             tag : 'div',
24862             cls : 'small-box ',
24863             cn : [
24864                 {
24865                     tag : 'div',
24866                     cls : 'inner',
24867                     cn :[
24868                         {
24869                             tag : 'h3',
24870                             cls : 'roo-headline',
24871                             html : this.headline
24872                         },
24873                         {
24874                             tag : 'p',
24875                             cls : 'roo-content',
24876                             html : this.content
24877                         }
24878                     ]
24879                 }
24880             ]
24881         };
24882         
24883         if(this.icon){
24884             cfg.cn.push({
24885                 tag : 'div',
24886                 cls : 'icon',
24887                 cn :[
24888                     {
24889                         tag : 'i',
24890                         cls : 'ion ' + this.icon
24891                     }
24892                 ]
24893             });
24894         }
24895         
24896         if(this.footer){
24897             var footer = {
24898                 tag : 'a',
24899                 cls : 'small-box-footer',
24900                 href : this.fhref || '#',
24901                 html : this.footer
24902             };
24903             
24904             cfg.cn.push(footer);
24905             
24906         }
24907         
24908         return  cfg;
24909     },
24910
24911     onRender : function(ct,position){
24912         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24913
24914
24915        
24916                 
24917     },
24918
24919     setHeadline: function (value)
24920     {
24921         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24922     },
24923     
24924     setFooter: function (value, href)
24925     {
24926         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24927         
24928         if(href){
24929             this.el.select('a.small-box-footer',true).first().attr('href', href);
24930         }
24931         
24932     },
24933
24934     setContent: function (value)
24935     {
24936         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24937     },
24938
24939     initEvents: function() 
24940     {   
24941         
24942     }
24943     
24944 });
24945
24946  
24947 /*
24948  * - LGPL
24949  *
24950  * TabBox
24951  * 
24952  */
24953 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24954
24955 /**
24956  * @class Roo.bootstrap.dash.TabBox
24957  * @extends Roo.bootstrap.Component
24958  * Bootstrap TabBox class
24959  * @cfg {String} title Title of the TabBox
24960  * @cfg {String} icon Icon of the TabBox
24961  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24962  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24963  * 
24964  * @constructor
24965  * Create a new TabBox
24966  * @param {Object} config The config object
24967  */
24968
24969
24970 Roo.bootstrap.dash.TabBox = function(config){
24971     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24972     this.addEvents({
24973         // raw events
24974         /**
24975          * @event addpane
24976          * When a pane is added
24977          * @param {Roo.bootstrap.dash.TabPane} pane
24978          */
24979         "addpane" : true,
24980         /**
24981          * @event activatepane
24982          * When a pane is activated
24983          * @param {Roo.bootstrap.dash.TabPane} pane
24984          */
24985         "activatepane" : true
24986         
24987          
24988     });
24989     
24990     this.panes = [];
24991 };
24992
24993 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24994
24995     title : '',
24996     icon : false,
24997     showtabs : true,
24998     tabScrollable : false,
24999     
25000     getChildContainer : function()
25001     {
25002         return this.el.select('.tab-content', true).first();
25003     },
25004     
25005     getAutoCreate : function(){
25006         
25007         var header = {
25008             tag: 'li',
25009             cls: 'pull-left header',
25010             html: this.title,
25011             cn : []
25012         };
25013         
25014         if(this.icon){
25015             header.cn.push({
25016                 tag: 'i',
25017                 cls: 'fa ' + this.icon
25018             });
25019         }
25020         
25021         var h = {
25022             tag: 'ul',
25023             cls: 'nav nav-tabs pull-right',
25024             cn: [
25025                 header
25026             ]
25027         };
25028         
25029         if(this.tabScrollable){
25030             h = {
25031                 tag: 'div',
25032                 cls: 'tab-header',
25033                 cn: [
25034                     {
25035                         tag: 'ul',
25036                         cls: 'nav nav-tabs pull-right',
25037                         cn: [
25038                             header
25039                         ]
25040                     }
25041                 ]
25042             };
25043         }
25044         
25045         var cfg = {
25046             tag: 'div',
25047             cls: 'nav-tabs-custom',
25048             cn: [
25049                 h,
25050                 {
25051                     tag: 'div',
25052                     cls: 'tab-content no-padding',
25053                     cn: []
25054                 }
25055             ]
25056         };
25057
25058         return  cfg;
25059     },
25060     initEvents : function()
25061     {
25062         //Roo.log('add add pane handler');
25063         this.on('addpane', this.onAddPane, this);
25064     },
25065      /**
25066      * Updates the box title
25067      * @param {String} html to set the title to.
25068      */
25069     setTitle : function(value)
25070     {
25071         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25072     },
25073     onAddPane : function(pane)
25074     {
25075         this.panes.push(pane);
25076         //Roo.log('addpane');
25077         //Roo.log(pane);
25078         // tabs are rendere left to right..
25079         if(!this.showtabs){
25080             return;
25081         }
25082         
25083         var ctr = this.el.select('.nav-tabs', true).first();
25084          
25085          
25086         var existing = ctr.select('.nav-tab',true);
25087         var qty = existing.getCount();;
25088         
25089         
25090         var tab = ctr.createChild({
25091             tag : 'li',
25092             cls : 'nav-tab' + (qty ? '' : ' active'),
25093             cn : [
25094                 {
25095                     tag : 'a',
25096                     href:'#',
25097                     html : pane.title
25098                 }
25099             ]
25100         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25101         pane.tab = tab;
25102         
25103         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25104         if (!qty) {
25105             pane.el.addClass('active');
25106         }
25107         
25108                 
25109     },
25110     onTabClick : function(ev,un,ob,pane)
25111     {
25112         //Roo.log('tab - prev default');
25113         ev.preventDefault();
25114         
25115         
25116         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25117         pane.tab.addClass('active');
25118         //Roo.log(pane.title);
25119         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25120         // technically we should have a deactivate event.. but maybe add later.
25121         // and it should not de-activate the selected tab...
25122         this.fireEvent('activatepane', pane);
25123         pane.el.addClass('active');
25124         pane.fireEvent('activate');
25125         
25126         
25127     },
25128     
25129     getActivePane : function()
25130     {
25131         var r = false;
25132         Roo.each(this.panes, function(p) {
25133             if(p.el.hasClass('active')){
25134                 r = p;
25135                 return false;
25136             }
25137             
25138             return;
25139         });
25140         
25141         return r;
25142     }
25143     
25144     
25145 });
25146
25147  
25148 /*
25149  * - LGPL
25150  *
25151  * Tab pane
25152  * 
25153  */
25154 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25155 /**
25156  * @class Roo.bootstrap.TabPane
25157  * @extends Roo.bootstrap.Component
25158  * Bootstrap TabPane class
25159  * @cfg {Boolean} active (false | true) Default false
25160  * @cfg {String} title title of panel
25161
25162  * 
25163  * @constructor
25164  * Create a new TabPane
25165  * @param {Object} config The config object
25166  */
25167
25168 Roo.bootstrap.dash.TabPane = function(config){
25169     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25170     
25171     this.addEvents({
25172         // raw events
25173         /**
25174          * @event activate
25175          * When a pane is activated
25176          * @param {Roo.bootstrap.dash.TabPane} pane
25177          */
25178         "activate" : true
25179          
25180     });
25181 };
25182
25183 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25184     
25185     active : false,
25186     title : '',
25187     
25188     // the tabBox that this is attached to.
25189     tab : false,
25190      
25191     getAutoCreate : function() 
25192     {
25193         var cfg = {
25194             tag: 'div',
25195             cls: 'tab-pane'
25196         };
25197         
25198         if(this.active){
25199             cfg.cls += ' active';
25200         }
25201         
25202         return cfg;
25203     },
25204     initEvents  : function()
25205     {
25206         //Roo.log('trigger add pane handler');
25207         this.parent().fireEvent('addpane', this)
25208     },
25209     
25210      /**
25211      * Updates the tab title 
25212      * @param {String} html to set the title to.
25213      */
25214     setTitle: function(str)
25215     {
25216         if (!this.tab) {
25217             return;
25218         }
25219         this.title = str;
25220         this.tab.select('a', true).first().dom.innerHTML = str;
25221         
25222     }
25223     
25224     
25225     
25226 });
25227
25228  
25229
25230
25231  /*
25232  * - LGPL
25233  *
25234  * menu
25235  * 
25236  */
25237 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25238
25239 /**
25240  * @class Roo.bootstrap.menu.Menu
25241  * @extends Roo.bootstrap.Component
25242  * Bootstrap Menu class - container for Menu
25243  * @cfg {String} html Text of the menu
25244  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25245  * @cfg {String} icon Font awesome icon
25246  * @cfg {String} pos Menu align to (top | bottom) default bottom
25247  * 
25248  * 
25249  * @constructor
25250  * Create a new Menu
25251  * @param {Object} config The config object
25252  */
25253
25254
25255 Roo.bootstrap.menu.Menu = function(config){
25256     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25257     
25258     this.addEvents({
25259         /**
25260          * @event beforeshow
25261          * Fires before this menu is displayed
25262          * @param {Roo.bootstrap.menu.Menu} this
25263          */
25264         beforeshow : true,
25265         /**
25266          * @event beforehide
25267          * Fires before this menu is hidden
25268          * @param {Roo.bootstrap.menu.Menu} this
25269          */
25270         beforehide : true,
25271         /**
25272          * @event show
25273          * Fires after this menu is displayed
25274          * @param {Roo.bootstrap.menu.Menu} this
25275          */
25276         show : true,
25277         /**
25278          * @event hide
25279          * Fires after this menu is hidden
25280          * @param {Roo.bootstrap.menu.Menu} this
25281          */
25282         hide : true,
25283         /**
25284          * @event click
25285          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25286          * @param {Roo.bootstrap.menu.Menu} this
25287          * @param {Roo.EventObject} e
25288          */
25289         click : true
25290     });
25291     
25292 };
25293
25294 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25295     
25296     submenu : false,
25297     html : '',
25298     weight : 'default',
25299     icon : false,
25300     pos : 'bottom',
25301     
25302     
25303     getChildContainer : function() {
25304         if(this.isSubMenu){
25305             return this.el;
25306         }
25307         
25308         return this.el.select('ul.dropdown-menu', true).first();  
25309     },
25310     
25311     getAutoCreate : function()
25312     {
25313         var text = [
25314             {
25315                 tag : 'span',
25316                 cls : 'roo-menu-text',
25317                 html : this.html
25318             }
25319         ];
25320         
25321         if(this.icon){
25322             text.unshift({
25323                 tag : 'i',
25324                 cls : 'fa ' + this.icon
25325             })
25326         }
25327         
25328         
25329         var cfg = {
25330             tag : 'div',
25331             cls : 'btn-group',
25332             cn : [
25333                 {
25334                     tag : 'button',
25335                     cls : 'dropdown-button btn btn-' + this.weight,
25336                     cn : text
25337                 },
25338                 {
25339                     tag : 'button',
25340                     cls : 'dropdown-toggle btn btn-' + this.weight,
25341                     cn : [
25342                         {
25343                             tag : 'span',
25344                             cls : 'caret'
25345                         }
25346                     ]
25347                 },
25348                 {
25349                     tag : 'ul',
25350                     cls : 'dropdown-menu'
25351                 }
25352             ]
25353             
25354         };
25355         
25356         if(this.pos == 'top'){
25357             cfg.cls += ' dropup';
25358         }
25359         
25360         if(this.isSubMenu){
25361             cfg = {
25362                 tag : 'ul',
25363                 cls : 'dropdown-menu'
25364             }
25365         }
25366         
25367         return cfg;
25368     },
25369     
25370     onRender : function(ct, position)
25371     {
25372         this.isSubMenu = ct.hasClass('dropdown-submenu');
25373         
25374         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25375     },
25376     
25377     initEvents : function() 
25378     {
25379         if(this.isSubMenu){
25380             return;
25381         }
25382         
25383         this.hidden = true;
25384         
25385         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25386         this.triggerEl.on('click', this.onTriggerPress, this);
25387         
25388         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25389         this.buttonEl.on('click', this.onClick, this);
25390         
25391     },
25392     
25393     list : function()
25394     {
25395         if(this.isSubMenu){
25396             return this.el;
25397         }
25398         
25399         return this.el.select('ul.dropdown-menu', true).first();
25400     },
25401     
25402     onClick : function(e)
25403     {
25404         this.fireEvent("click", this, e);
25405     },
25406     
25407     onTriggerPress  : function(e)
25408     {   
25409         if (this.isVisible()) {
25410             this.hide();
25411         } else {
25412             this.show();
25413         }
25414     },
25415     
25416     isVisible : function(){
25417         return !this.hidden;
25418     },
25419     
25420     show : function()
25421     {
25422         this.fireEvent("beforeshow", this);
25423         
25424         this.hidden = false;
25425         this.el.addClass('open');
25426         
25427         Roo.get(document).on("mouseup", this.onMouseUp, this);
25428         
25429         this.fireEvent("show", this);
25430         
25431         
25432     },
25433     
25434     hide : function()
25435     {
25436         this.fireEvent("beforehide", this);
25437         
25438         this.hidden = true;
25439         this.el.removeClass('open');
25440         
25441         Roo.get(document).un("mouseup", this.onMouseUp);
25442         
25443         this.fireEvent("hide", this);
25444     },
25445     
25446     onMouseUp : function()
25447     {
25448         this.hide();
25449     }
25450     
25451 });
25452
25453  
25454  /*
25455  * - LGPL
25456  *
25457  * menu item
25458  * 
25459  */
25460 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25461
25462 /**
25463  * @class Roo.bootstrap.menu.Item
25464  * @extends Roo.bootstrap.Component
25465  * Bootstrap MenuItem class
25466  * @cfg {Boolean} submenu (true | false) default false
25467  * @cfg {String} html text of the item
25468  * @cfg {String} href the link
25469  * @cfg {Boolean} disable (true | false) default false
25470  * @cfg {Boolean} preventDefault (true | false) default true
25471  * @cfg {String} icon Font awesome icon
25472  * @cfg {String} pos Submenu align to (left | right) default right 
25473  * 
25474  * 
25475  * @constructor
25476  * Create a new Item
25477  * @param {Object} config The config object
25478  */
25479
25480
25481 Roo.bootstrap.menu.Item = function(config){
25482     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25483     this.addEvents({
25484         /**
25485          * @event mouseover
25486          * Fires when the mouse is hovering over this menu
25487          * @param {Roo.bootstrap.menu.Item} this
25488          * @param {Roo.EventObject} e
25489          */
25490         mouseover : true,
25491         /**
25492          * @event mouseout
25493          * Fires when the mouse exits this menu
25494          * @param {Roo.bootstrap.menu.Item} this
25495          * @param {Roo.EventObject} e
25496          */
25497         mouseout : true,
25498         // raw events
25499         /**
25500          * @event click
25501          * The raw click event for the entire grid.
25502          * @param {Roo.EventObject} e
25503          */
25504         click : true
25505     });
25506 };
25507
25508 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25509     
25510     submenu : false,
25511     href : '',
25512     html : '',
25513     preventDefault: true,
25514     disable : false,
25515     icon : false,
25516     pos : 'right',
25517     
25518     getAutoCreate : function()
25519     {
25520         var text = [
25521             {
25522                 tag : 'span',
25523                 cls : 'roo-menu-item-text',
25524                 html : this.html
25525             }
25526         ];
25527         
25528         if(this.icon){
25529             text.unshift({
25530                 tag : 'i',
25531                 cls : 'fa ' + this.icon
25532             })
25533         }
25534         
25535         var cfg = {
25536             tag : 'li',
25537             cn : [
25538                 {
25539                     tag : 'a',
25540                     href : this.href || '#',
25541                     cn : text
25542                 }
25543             ]
25544         };
25545         
25546         if(this.disable){
25547             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25548         }
25549         
25550         if(this.submenu){
25551             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25552             
25553             if(this.pos == 'left'){
25554                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25555             }
25556         }
25557         
25558         return cfg;
25559     },
25560     
25561     initEvents : function() 
25562     {
25563         this.el.on('mouseover', this.onMouseOver, this);
25564         this.el.on('mouseout', this.onMouseOut, this);
25565         
25566         this.el.select('a', true).first().on('click', this.onClick, this);
25567         
25568     },
25569     
25570     onClick : function(e)
25571     {
25572         if(this.preventDefault){
25573             e.preventDefault();
25574         }
25575         
25576         this.fireEvent("click", this, e);
25577     },
25578     
25579     onMouseOver : function(e)
25580     {
25581         if(this.submenu && this.pos == 'left'){
25582             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25583         }
25584         
25585         this.fireEvent("mouseover", this, e);
25586     },
25587     
25588     onMouseOut : function(e)
25589     {
25590         this.fireEvent("mouseout", this, e);
25591     }
25592 });
25593
25594  
25595
25596  /*
25597  * - LGPL
25598  *
25599  * menu separator
25600  * 
25601  */
25602 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25603
25604 /**
25605  * @class Roo.bootstrap.menu.Separator
25606  * @extends Roo.bootstrap.Component
25607  * Bootstrap Separator class
25608  * 
25609  * @constructor
25610  * Create a new Separator
25611  * @param {Object} config The config object
25612  */
25613
25614
25615 Roo.bootstrap.menu.Separator = function(config){
25616     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25617 };
25618
25619 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25620     
25621     getAutoCreate : function(){
25622         var cfg = {
25623             tag : 'li',
25624             cls: 'divider'
25625         };
25626         
25627         return cfg;
25628     }
25629    
25630 });
25631
25632  
25633
25634  /*
25635  * - LGPL
25636  *
25637  * Tooltip
25638  * 
25639  */
25640
25641 /**
25642  * @class Roo.bootstrap.Tooltip
25643  * Bootstrap Tooltip class
25644  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25645  * to determine which dom element triggers the tooltip.
25646  * 
25647  * It needs to add support for additional attributes like tooltip-position
25648  * 
25649  * @constructor
25650  * Create a new Toolti
25651  * @param {Object} config The config object
25652  */
25653
25654 Roo.bootstrap.Tooltip = function(config){
25655     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25656     
25657     this.alignment = Roo.bootstrap.Tooltip.alignment;
25658     
25659     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25660         this.alignment = config.alignment;
25661     }
25662     
25663 };
25664
25665 Roo.apply(Roo.bootstrap.Tooltip, {
25666     /**
25667      * @function init initialize tooltip monitoring.
25668      * @static
25669      */
25670     currentEl : false,
25671     currentTip : false,
25672     currentRegion : false,
25673     
25674     //  init : delay?
25675     
25676     init : function()
25677     {
25678         Roo.get(document).on('mouseover', this.enter ,this);
25679         Roo.get(document).on('mouseout', this.leave, this);
25680          
25681         
25682         this.currentTip = new Roo.bootstrap.Tooltip();
25683     },
25684     
25685     enter : function(ev)
25686     {
25687         var dom = ev.getTarget();
25688         
25689         //Roo.log(['enter',dom]);
25690         var el = Roo.fly(dom);
25691         if (this.currentEl) {
25692             //Roo.log(dom);
25693             //Roo.log(this.currentEl);
25694             //Roo.log(this.currentEl.contains(dom));
25695             if (this.currentEl == el) {
25696                 return;
25697             }
25698             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25699                 return;
25700             }
25701
25702         }
25703         
25704         if (this.currentTip.el) {
25705             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25706         }    
25707         //Roo.log(ev);
25708         
25709         if(!el || el.dom == document){
25710             return;
25711         }
25712         
25713         var bindEl = el;
25714         
25715         // you can not look for children, as if el is the body.. then everythign is the child..
25716         if (!el.attr('tooltip')) { //
25717             if (!el.select("[tooltip]").elements.length) {
25718                 return;
25719             }
25720             // is the mouse over this child...?
25721             bindEl = el.select("[tooltip]").first();
25722             var xy = ev.getXY();
25723             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25724                 //Roo.log("not in region.");
25725                 return;
25726             }
25727             //Roo.log("child element over..");
25728             
25729         }
25730         this.currentEl = bindEl;
25731         this.currentTip.bind(bindEl);
25732         this.currentRegion = Roo.lib.Region.getRegion(dom);
25733         this.currentTip.enter();
25734         
25735     },
25736     leave : function(ev)
25737     {
25738         var dom = ev.getTarget();
25739         //Roo.log(['leave',dom]);
25740         if (!this.currentEl) {
25741             return;
25742         }
25743         
25744         
25745         if (dom != this.currentEl.dom) {
25746             return;
25747         }
25748         var xy = ev.getXY();
25749         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25750             return;
25751         }
25752         // only activate leave if mouse cursor is outside... bounding box..
25753         
25754         
25755         
25756         
25757         if (this.currentTip) {
25758             this.currentTip.leave();
25759         }
25760         //Roo.log('clear currentEl');
25761         this.currentEl = false;
25762         
25763         
25764     },
25765     alignment : {
25766         'left' : ['r-l', [-2,0], 'right'],
25767         'right' : ['l-r', [2,0], 'left'],
25768         'bottom' : ['t-b', [0,2], 'top'],
25769         'top' : [ 'b-t', [0,-2], 'bottom']
25770     }
25771     
25772 });
25773
25774
25775 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25776     
25777     
25778     bindEl : false,
25779     
25780     delay : null, // can be { show : 300 , hide: 500}
25781     
25782     timeout : null,
25783     
25784     hoverState : null, //???
25785     
25786     placement : 'bottom', 
25787     
25788     alignment : false,
25789     
25790     getAutoCreate : function(){
25791     
25792         var cfg = {
25793            cls : 'tooltip',
25794            role : 'tooltip',
25795            cn : [
25796                 {
25797                     cls : 'tooltip-arrow'
25798                 },
25799                 {
25800                     cls : 'tooltip-inner'
25801                 }
25802            ]
25803         };
25804         
25805         return cfg;
25806     },
25807     bind : function(el)
25808     {
25809         this.bindEl = el;
25810     },
25811       
25812     
25813     enter : function () {
25814        
25815         if (this.timeout != null) {
25816             clearTimeout(this.timeout);
25817         }
25818         
25819         this.hoverState = 'in';
25820          //Roo.log("enter - show");
25821         if (!this.delay || !this.delay.show) {
25822             this.show();
25823             return;
25824         }
25825         var _t = this;
25826         this.timeout = setTimeout(function () {
25827             if (_t.hoverState == 'in') {
25828                 _t.show();
25829             }
25830         }, this.delay.show);
25831     },
25832     leave : function()
25833     {
25834         clearTimeout(this.timeout);
25835     
25836         this.hoverState = 'out';
25837          if (!this.delay || !this.delay.hide) {
25838             this.hide();
25839             return;
25840         }
25841        
25842         var _t = this;
25843         this.timeout = setTimeout(function () {
25844             //Roo.log("leave - timeout");
25845             
25846             if (_t.hoverState == 'out') {
25847                 _t.hide();
25848                 Roo.bootstrap.Tooltip.currentEl = false;
25849             }
25850         }, delay);
25851     },
25852     
25853     show : function (msg)
25854     {
25855         if (!this.el) {
25856             this.render(document.body);
25857         }
25858         // set content.
25859         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25860         
25861         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25862         
25863         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25864         
25865         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25866         
25867         var placement = typeof this.placement == 'function' ?
25868             this.placement.call(this, this.el, on_el) :
25869             this.placement;
25870             
25871         var autoToken = /\s?auto?\s?/i;
25872         var autoPlace = autoToken.test(placement);
25873         if (autoPlace) {
25874             placement = placement.replace(autoToken, '') || 'top';
25875         }
25876         
25877         //this.el.detach()
25878         //this.el.setXY([0,0]);
25879         this.el.show();
25880         //this.el.dom.style.display='block';
25881         
25882         //this.el.appendTo(on_el);
25883         
25884         var p = this.getPosition();
25885         var box = this.el.getBox();
25886         
25887         if (autoPlace) {
25888             // fixme..
25889         }
25890         
25891         var align = this.alignment[placement];
25892         
25893         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25894         
25895         if(placement == 'top' || placement == 'bottom'){
25896             if(xy[0] < 0){
25897                 placement = 'right';
25898             }
25899             
25900             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25901                 placement = 'left';
25902             }
25903             
25904             var scroll = Roo.select('body', true).first().getScroll();
25905             
25906             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25907                 placement = 'top';
25908             }
25909             
25910         }
25911         
25912         this.el.alignTo(this.bindEl, align[0],align[1]);
25913         //var arrow = this.el.select('.arrow',true).first();
25914         //arrow.set(align[2], 
25915         
25916         this.el.addClass(placement);
25917         
25918         this.el.addClass('in fade');
25919         
25920         this.hoverState = null;
25921         
25922         if (this.el.hasClass('fade')) {
25923             // fade it?
25924         }
25925         
25926     },
25927     hide : function()
25928     {
25929          
25930         if (!this.el) {
25931             return;
25932         }
25933         //this.el.setXY([0,0]);
25934         this.el.removeClass('in');
25935         //this.el.hide();
25936         
25937     }
25938     
25939 });
25940  
25941
25942  /*
25943  * - LGPL
25944  *
25945  * Location Picker
25946  * 
25947  */
25948
25949 /**
25950  * @class Roo.bootstrap.LocationPicker
25951  * @extends Roo.bootstrap.Component
25952  * Bootstrap LocationPicker class
25953  * @cfg {Number} latitude Position when init default 0
25954  * @cfg {Number} longitude Position when init default 0
25955  * @cfg {Number} zoom default 15
25956  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25957  * @cfg {Boolean} mapTypeControl default false
25958  * @cfg {Boolean} disableDoubleClickZoom default false
25959  * @cfg {Boolean} scrollwheel default true
25960  * @cfg {Boolean} streetViewControl default false
25961  * @cfg {Number} radius default 0
25962  * @cfg {String} locationName
25963  * @cfg {Boolean} draggable default true
25964  * @cfg {Boolean} enableAutocomplete default false
25965  * @cfg {Boolean} enableReverseGeocode default true
25966  * @cfg {String} markerTitle
25967  * 
25968  * @constructor
25969  * Create a new LocationPicker
25970  * @param {Object} config The config object
25971  */
25972
25973
25974 Roo.bootstrap.LocationPicker = function(config){
25975     
25976     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25977     
25978     this.addEvents({
25979         /**
25980          * @event initial
25981          * Fires when the picker initialized.
25982          * @param {Roo.bootstrap.LocationPicker} this
25983          * @param {Google Location} location
25984          */
25985         initial : true,
25986         /**
25987          * @event positionchanged
25988          * Fires when the picker position changed.
25989          * @param {Roo.bootstrap.LocationPicker} this
25990          * @param {Google Location} location
25991          */
25992         positionchanged : true,
25993         /**
25994          * @event resize
25995          * Fires when the map resize.
25996          * @param {Roo.bootstrap.LocationPicker} this
25997          */
25998         resize : true,
25999         /**
26000          * @event show
26001          * Fires when the map show.
26002          * @param {Roo.bootstrap.LocationPicker} this
26003          */
26004         show : true,
26005         /**
26006          * @event hide
26007          * Fires when the map hide.
26008          * @param {Roo.bootstrap.LocationPicker} this
26009          */
26010         hide : true,
26011         /**
26012          * @event mapClick
26013          * Fires when click the map.
26014          * @param {Roo.bootstrap.LocationPicker} this
26015          * @param {Map event} e
26016          */
26017         mapClick : true,
26018         /**
26019          * @event mapRightClick
26020          * Fires when right click the map.
26021          * @param {Roo.bootstrap.LocationPicker} this
26022          * @param {Map event} e
26023          */
26024         mapRightClick : true,
26025         /**
26026          * @event markerClick
26027          * Fires when click the marker.
26028          * @param {Roo.bootstrap.LocationPicker} this
26029          * @param {Map event} e
26030          */
26031         markerClick : true,
26032         /**
26033          * @event markerRightClick
26034          * Fires when right click the marker.
26035          * @param {Roo.bootstrap.LocationPicker} this
26036          * @param {Map event} e
26037          */
26038         markerRightClick : true,
26039         /**
26040          * @event OverlayViewDraw
26041          * Fires when OverlayView Draw
26042          * @param {Roo.bootstrap.LocationPicker} this
26043          */
26044         OverlayViewDraw : true,
26045         /**
26046          * @event OverlayViewOnAdd
26047          * Fires when OverlayView Draw
26048          * @param {Roo.bootstrap.LocationPicker} this
26049          */
26050         OverlayViewOnAdd : true,
26051         /**
26052          * @event OverlayViewOnRemove
26053          * Fires when OverlayView Draw
26054          * @param {Roo.bootstrap.LocationPicker} this
26055          */
26056         OverlayViewOnRemove : true,
26057         /**
26058          * @event OverlayViewShow
26059          * Fires when OverlayView Draw
26060          * @param {Roo.bootstrap.LocationPicker} this
26061          * @param {Pixel} cpx
26062          */
26063         OverlayViewShow : true,
26064         /**
26065          * @event OverlayViewHide
26066          * Fires when OverlayView Draw
26067          * @param {Roo.bootstrap.LocationPicker} this
26068          */
26069         OverlayViewHide : true,
26070         /**
26071          * @event loadexception
26072          * Fires when load google lib failed.
26073          * @param {Roo.bootstrap.LocationPicker} this
26074          */
26075         loadexception : true
26076     });
26077         
26078 };
26079
26080 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26081     
26082     gMapContext: false,
26083     
26084     latitude: 0,
26085     longitude: 0,
26086     zoom: 15,
26087     mapTypeId: false,
26088     mapTypeControl: false,
26089     disableDoubleClickZoom: false,
26090     scrollwheel: true,
26091     streetViewControl: false,
26092     radius: 0,
26093     locationName: '',
26094     draggable: true,
26095     enableAutocomplete: false,
26096     enableReverseGeocode: true,
26097     markerTitle: '',
26098     
26099     getAutoCreate: function()
26100     {
26101
26102         var cfg = {
26103             tag: 'div',
26104             cls: 'roo-location-picker'
26105         };
26106         
26107         return cfg
26108     },
26109     
26110     initEvents: function(ct, position)
26111     {       
26112         if(!this.el.getWidth() || this.isApplied()){
26113             return;
26114         }
26115         
26116         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26117         
26118         this.initial();
26119     },
26120     
26121     initial: function()
26122     {
26123         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26124             this.fireEvent('loadexception', this);
26125             return;
26126         }
26127         
26128         if(!this.mapTypeId){
26129             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26130         }
26131         
26132         this.gMapContext = this.GMapContext();
26133         
26134         this.initOverlayView();
26135         
26136         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26137         
26138         var _this = this;
26139                 
26140         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26141             _this.setPosition(_this.gMapContext.marker.position);
26142         });
26143         
26144         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26145             _this.fireEvent('mapClick', this, event);
26146             
26147         });
26148
26149         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26150             _this.fireEvent('mapRightClick', this, event);
26151             
26152         });
26153         
26154         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26155             _this.fireEvent('markerClick', this, event);
26156             
26157         });
26158
26159         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26160             _this.fireEvent('markerRightClick', this, event);
26161             
26162         });
26163         
26164         this.setPosition(this.gMapContext.location);
26165         
26166         this.fireEvent('initial', this, this.gMapContext.location);
26167     },
26168     
26169     initOverlayView: function()
26170     {
26171         var _this = this;
26172         
26173         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26174             
26175             draw: function()
26176             {
26177                 _this.fireEvent('OverlayViewDraw', _this);
26178             },
26179             
26180             onAdd: function()
26181             {
26182                 _this.fireEvent('OverlayViewOnAdd', _this);
26183             },
26184             
26185             onRemove: function()
26186             {
26187                 _this.fireEvent('OverlayViewOnRemove', _this);
26188             },
26189             
26190             show: function(cpx)
26191             {
26192                 _this.fireEvent('OverlayViewShow', _this, cpx);
26193             },
26194             
26195             hide: function()
26196             {
26197                 _this.fireEvent('OverlayViewHide', _this);
26198             }
26199             
26200         });
26201     },
26202     
26203     fromLatLngToContainerPixel: function(event)
26204     {
26205         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26206     },
26207     
26208     isApplied: function() 
26209     {
26210         return this.getGmapContext() == false ? false : true;
26211     },
26212     
26213     getGmapContext: function() 
26214     {
26215         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26216     },
26217     
26218     GMapContext: function() 
26219     {
26220         var position = new google.maps.LatLng(this.latitude, this.longitude);
26221         
26222         var _map = new google.maps.Map(this.el.dom, {
26223             center: position,
26224             zoom: this.zoom,
26225             mapTypeId: this.mapTypeId,
26226             mapTypeControl: this.mapTypeControl,
26227             disableDoubleClickZoom: this.disableDoubleClickZoom,
26228             scrollwheel: this.scrollwheel,
26229             streetViewControl: this.streetViewControl,
26230             locationName: this.locationName,
26231             draggable: this.draggable,
26232             enableAutocomplete: this.enableAutocomplete,
26233             enableReverseGeocode: this.enableReverseGeocode
26234         });
26235         
26236         var _marker = new google.maps.Marker({
26237             position: position,
26238             map: _map,
26239             title: this.markerTitle,
26240             draggable: this.draggable
26241         });
26242         
26243         return {
26244             map: _map,
26245             marker: _marker,
26246             circle: null,
26247             location: position,
26248             radius: this.radius,
26249             locationName: this.locationName,
26250             addressComponents: {
26251                 formatted_address: null,
26252                 addressLine1: null,
26253                 addressLine2: null,
26254                 streetName: null,
26255                 streetNumber: null,
26256                 city: null,
26257                 district: null,
26258                 state: null,
26259                 stateOrProvince: null
26260             },
26261             settings: this,
26262             domContainer: this.el.dom,
26263             geodecoder: new google.maps.Geocoder()
26264         };
26265     },
26266     
26267     drawCircle: function(center, radius, options) 
26268     {
26269         if (this.gMapContext.circle != null) {
26270             this.gMapContext.circle.setMap(null);
26271         }
26272         if (radius > 0) {
26273             radius *= 1;
26274             options = Roo.apply({}, options, {
26275                 strokeColor: "#0000FF",
26276                 strokeOpacity: .35,
26277                 strokeWeight: 2,
26278                 fillColor: "#0000FF",
26279                 fillOpacity: .2
26280             });
26281             
26282             options.map = this.gMapContext.map;
26283             options.radius = radius;
26284             options.center = center;
26285             this.gMapContext.circle = new google.maps.Circle(options);
26286             return this.gMapContext.circle;
26287         }
26288         
26289         return null;
26290     },
26291     
26292     setPosition: function(location) 
26293     {
26294         this.gMapContext.location = location;
26295         this.gMapContext.marker.setPosition(location);
26296         this.gMapContext.map.panTo(location);
26297         this.drawCircle(location, this.gMapContext.radius, {});
26298         
26299         var _this = this;
26300         
26301         if (this.gMapContext.settings.enableReverseGeocode) {
26302             this.gMapContext.geodecoder.geocode({
26303                 latLng: this.gMapContext.location
26304             }, function(results, status) {
26305                 
26306                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26307                     _this.gMapContext.locationName = results[0].formatted_address;
26308                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26309                     
26310                     _this.fireEvent('positionchanged', this, location);
26311                 }
26312             });
26313             
26314             return;
26315         }
26316         
26317         this.fireEvent('positionchanged', this, location);
26318     },
26319     
26320     resize: function()
26321     {
26322         google.maps.event.trigger(this.gMapContext.map, "resize");
26323         
26324         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26325         
26326         this.fireEvent('resize', this);
26327     },
26328     
26329     setPositionByLatLng: function(latitude, longitude)
26330     {
26331         this.setPosition(new google.maps.LatLng(latitude, longitude));
26332     },
26333     
26334     getCurrentPosition: function() 
26335     {
26336         return {
26337             latitude: this.gMapContext.location.lat(),
26338             longitude: this.gMapContext.location.lng()
26339         };
26340     },
26341     
26342     getAddressName: function() 
26343     {
26344         return this.gMapContext.locationName;
26345     },
26346     
26347     getAddressComponents: function() 
26348     {
26349         return this.gMapContext.addressComponents;
26350     },
26351     
26352     address_component_from_google_geocode: function(address_components) 
26353     {
26354         var result = {};
26355         
26356         for (var i = 0; i < address_components.length; i++) {
26357             var component = address_components[i];
26358             if (component.types.indexOf("postal_code") >= 0) {
26359                 result.postalCode = component.short_name;
26360             } else if (component.types.indexOf("street_number") >= 0) {
26361                 result.streetNumber = component.short_name;
26362             } else if (component.types.indexOf("route") >= 0) {
26363                 result.streetName = component.short_name;
26364             } else if (component.types.indexOf("neighborhood") >= 0) {
26365                 result.city = component.short_name;
26366             } else if (component.types.indexOf("locality") >= 0) {
26367                 result.city = component.short_name;
26368             } else if (component.types.indexOf("sublocality") >= 0) {
26369                 result.district = component.short_name;
26370             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26371                 result.stateOrProvince = component.short_name;
26372             } else if (component.types.indexOf("country") >= 0) {
26373                 result.country = component.short_name;
26374             }
26375         }
26376         
26377         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26378         result.addressLine2 = "";
26379         return result;
26380     },
26381     
26382     setZoomLevel: function(zoom)
26383     {
26384         this.gMapContext.map.setZoom(zoom);
26385     },
26386     
26387     show: function()
26388     {
26389         if(!this.el){
26390             return;
26391         }
26392         
26393         this.el.show();
26394         
26395         this.resize();
26396         
26397         this.fireEvent('show', this);
26398     },
26399     
26400     hide: function()
26401     {
26402         if(!this.el){
26403             return;
26404         }
26405         
26406         this.el.hide();
26407         
26408         this.fireEvent('hide', this);
26409     }
26410     
26411 });
26412
26413 Roo.apply(Roo.bootstrap.LocationPicker, {
26414     
26415     OverlayView : function(map, options)
26416     {
26417         options = options || {};
26418         
26419         this.setMap(map);
26420     }
26421     
26422     
26423 });/*
26424  * - LGPL
26425  *
26426  * Alert
26427  * 
26428  */
26429
26430 /**
26431  * @class Roo.bootstrap.Alert
26432  * @extends Roo.bootstrap.Component
26433  * Bootstrap Alert class
26434  * @cfg {String} title The title of alert
26435  * @cfg {String} html The content of alert
26436  * @cfg {String} weight (  success | info | warning | danger )
26437  * @cfg {String} faicon font-awesomeicon
26438  * 
26439  * @constructor
26440  * Create a new alert
26441  * @param {Object} config The config object
26442  */
26443
26444
26445 Roo.bootstrap.Alert = function(config){
26446     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26447     
26448 };
26449
26450 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26451     
26452     title: '',
26453     html: '',
26454     weight: false,
26455     faicon: false,
26456     
26457     getAutoCreate : function()
26458     {
26459         
26460         var cfg = {
26461             tag : 'div',
26462             cls : 'alert',
26463             cn : [
26464                 {
26465                     tag : 'i',
26466                     cls : 'roo-alert-icon'
26467                     
26468                 },
26469                 {
26470                     tag : 'b',
26471                     cls : 'roo-alert-title',
26472                     html : this.title
26473                 },
26474                 {
26475                     tag : 'span',
26476                     cls : 'roo-alert-text',
26477                     html : this.html
26478                 }
26479             ]
26480         };
26481         
26482         if(this.faicon){
26483             cfg.cn[0].cls += ' fa ' + this.faicon;
26484         }
26485         
26486         if(this.weight){
26487             cfg.cls += ' alert-' + this.weight;
26488         }
26489         
26490         return cfg;
26491     },
26492     
26493     initEvents: function() 
26494     {
26495         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26496     },
26497     
26498     setTitle : function(str)
26499     {
26500         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26501     },
26502     
26503     setText : function(str)
26504     {
26505         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26506     },
26507     
26508     setWeight : function(weight)
26509     {
26510         if(this.weight){
26511             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26512         }
26513         
26514         this.weight = weight;
26515         
26516         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26517     },
26518     
26519     setIcon : function(icon)
26520     {
26521         if(this.faicon){
26522             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26523         }
26524         
26525         this.faicon = icon;
26526         
26527         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26528     },
26529     
26530     hide: function() 
26531     {
26532         this.el.hide();   
26533     },
26534     
26535     show: function() 
26536     {  
26537         this.el.show();   
26538     }
26539     
26540 });
26541
26542  
26543 /*
26544 * Licence: LGPL
26545 */
26546
26547 /**
26548  * @class Roo.bootstrap.UploadCropbox
26549  * @extends Roo.bootstrap.Component
26550  * Bootstrap UploadCropbox class
26551  * @cfg {String} emptyText show when image has been loaded
26552  * @cfg {String} rotateNotify show when image too small to rotate
26553  * @cfg {Number} errorTimeout default 3000
26554  * @cfg {Number} minWidth default 300
26555  * @cfg {Number} minHeight default 300
26556  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26557  * @cfg {Boolean} isDocument (true|false) default false
26558  * @cfg {String} url action url
26559  * @cfg {String} paramName default 'imageUpload'
26560  * @cfg {String} method default POST
26561  * @cfg {Boolean} loadMask (true|false) default true
26562  * @cfg {Boolean} loadingText default 'Loading...'
26563  * 
26564  * @constructor
26565  * Create a new UploadCropbox
26566  * @param {Object} config The config object
26567  */
26568
26569 Roo.bootstrap.UploadCropbox = function(config){
26570     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26571     
26572     this.addEvents({
26573         /**
26574          * @event beforeselectfile
26575          * Fire before select file
26576          * @param {Roo.bootstrap.UploadCropbox} this
26577          */
26578         "beforeselectfile" : true,
26579         /**
26580          * @event initial
26581          * Fire after initEvent
26582          * @param {Roo.bootstrap.UploadCropbox} this
26583          */
26584         "initial" : true,
26585         /**
26586          * @event crop
26587          * Fire after initEvent
26588          * @param {Roo.bootstrap.UploadCropbox} this
26589          * @param {String} data
26590          */
26591         "crop" : true,
26592         /**
26593          * @event prepare
26594          * Fire when preparing the file data
26595          * @param {Roo.bootstrap.UploadCropbox} this
26596          * @param {Object} file
26597          */
26598         "prepare" : true,
26599         /**
26600          * @event exception
26601          * Fire when get exception
26602          * @param {Roo.bootstrap.UploadCropbox} this
26603          * @param {XMLHttpRequest} xhr
26604          */
26605         "exception" : true,
26606         /**
26607          * @event beforeloadcanvas
26608          * Fire before load the canvas
26609          * @param {Roo.bootstrap.UploadCropbox} this
26610          * @param {String} src
26611          */
26612         "beforeloadcanvas" : true,
26613         /**
26614          * @event trash
26615          * Fire when trash image
26616          * @param {Roo.bootstrap.UploadCropbox} this
26617          */
26618         "trash" : true,
26619         /**
26620          * @event download
26621          * Fire when download the image
26622          * @param {Roo.bootstrap.UploadCropbox} this
26623          */
26624         "download" : true,
26625         /**
26626          * @event footerbuttonclick
26627          * Fire when footerbuttonclick
26628          * @param {Roo.bootstrap.UploadCropbox} this
26629          * @param {String} type
26630          */
26631         "footerbuttonclick" : true,
26632         /**
26633          * @event resize
26634          * Fire when resize
26635          * @param {Roo.bootstrap.UploadCropbox} this
26636          */
26637         "resize" : true,
26638         /**
26639          * @event rotate
26640          * Fire when rotate the image
26641          * @param {Roo.bootstrap.UploadCropbox} this
26642          * @param {String} pos
26643          */
26644         "rotate" : true,
26645         /**
26646          * @event inspect
26647          * Fire when inspect the file
26648          * @param {Roo.bootstrap.UploadCropbox} this
26649          * @param {Object} file
26650          */
26651         "inspect" : true,
26652         /**
26653          * @event upload
26654          * Fire when xhr upload the file
26655          * @param {Roo.bootstrap.UploadCropbox} this
26656          * @param {Object} data
26657          */
26658         "upload" : true,
26659         /**
26660          * @event arrange
26661          * Fire when arrange the file data
26662          * @param {Roo.bootstrap.UploadCropbox} this
26663          * @param {Object} formData
26664          */
26665         "arrange" : true
26666     });
26667     
26668     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26669 };
26670
26671 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26672     
26673     emptyText : 'Click to upload image',
26674     rotateNotify : 'Image is too small to rotate',
26675     errorTimeout : 3000,
26676     scale : 0,
26677     baseScale : 1,
26678     rotate : 0,
26679     dragable : false,
26680     pinching : false,
26681     mouseX : 0,
26682     mouseY : 0,
26683     cropData : false,
26684     minWidth : 300,
26685     minHeight : 300,
26686     file : false,
26687     exif : {},
26688     baseRotate : 1,
26689     cropType : 'image/jpeg',
26690     buttons : false,
26691     canvasLoaded : false,
26692     isDocument : false,
26693     method : 'POST',
26694     paramName : 'imageUpload',
26695     loadMask : true,
26696     loadingText : 'Loading...',
26697     maskEl : false,
26698     
26699     getAutoCreate : function()
26700     {
26701         var cfg = {
26702             tag : 'div',
26703             cls : 'roo-upload-cropbox',
26704             cn : [
26705                 {
26706                     tag : 'input',
26707                     cls : 'roo-upload-cropbox-selector',
26708                     type : 'file'
26709                 },
26710                 {
26711                     tag : 'div',
26712                     cls : 'roo-upload-cropbox-body',
26713                     style : 'cursor:pointer',
26714                     cn : [
26715                         {
26716                             tag : 'div',
26717                             cls : 'roo-upload-cropbox-preview'
26718                         },
26719                         {
26720                             tag : 'div',
26721                             cls : 'roo-upload-cropbox-thumb'
26722                         },
26723                         {
26724                             tag : 'div',
26725                             cls : 'roo-upload-cropbox-empty-notify',
26726                             html : this.emptyText
26727                         },
26728                         {
26729                             tag : 'div',
26730                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26731                             html : this.rotateNotify
26732                         }
26733                     ]
26734                 },
26735                 {
26736                     tag : 'div',
26737                     cls : 'roo-upload-cropbox-footer',
26738                     cn : {
26739                         tag : 'div',
26740                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26741                         cn : []
26742                     }
26743                 }
26744             ]
26745         };
26746         
26747         return cfg;
26748     },
26749     
26750     onRender : function(ct, position)
26751     {
26752         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26753         
26754         if (this.buttons.length) {
26755             
26756             Roo.each(this.buttons, function(bb) {
26757                 
26758                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26759                 
26760                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26761                 
26762             }, this);
26763         }
26764         
26765         if(this.loadMask){
26766             this.maskEl = this.el;
26767         }
26768     },
26769     
26770     initEvents : function()
26771     {
26772         this.urlAPI = (window.createObjectURL && window) || 
26773                                 (window.URL && URL.revokeObjectURL && URL) || 
26774                                 (window.webkitURL && webkitURL);
26775                         
26776         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26777         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26778         
26779         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26780         this.selectorEl.hide();
26781         
26782         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26783         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26784         
26785         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26786         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26787         this.thumbEl.hide();
26788         
26789         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26790         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26791         
26792         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26793         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26794         this.errorEl.hide();
26795         
26796         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26797         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26798         this.footerEl.hide();
26799         
26800         this.setThumbBoxSize();
26801         
26802         this.bind();
26803         
26804         this.resize();
26805         
26806         this.fireEvent('initial', this);
26807     },
26808
26809     bind : function()
26810     {
26811         var _this = this;
26812         
26813         window.addEventListener("resize", function() { _this.resize(); } );
26814         
26815         this.bodyEl.on('click', this.beforeSelectFile, this);
26816         
26817         if(Roo.isTouch){
26818             this.bodyEl.on('touchstart', this.onTouchStart, this);
26819             this.bodyEl.on('touchmove', this.onTouchMove, this);
26820             this.bodyEl.on('touchend', this.onTouchEnd, this);
26821         }
26822         
26823         if(!Roo.isTouch){
26824             this.bodyEl.on('mousedown', this.onMouseDown, this);
26825             this.bodyEl.on('mousemove', this.onMouseMove, this);
26826             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26827             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26828             Roo.get(document).on('mouseup', this.onMouseUp, this);
26829         }
26830         
26831         this.selectorEl.on('change', this.onFileSelected, this);
26832     },
26833     
26834     reset : function()
26835     {    
26836         this.scale = 0;
26837         this.baseScale = 1;
26838         this.rotate = 0;
26839         this.baseRotate = 1;
26840         this.dragable = false;
26841         this.pinching = false;
26842         this.mouseX = 0;
26843         this.mouseY = 0;
26844         this.cropData = false;
26845         this.notifyEl.dom.innerHTML = this.emptyText;
26846         
26847         this.selectorEl.dom.value = '';
26848         
26849     },
26850     
26851     resize : function()
26852     {
26853         if(this.fireEvent('resize', this) != false){
26854             this.setThumbBoxPosition();
26855             this.setCanvasPosition();
26856         }
26857     },
26858     
26859     onFooterButtonClick : function(e, el, o, type)
26860     {
26861         switch (type) {
26862             case 'rotate-left' :
26863                 this.onRotateLeft(e);
26864                 break;
26865             case 'rotate-right' :
26866                 this.onRotateRight(e);
26867                 break;
26868             case 'picture' :
26869                 this.beforeSelectFile(e);
26870                 break;
26871             case 'trash' :
26872                 this.trash(e);
26873                 break;
26874             case 'crop' :
26875                 this.crop(e);
26876                 break;
26877             case 'download' :
26878                 this.download(e);
26879                 break;
26880             default :
26881                 break;
26882         }
26883         
26884         this.fireEvent('footerbuttonclick', this, type);
26885     },
26886     
26887     beforeSelectFile : function(e)
26888     {
26889         e.preventDefault();
26890         
26891         if(this.fireEvent('beforeselectfile', this) != false){
26892             this.selectorEl.dom.click();
26893         }
26894     },
26895     
26896     onFileSelected : function(e)
26897     {
26898         e.preventDefault();
26899         
26900         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26901             return;
26902         }
26903         
26904         var file = this.selectorEl.dom.files[0];
26905         
26906         if(this.fireEvent('inspect', this, file) != false){
26907             this.prepare(file);
26908         }
26909         
26910     },
26911     
26912     trash : function(e)
26913     {
26914         this.fireEvent('trash', this);
26915     },
26916     
26917     download : function(e)
26918     {
26919         this.fireEvent('download', this);
26920     },
26921     
26922     loadCanvas : function(src)
26923     {   
26924         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26925             
26926             this.reset();
26927             
26928             this.imageEl = document.createElement('img');
26929             
26930             var _this = this;
26931             
26932             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26933             
26934             this.imageEl.src = src;
26935         }
26936     },
26937     
26938     onLoadCanvas : function()
26939     {   
26940         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26941         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26942         
26943         this.bodyEl.un('click', this.beforeSelectFile, this);
26944         
26945         this.notifyEl.hide();
26946         this.thumbEl.show();
26947         this.footerEl.show();
26948         
26949         this.baseRotateLevel();
26950         
26951         if(this.isDocument){
26952             this.setThumbBoxSize();
26953         }
26954         
26955         this.setThumbBoxPosition();
26956         
26957         this.baseScaleLevel();
26958         
26959         this.draw();
26960         
26961         this.resize();
26962         
26963         this.canvasLoaded = true;
26964         
26965         if(this.loadMask){
26966             this.maskEl.unmask();
26967         }
26968         
26969     },
26970     
26971     setCanvasPosition : function()
26972     {   
26973         if(!this.canvasEl){
26974             return;
26975         }
26976         
26977         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26978         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26979         
26980         this.previewEl.setLeft(pw);
26981         this.previewEl.setTop(ph);
26982         
26983     },
26984     
26985     onMouseDown : function(e)
26986     {   
26987         e.stopEvent();
26988         
26989         this.dragable = true;
26990         this.pinching = false;
26991         
26992         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26993             this.dragable = false;
26994             return;
26995         }
26996         
26997         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26998         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26999         
27000     },
27001     
27002     onMouseMove : function(e)
27003     {   
27004         e.stopEvent();
27005         
27006         if(!this.canvasLoaded){
27007             return;
27008         }
27009         
27010         if (!this.dragable){
27011             return;
27012         }
27013         
27014         var minX = Math.ceil(this.thumbEl.getLeft(true));
27015         var minY = Math.ceil(this.thumbEl.getTop(true));
27016         
27017         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27018         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27019         
27020         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27021         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27022         
27023         x = x - this.mouseX;
27024         y = y - this.mouseY;
27025         
27026         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27027         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27028         
27029         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27030         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27031         
27032         this.previewEl.setLeft(bgX);
27033         this.previewEl.setTop(bgY);
27034         
27035         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27036         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27037     },
27038     
27039     onMouseUp : function(e)
27040     {   
27041         e.stopEvent();
27042         
27043         this.dragable = false;
27044     },
27045     
27046     onMouseWheel : function(e)
27047     {   
27048         e.stopEvent();
27049         
27050         this.startScale = this.scale;
27051         
27052         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27053         
27054         if(!this.zoomable()){
27055             this.scale = this.startScale;
27056             return;
27057         }
27058         
27059         this.draw();
27060         
27061         return;
27062     },
27063     
27064     zoomable : function()
27065     {
27066         var minScale = this.thumbEl.getWidth() / this.minWidth;
27067         
27068         if(this.minWidth < this.minHeight){
27069             minScale = this.thumbEl.getHeight() / this.minHeight;
27070         }
27071         
27072         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27073         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27074         
27075         if(
27076                 this.isDocument &&
27077                 (this.rotate == 0 || this.rotate == 180) && 
27078                 (
27079                     width > this.imageEl.OriginWidth || 
27080                     height > this.imageEl.OriginHeight ||
27081                     (width < this.minWidth && height < this.minHeight)
27082                 )
27083         ){
27084             return false;
27085         }
27086         
27087         if(
27088                 this.isDocument &&
27089                 (this.rotate == 90 || this.rotate == 270) && 
27090                 (
27091                     width > this.imageEl.OriginWidth || 
27092                     height > this.imageEl.OriginHeight ||
27093                     (width < this.minHeight && height < this.minWidth)
27094                 )
27095         ){
27096             return false;
27097         }
27098         
27099         if(
27100                 !this.isDocument &&
27101                 (this.rotate == 0 || this.rotate == 180) && 
27102                 (
27103                     width < this.minWidth || 
27104                     width > this.imageEl.OriginWidth || 
27105                     height < this.minHeight || 
27106                     height > this.imageEl.OriginHeight
27107                 )
27108         ){
27109             return false;
27110         }
27111         
27112         if(
27113                 !this.isDocument &&
27114                 (this.rotate == 90 || this.rotate == 270) && 
27115                 (
27116                     width < this.minHeight || 
27117                     width > this.imageEl.OriginWidth || 
27118                     height < this.minWidth || 
27119                     height > this.imageEl.OriginHeight
27120                 )
27121         ){
27122             return false;
27123         }
27124         
27125         return true;
27126         
27127     },
27128     
27129     onRotateLeft : function(e)
27130     {   
27131         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27132             
27133             var minScale = this.thumbEl.getWidth() / this.minWidth;
27134             
27135             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27136             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27137             
27138             this.startScale = this.scale;
27139             
27140             while (this.getScaleLevel() < minScale){
27141             
27142                 this.scale = this.scale + 1;
27143                 
27144                 if(!this.zoomable()){
27145                     break;
27146                 }
27147                 
27148                 if(
27149                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27150                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27151                 ){
27152                     continue;
27153                 }
27154                 
27155                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27156
27157                 this.draw();
27158                 
27159                 return;
27160             }
27161             
27162             this.scale = this.startScale;
27163             
27164             this.onRotateFail();
27165             
27166             return false;
27167         }
27168         
27169         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27170
27171         if(this.isDocument){
27172             this.setThumbBoxSize();
27173             this.setThumbBoxPosition();
27174             this.setCanvasPosition();
27175         }
27176         
27177         this.draw();
27178         
27179         this.fireEvent('rotate', this, 'left');
27180         
27181     },
27182     
27183     onRotateRight : function(e)
27184     {
27185         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27186             
27187             var minScale = this.thumbEl.getWidth() / this.minWidth;
27188         
27189             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27190             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27191             
27192             this.startScale = this.scale;
27193             
27194             while (this.getScaleLevel() < minScale){
27195             
27196                 this.scale = this.scale + 1;
27197                 
27198                 if(!this.zoomable()){
27199                     break;
27200                 }
27201                 
27202                 if(
27203                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27204                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27205                 ){
27206                     continue;
27207                 }
27208                 
27209                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27210
27211                 this.draw();
27212                 
27213                 return;
27214             }
27215             
27216             this.scale = this.startScale;
27217             
27218             this.onRotateFail();
27219             
27220             return false;
27221         }
27222         
27223         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27224
27225         if(this.isDocument){
27226             this.setThumbBoxSize();
27227             this.setThumbBoxPosition();
27228             this.setCanvasPosition();
27229         }
27230         
27231         this.draw();
27232         
27233         this.fireEvent('rotate', this, 'right');
27234     },
27235     
27236     onRotateFail : function()
27237     {
27238         this.errorEl.show(true);
27239         
27240         var _this = this;
27241         
27242         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27243     },
27244     
27245     draw : function()
27246     {
27247         this.previewEl.dom.innerHTML = '';
27248         
27249         var canvasEl = document.createElement("canvas");
27250         
27251         var contextEl = canvasEl.getContext("2d");
27252         
27253         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27254         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27255         var center = this.imageEl.OriginWidth / 2;
27256         
27257         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27258             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27259             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27260             center = this.imageEl.OriginHeight / 2;
27261         }
27262         
27263         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27264         
27265         contextEl.translate(center, center);
27266         contextEl.rotate(this.rotate * Math.PI / 180);
27267
27268         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27269         
27270         this.canvasEl = document.createElement("canvas");
27271         
27272         this.contextEl = this.canvasEl.getContext("2d");
27273         
27274         switch (this.rotate) {
27275             case 0 :
27276                 
27277                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27278                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27279                 
27280                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27281                 
27282                 break;
27283             case 90 : 
27284                 
27285                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27286                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27287                 
27288                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27289                     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);
27290                     break;
27291                 }
27292                 
27293                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27294                 
27295                 break;
27296             case 180 :
27297                 
27298                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27299                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27300                 
27301                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27302                     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);
27303                     break;
27304                 }
27305                 
27306                 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);
27307                 
27308                 break;
27309             case 270 :
27310                 
27311                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27312                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27313         
27314                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27315                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27316                     break;
27317                 }
27318                 
27319                 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);
27320                 
27321                 break;
27322             default : 
27323                 break;
27324         }
27325         
27326         this.previewEl.appendChild(this.canvasEl);
27327         
27328         this.setCanvasPosition();
27329     },
27330     
27331     crop : function()
27332     {
27333         if(!this.canvasLoaded){
27334             return;
27335         }
27336         
27337         var imageCanvas = document.createElement("canvas");
27338         
27339         var imageContext = imageCanvas.getContext("2d");
27340         
27341         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27342         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27343         
27344         var center = imageCanvas.width / 2;
27345         
27346         imageContext.translate(center, center);
27347         
27348         imageContext.rotate(this.rotate * Math.PI / 180);
27349         
27350         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27351         
27352         var canvas = document.createElement("canvas");
27353         
27354         var context = canvas.getContext("2d");
27355                 
27356         canvas.width = this.minWidth;
27357         canvas.height = this.minHeight;
27358
27359         switch (this.rotate) {
27360             case 0 :
27361                 
27362                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27363                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27364                 
27365                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27366                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27367                 
27368                 var targetWidth = this.minWidth - 2 * x;
27369                 var targetHeight = this.minHeight - 2 * y;
27370                 
27371                 var scale = 1;
27372                 
27373                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27374                     scale = targetWidth / width;
27375                 }
27376                 
27377                 if(x > 0 && y == 0){
27378                     scale = targetHeight / height;
27379                 }
27380                 
27381                 if(x > 0 && y > 0){
27382                     scale = targetWidth / width;
27383                     
27384                     if(width < height){
27385                         scale = targetHeight / height;
27386                     }
27387                 }
27388                 
27389                 context.scale(scale, scale);
27390                 
27391                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27392                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27393
27394                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27395                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27396
27397                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27398                 
27399                 break;
27400             case 90 : 
27401                 
27402                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27403                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27404                 
27405                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27406                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27407                 
27408                 var targetWidth = this.minWidth - 2 * x;
27409                 var targetHeight = this.minHeight - 2 * y;
27410                 
27411                 var scale = 1;
27412                 
27413                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27414                     scale = targetWidth / width;
27415                 }
27416                 
27417                 if(x > 0 && y == 0){
27418                     scale = targetHeight / height;
27419                 }
27420                 
27421                 if(x > 0 && y > 0){
27422                     scale = targetWidth / width;
27423                     
27424                     if(width < height){
27425                         scale = targetHeight / height;
27426                     }
27427                 }
27428                 
27429                 context.scale(scale, scale);
27430                 
27431                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27432                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27433
27434                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27435                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27436                 
27437                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27438                 
27439                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27440                 
27441                 break;
27442             case 180 :
27443                 
27444                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27445                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27446                 
27447                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27448                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27449                 
27450                 var targetWidth = this.minWidth - 2 * x;
27451                 var targetHeight = this.minHeight - 2 * y;
27452                 
27453                 var scale = 1;
27454                 
27455                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27456                     scale = targetWidth / width;
27457                 }
27458                 
27459                 if(x > 0 && y == 0){
27460                     scale = targetHeight / height;
27461                 }
27462                 
27463                 if(x > 0 && y > 0){
27464                     scale = targetWidth / width;
27465                     
27466                     if(width < height){
27467                         scale = targetHeight / height;
27468                     }
27469                 }
27470                 
27471                 context.scale(scale, scale);
27472                 
27473                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27474                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27475
27476                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27477                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27478
27479                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27480                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27481                 
27482                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27483                 
27484                 break;
27485             case 270 :
27486                 
27487                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27488                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27489                 
27490                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27491                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27492                 
27493                 var targetWidth = this.minWidth - 2 * x;
27494                 var targetHeight = this.minHeight - 2 * y;
27495                 
27496                 var scale = 1;
27497                 
27498                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27499                     scale = targetWidth / width;
27500                 }
27501                 
27502                 if(x > 0 && y == 0){
27503                     scale = targetHeight / height;
27504                 }
27505                 
27506                 if(x > 0 && y > 0){
27507                     scale = targetWidth / width;
27508                     
27509                     if(width < height){
27510                         scale = targetHeight / height;
27511                     }
27512                 }
27513                 
27514                 context.scale(scale, scale);
27515                 
27516                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27517                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27518
27519                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27520                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27521                 
27522                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27523                 
27524                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27525                 
27526                 break;
27527             default : 
27528                 break;
27529         }
27530         
27531         this.cropData = canvas.toDataURL(this.cropType);
27532         
27533         if(this.fireEvent('crop', this, this.cropData) !== false){
27534             this.process(this.file, this.cropData);
27535         }
27536         
27537         return;
27538         
27539     },
27540     
27541     setThumbBoxSize : function()
27542     {
27543         var width, height;
27544         
27545         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27546             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27547             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27548             
27549             this.minWidth = width;
27550             this.minHeight = height;
27551             
27552             if(this.rotate == 90 || this.rotate == 270){
27553                 this.minWidth = height;
27554                 this.minHeight = width;
27555             }
27556         }
27557         
27558         height = 300;
27559         width = Math.ceil(this.minWidth * height / this.minHeight);
27560         
27561         if(this.minWidth > this.minHeight){
27562             width = 300;
27563             height = Math.ceil(this.minHeight * width / this.minWidth);
27564         }
27565         
27566         this.thumbEl.setStyle({
27567             width : width + 'px',
27568             height : height + 'px'
27569         });
27570
27571         return;
27572             
27573     },
27574     
27575     setThumbBoxPosition : function()
27576     {
27577         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27578         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27579         
27580         this.thumbEl.setLeft(x);
27581         this.thumbEl.setTop(y);
27582         
27583     },
27584     
27585     baseRotateLevel : function()
27586     {
27587         this.baseRotate = 1;
27588         
27589         if(
27590                 typeof(this.exif) != 'undefined' &&
27591                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27592                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27593         ){
27594             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27595         }
27596         
27597         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27598         
27599     },
27600     
27601     baseScaleLevel : function()
27602     {
27603         var width, height;
27604         
27605         if(this.isDocument){
27606             
27607             if(this.baseRotate == 6 || this.baseRotate == 8){
27608             
27609                 height = this.thumbEl.getHeight();
27610                 this.baseScale = height / this.imageEl.OriginWidth;
27611
27612                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27613                     width = this.thumbEl.getWidth();
27614                     this.baseScale = width / this.imageEl.OriginHeight;
27615                 }
27616
27617                 return;
27618             }
27619
27620             height = this.thumbEl.getHeight();
27621             this.baseScale = height / this.imageEl.OriginHeight;
27622
27623             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27624                 width = this.thumbEl.getWidth();
27625                 this.baseScale = width / this.imageEl.OriginWidth;
27626             }
27627
27628             return;
27629         }
27630         
27631         if(this.baseRotate == 6 || this.baseRotate == 8){
27632             
27633             width = this.thumbEl.getHeight();
27634             this.baseScale = width / this.imageEl.OriginHeight;
27635             
27636             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27637                 height = this.thumbEl.getWidth();
27638                 this.baseScale = height / this.imageEl.OriginHeight;
27639             }
27640             
27641             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27642                 height = this.thumbEl.getWidth();
27643                 this.baseScale = height / this.imageEl.OriginHeight;
27644                 
27645                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27646                     width = this.thumbEl.getHeight();
27647                     this.baseScale = width / this.imageEl.OriginWidth;
27648                 }
27649             }
27650             
27651             return;
27652         }
27653         
27654         width = this.thumbEl.getWidth();
27655         this.baseScale = width / this.imageEl.OriginWidth;
27656         
27657         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27658             height = this.thumbEl.getHeight();
27659             this.baseScale = height / this.imageEl.OriginHeight;
27660         }
27661         
27662         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27663             
27664             height = this.thumbEl.getHeight();
27665             this.baseScale = height / this.imageEl.OriginHeight;
27666             
27667             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27668                 width = this.thumbEl.getWidth();
27669                 this.baseScale = width / this.imageEl.OriginWidth;
27670             }
27671             
27672         }
27673         
27674         return;
27675     },
27676     
27677     getScaleLevel : function()
27678     {
27679         return this.baseScale * Math.pow(1.1, this.scale);
27680     },
27681     
27682     onTouchStart : function(e)
27683     {
27684         if(!this.canvasLoaded){
27685             this.beforeSelectFile(e);
27686             return;
27687         }
27688         
27689         var touches = e.browserEvent.touches;
27690         
27691         if(!touches){
27692             return;
27693         }
27694         
27695         if(touches.length == 1){
27696             this.onMouseDown(e);
27697             return;
27698         }
27699         
27700         if(touches.length != 2){
27701             return;
27702         }
27703         
27704         var coords = [];
27705         
27706         for(var i = 0, finger; finger = touches[i]; i++){
27707             coords.push(finger.pageX, finger.pageY);
27708         }
27709         
27710         var x = Math.pow(coords[0] - coords[2], 2);
27711         var y = Math.pow(coords[1] - coords[3], 2);
27712         
27713         this.startDistance = Math.sqrt(x + y);
27714         
27715         this.startScale = this.scale;
27716         
27717         this.pinching = true;
27718         this.dragable = false;
27719         
27720     },
27721     
27722     onTouchMove : function(e)
27723     {
27724         if(!this.pinching && !this.dragable){
27725             return;
27726         }
27727         
27728         var touches = e.browserEvent.touches;
27729         
27730         if(!touches){
27731             return;
27732         }
27733         
27734         if(this.dragable){
27735             this.onMouseMove(e);
27736             return;
27737         }
27738         
27739         var coords = [];
27740         
27741         for(var i = 0, finger; finger = touches[i]; i++){
27742             coords.push(finger.pageX, finger.pageY);
27743         }
27744         
27745         var x = Math.pow(coords[0] - coords[2], 2);
27746         var y = Math.pow(coords[1] - coords[3], 2);
27747         
27748         this.endDistance = Math.sqrt(x + y);
27749         
27750         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27751         
27752         if(!this.zoomable()){
27753             this.scale = this.startScale;
27754             return;
27755         }
27756         
27757         this.draw();
27758         
27759     },
27760     
27761     onTouchEnd : function(e)
27762     {
27763         this.pinching = false;
27764         this.dragable = false;
27765         
27766     },
27767     
27768     process : function(file, crop)
27769     {
27770         if(this.loadMask){
27771             this.maskEl.mask(this.loadingText);
27772         }
27773         
27774         this.xhr = new XMLHttpRequest();
27775         
27776         file.xhr = this.xhr;
27777
27778         this.xhr.open(this.method, this.url, true);
27779         
27780         var headers = {
27781             "Accept": "application/json",
27782             "Cache-Control": "no-cache",
27783             "X-Requested-With": "XMLHttpRequest"
27784         };
27785         
27786         for (var headerName in headers) {
27787             var headerValue = headers[headerName];
27788             if (headerValue) {
27789                 this.xhr.setRequestHeader(headerName, headerValue);
27790             }
27791         }
27792         
27793         var _this = this;
27794         
27795         this.xhr.onload = function()
27796         {
27797             _this.xhrOnLoad(_this.xhr);
27798         }
27799         
27800         this.xhr.onerror = function()
27801         {
27802             _this.xhrOnError(_this.xhr);
27803         }
27804         
27805         var formData = new FormData();
27806
27807         formData.append('returnHTML', 'NO');
27808         
27809         if(crop){
27810             formData.append('crop', crop);
27811         }
27812         
27813         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27814             formData.append(this.paramName, file, file.name);
27815         }
27816         
27817         if(typeof(file.filename) != 'undefined'){
27818             formData.append('filename', file.filename);
27819         }
27820         
27821         if(typeof(file.mimetype) != 'undefined'){
27822             formData.append('mimetype', file.mimetype);
27823         }
27824         
27825         if(this.fireEvent('arrange', this, formData) != false){
27826             this.xhr.send(formData);
27827         };
27828     },
27829     
27830     xhrOnLoad : function(xhr)
27831     {
27832         if(this.loadMask){
27833             this.maskEl.unmask();
27834         }
27835         
27836         if (xhr.readyState !== 4) {
27837             this.fireEvent('exception', this, xhr);
27838             return;
27839         }
27840
27841         var response = Roo.decode(xhr.responseText);
27842         
27843         if(!response.success){
27844             this.fireEvent('exception', this, xhr);
27845             return;
27846         }
27847         
27848         var response = Roo.decode(xhr.responseText);
27849         
27850         this.fireEvent('upload', this, response);
27851         
27852     },
27853     
27854     xhrOnError : function()
27855     {
27856         if(this.loadMask){
27857             this.maskEl.unmask();
27858         }
27859         
27860         Roo.log('xhr on error');
27861         
27862         var response = Roo.decode(xhr.responseText);
27863           
27864         Roo.log(response);
27865         
27866     },
27867     
27868     prepare : function(file)
27869     {   
27870         if(this.loadMask){
27871             this.maskEl.mask(this.loadingText);
27872         }
27873         
27874         this.file = false;
27875         this.exif = {};
27876         
27877         if(typeof(file) === 'string'){
27878             this.loadCanvas(file);
27879             return;
27880         }
27881         
27882         if(!file || !this.urlAPI){
27883             return;
27884         }
27885         
27886         this.file = file;
27887         this.cropType = file.type;
27888         
27889         var _this = this;
27890         
27891         if(this.fireEvent('prepare', this, this.file) != false){
27892             
27893             var reader = new FileReader();
27894             
27895             reader.onload = function (e) {
27896                 if (e.target.error) {
27897                     Roo.log(e.target.error);
27898                     return;
27899                 }
27900                 
27901                 var buffer = e.target.result,
27902                     dataView = new DataView(buffer),
27903                     offset = 2,
27904                     maxOffset = dataView.byteLength - 4,
27905                     markerBytes,
27906                     markerLength;
27907                 
27908                 if (dataView.getUint16(0) === 0xffd8) {
27909                     while (offset < maxOffset) {
27910                         markerBytes = dataView.getUint16(offset);
27911                         
27912                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27913                             markerLength = dataView.getUint16(offset + 2) + 2;
27914                             if (offset + markerLength > dataView.byteLength) {
27915                                 Roo.log('Invalid meta data: Invalid segment size.');
27916                                 break;
27917                             }
27918                             
27919                             if(markerBytes == 0xffe1){
27920                                 _this.parseExifData(
27921                                     dataView,
27922                                     offset,
27923                                     markerLength
27924                                 );
27925                             }
27926                             
27927                             offset += markerLength;
27928                             
27929                             continue;
27930                         }
27931                         
27932                         break;
27933                     }
27934                     
27935                 }
27936                 
27937                 var url = _this.urlAPI.createObjectURL(_this.file);
27938                 
27939                 _this.loadCanvas(url);
27940                 
27941                 return;
27942             }
27943             
27944             reader.readAsArrayBuffer(this.file);
27945             
27946         }
27947         
27948     },
27949     
27950     parseExifData : function(dataView, offset, length)
27951     {
27952         var tiffOffset = offset + 10,
27953             littleEndian,
27954             dirOffset;
27955     
27956         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27957             // No Exif data, might be XMP data instead
27958             return;
27959         }
27960         
27961         // Check for the ASCII code for "Exif" (0x45786966):
27962         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27963             // No Exif data, might be XMP data instead
27964             return;
27965         }
27966         if (tiffOffset + 8 > dataView.byteLength) {
27967             Roo.log('Invalid Exif data: Invalid segment size.');
27968             return;
27969         }
27970         // Check for the two null bytes:
27971         if (dataView.getUint16(offset + 8) !== 0x0000) {
27972             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27973             return;
27974         }
27975         // Check the byte alignment:
27976         switch (dataView.getUint16(tiffOffset)) {
27977         case 0x4949:
27978             littleEndian = true;
27979             break;
27980         case 0x4D4D:
27981             littleEndian = false;
27982             break;
27983         default:
27984             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27985             return;
27986         }
27987         // Check for the TIFF tag marker (0x002A):
27988         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27989             Roo.log('Invalid Exif data: Missing TIFF marker.');
27990             return;
27991         }
27992         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27993         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27994         
27995         this.parseExifTags(
27996             dataView,
27997             tiffOffset,
27998             tiffOffset + dirOffset,
27999             littleEndian
28000         );
28001     },
28002     
28003     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28004     {
28005         var tagsNumber,
28006             dirEndOffset,
28007             i;
28008         if (dirOffset + 6 > dataView.byteLength) {
28009             Roo.log('Invalid Exif data: Invalid directory offset.');
28010             return;
28011         }
28012         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28013         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28014         if (dirEndOffset + 4 > dataView.byteLength) {
28015             Roo.log('Invalid Exif data: Invalid directory size.');
28016             return;
28017         }
28018         for (i = 0; i < tagsNumber; i += 1) {
28019             this.parseExifTag(
28020                 dataView,
28021                 tiffOffset,
28022                 dirOffset + 2 + 12 * i, // tag offset
28023                 littleEndian
28024             );
28025         }
28026         // Return the offset to the next directory:
28027         return dataView.getUint32(dirEndOffset, littleEndian);
28028     },
28029     
28030     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28031     {
28032         var tag = dataView.getUint16(offset, littleEndian);
28033         
28034         this.exif[tag] = this.getExifValue(
28035             dataView,
28036             tiffOffset,
28037             offset,
28038             dataView.getUint16(offset + 2, littleEndian), // tag type
28039             dataView.getUint32(offset + 4, littleEndian), // tag length
28040             littleEndian
28041         );
28042     },
28043     
28044     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28045     {
28046         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28047             tagSize,
28048             dataOffset,
28049             values,
28050             i,
28051             str,
28052             c;
28053     
28054         if (!tagType) {
28055             Roo.log('Invalid Exif data: Invalid tag type.');
28056             return;
28057         }
28058         
28059         tagSize = tagType.size * length;
28060         // Determine if the value is contained in the dataOffset bytes,
28061         // or if the value at the dataOffset is a pointer to the actual data:
28062         dataOffset = tagSize > 4 ?
28063                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28064         if (dataOffset + tagSize > dataView.byteLength) {
28065             Roo.log('Invalid Exif data: Invalid data offset.');
28066             return;
28067         }
28068         if (length === 1) {
28069             return tagType.getValue(dataView, dataOffset, littleEndian);
28070         }
28071         values = [];
28072         for (i = 0; i < length; i += 1) {
28073             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28074         }
28075         
28076         if (tagType.ascii) {
28077             str = '';
28078             // Concatenate the chars:
28079             for (i = 0; i < values.length; i += 1) {
28080                 c = values[i];
28081                 // Ignore the terminating NULL byte(s):
28082                 if (c === '\u0000') {
28083                     break;
28084                 }
28085                 str += c;
28086             }
28087             return str;
28088         }
28089         return values;
28090     }
28091     
28092 });
28093
28094 Roo.apply(Roo.bootstrap.UploadCropbox, {
28095     tags : {
28096         'Orientation': 0x0112
28097     },
28098     
28099     Orientation: {
28100             1: 0, //'top-left',
28101 //            2: 'top-right',
28102             3: 180, //'bottom-right',
28103 //            4: 'bottom-left',
28104 //            5: 'left-top',
28105             6: 90, //'right-top',
28106 //            7: 'right-bottom',
28107             8: 270 //'left-bottom'
28108     },
28109     
28110     exifTagTypes : {
28111         // byte, 8-bit unsigned int:
28112         1: {
28113             getValue: function (dataView, dataOffset) {
28114                 return dataView.getUint8(dataOffset);
28115             },
28116             size: 1
28117         },
28118         // ascii, 8-bit byte:
28119         2: {
28120             getValue: function (dataView, dataOffset) {
28121                 return String.fromCharCode(dataView.getUint8(dataOffset));
28122             },
28123             size: 1,
28124             ascii: true
28125         },
28126         // short, 16 bit int:
28127         3: {
28128             getValue: function (dataView, dataOffset, littleEndian) {
28129                 return dataView.getUint16(dataOffset, littleEndian);
28130             },
28131             size: 2
28132         },
28133         // long, 32 bit int:
28134         4: {
28135             getValue: function (dataView, dataOffset, littleEndian) {
28136                 return dataView.getUint32(dataOffset, littleEndian);
28137             },
28138             size: 4
28139         },
28140         // rational = two long values, first is numerator, second is denominator:
28141         5: {
28142             getValue: function (dataView, dataOffset, littleEndian) {
28143                 return dataView.getUint32(dataOffset, littleEndian) /
28144                     dataView.getUint32(dataOffset + 4, littleEndian);
28145             },
28146             size: 8
28147         },
28148         // slong, 32 bit signed int:
28149         9: {
28150             getValue: function (dataView, dataOffset, littleEndian) {
28151                 return dataView.getInt32(dataOffset, littleEndian);
28152             },
28153             size: 4
28154         },
28155         // srational, two slongs, first is numerator, second is denominator:
28156         10: {
28157             getValue: function (dataView, dataOffset, littleEndian) {
28158                 return dataView.getInt32(dataOffset, littleEndian) /
28159                     dataView.getInt32(dataOffset + 4, littleEndian);
28160             },
28161             size: 8
28162         }
28163     },
28164     
28165     footer : {
28166         STANDARD : [
28167             {
28168                 tag : 'div',
28169                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28170                 action : 'rotate-left',
28171                 cn : [
28172                     {
28173                         tag : 'button',
28174                         cls : 'btn btn-default',
28175                         html : '<i class="fa fa-undo"></i>'
28176                     }
28177                 ]
28178             },
28179             {
28180                 tag : 'div',
28181                 cls : 'btn-group roo-upload-cropbox-picture',
28182                 action : 'picture',
28183                 cn : [
28184                     {
28185                         tag : 'button',
28186                         cls : 'btn btn-default',
28187                         html : '<i class="fa fa-picture-o"></i>'
28188                     }
28189                 ]
28190             },
28191             {
28192                 tag : 'div',
28193                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28194                 action : 'rotate-right',
28195                 cn : [
28196                     {
28197                         tag : 'button',
28198                         cls : 'btn btn-default',
28199                         html : '<i class="fa fa-repeat"></i>'
28200                     }
28201                 ]
28202             }
28203         ],
28204         DOCUMENT : [
28205             {
28206                 tag : 'div',
28207                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28208                 action : 'rotate-left',
28209                 cn : [
28210                     {
28211                         tag : 'button',
28212                         cls : 'btn btn-default',
28213                         html : '<i class="fa fa-undo"></i>'
28214                     }
28215                 ]
28216             },
28217             {
28218                 tag : 'div',
28219                 cls : 'btn-group roo-upload-cropbox-download',
28220                 action : 'download',
28221                 cn : [
28222                     {
28223                         tag : 'button',
28224                         cls : 'btn btn-default',
28225                         html : '<i class="fa fa-download"></i>'
28226                     }
28227                 ]
28228             },
28229             {
28230                 tag : 'div',
28231                 cls : 'btn-group roo-upload-cropbox-crop',
28232                 action : 'crop',
28233                 cn : [
28234                     {
28235                         tag : 'button',
28236                         cls : 'btn btn-default',
28237                         html : '<i class="fa fa-crop"></i>'
28238                     }
28239                 ]
28240             },
28241             {
28242                 tag : 'div',
28243                 cls : 'btn-group roo-upload-cropbox-trash',
28244                 action : 'trash',
28245                 cn : [
28246                     {
28247                         tag : 'button',
28248                         cls : 'btn btn-default',
28249                         html : '<i class="fa fa-trash"></i>'
28250                     }
28251                 ]
28252             },
28253             {
28254                 tag : 'div',
28255                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28256                 action : 'rotate-right',
28257                 cn : [
28258                     {
28259                         tag : 'button',
28260                         cls : 'btn btn-default',
28261                         html : '<i class="fa fa-repeat"></i>'
28262                     }
28263                 ]
28264             }
28265         ],
28266         ROTATOR : [
28267             {
28268                 tag : 'div',
28269                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28270                 action : 'rotate-left',
28271                 cn : [
28272                     {
28273                         tag : 'button',
28274                         cls : 'btn btn-default',
28275                         html : '<i class="fa fa-undo"></i>'
28276                     }
28277                 ]
28278             },
28279             {
28280                 tag : 'div',
28281                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28282                 action : 'rotate-right',
28283                 cn : [
28284                     {
28285                         tag : 'button',
28286                         cls : 'btn btn-default',
28287                         html : '<i class="fa fa-repeat"></i>'
28288                     }
28289                 ]
28290             }
28291         ]
28292     }
28293 });
28294
28295 /*
28296 * Licence: LGPL
28297 */
28298
28299 /**
28300  * @class Roo.bootstrap.DocumentManager
28301  * @extends Roo.bootstrap.Component
28302  * Bootstrap DocumentManager class
28303  * @cfg {String} paramName default 'imageUpload'
28304  * @cfg {String} toolTipName default 'filename'
28305  * @cfg {String} method default POST
28306  * @cfg {String} url action url
28307  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28308  * @cfg {Boolean} multiple multiple upload default true
28309  * @cfg {Number} thumbSize default 300
28310  * @cfg {String} fieldLabel
28311  * @cfg {Number} labelWidth default 4
28312  * @cfg {String} labelAlign (left|top) default left
28313  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28314 * @cfg {Number} labellg set the width of label (1-12)
28315  * @cfg {Number} labelmd set the width of label (1-12)
28316  * @cfg {Number} labelsm set the width of label (1-12)
28317  * @cfg {Number} labelxs set the width of label (1-12)
28318  * 
28319  * @constructor
28320  * Create a new DocumentManager
28321  * @param {Object} config The config object
28322  */
28323
28324 Roo.bootstrap.DocumentManager = function(config){
28325     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28326     
28327     this.files = [];
28328     this.delegates = [];
28329     
28330     this.addEvents({
28331         /**
28332          * @event initial
28333          * Fire when initial the DocumentManager
28334          * @param {Roo.bootstrap.DocumentManager} this
28335          */
28336         "initial" : true,
28337         /**
28338          * @event inspect
28339          * inspect selected file
28340          * @param {Roo.bootstrap.DocumentManager} this
28341          * @param {File} file
28342          */
28343         "inspect" : true,
28344         /**
28345          * @event exception
28346          * Fire when xhr load exception
28347          * @param {Roo.bootstrap.DocumentManager} this
28348          * @param {XMLHttpRequest} xhr
28349          */
28350         "exception" : true,
28351         /**
28352          * @event afterupload
28353          * Fire when xhr load exception
28354          * @param {Roo.bootstrap.DocumentManager} this
28355          * @param {XMLHttpRequest} xhr
28356          */
28357         "afterupload" : true,
28358         /**
28359          * @event prepare
28360          * prepare the form data
28361          * @param {Roo.bootstrap.DocumentManager} this
28362          * @param {Object} formData
28363          */
28364         "prepare" : true,
28365         /**
28366          * @event remove
28367          * Fire when remove the file
28368          * @param {Roo.bootstrap.DocumentManager} this
28369          * @param {Object} file
28370          */
28371         "remove" : true,
28372         /**
28373          * @event refresh
28374          * Fire after refresh the file
28375          * @param {Roo.bootstrap.DocumentManager} this
28376          */
28377         "refresh" : true,
28378         /**
28379          * @event click
28380          * Fire after click the image
28381          * @param {Roo.bootstrap.DocumentManager} this
28382          * @param {Object} file
28383          */
28384         "click" : true,
28385         /**
28386          * @event edit
28387          * Fire when upload a image and editable set to true
28388          * @param {Roo.bootstrap.DocumentManager} this
28389          * @param {Object} file
28390          */
28391         "edit" : true,
28392         /**
28393          * @event beforeselectfile
28394          * Fire before select file
28395          * @param {Roo.bootstrap.DocumentManager} this
28396          */
28397         "beforeselectfile" : true,
28398         /**
28399          * @event process
28400          * Fire before process file
28401          * @param {Roo.bootstrap.DocumentManager} this
28402          * @param {Object} file
28403          */
28404         "process" : true
28405         
28406     });
28407 };
28408
28409 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28410     
28411     boxes : 0,
28412     inputName : '',
28413     thumbSize : 300,
28414     multiple : true,
28415     files : false,
28416     method : 'POST',
28417     url : '',
28418     paramName : 'imageUpload',
28419     toolTipName : 'filename',
28420     fieldLabel : '',
28421     labelWidth : 4,
28422     labelAlign : 'left',
28423     editable : true,
28424     delegates : false,
28425     xhr : false, 
28426     
28427     labellg : 0,
28428     labelmd : 0,
28429     labelsm : 0,
28430     labelxs : 0,
28431     
28432     getAutoCreate : function()
28433     {   
28434         var managerWidget = {
28435             tag : 'div',
28436             cls : 'roo-document-manager',
28437             cn : [
28438                 {
28439                     tag : 'input',
28440                     cls : 'roo-document-manager-selector',
28441                     type : 'file'
28442                 },
28443                 {
28444                     tag : 'div',
28445                     cls : 'roo-document-manager-uploader',
28446                     cn : [
28447                         {
28448                             tag : 'div',
28449                             cls : 'roo-document-manager-upload-btn',
28450                             html : '<i class="fa fa-plus"></i>'
28451                         }
28452                     ]
28453                     
28454                 }
28455             ]
28456         };
28457         
28458         var content = [
28459             {
28460                 tag : 'div',
28461                 cls : 'column col-md-12',
28462                 cn : managerWidget
28463             }
28464         ];
28465         
28466         if(this.fieldLabel.length){
28467             
28468             content = [
28469                 {
28470                     tag : 'div',
28471                     cls : 'column col-md-12',
28472                     html : this.fieldLabel
28473                 },
28474                 {
28475                     tag : 'div',
28476                     cls : 'column col-md-12',
28477                     cn : managerWidget
28478                 }
28479             ];
28480
28481             if(this.labelAlign == 'left'){
28482                 content = [
28483                     {
28484                         tag : 'div',
28485                         cls : 'column',
28486                         html : this.fieldLabel
28487                     },
28488                     {
28489                         tag : 'div',
28490                         cls : 'column',
28491                         cn : managerWidget
28492                     }
28493                 ];
28494                 
28495                 if(this.labelWidth > 12){
28496                     content[0].style = "width: " + this.labelWidth + 'px';
28497                 }
28498
28499                 if(this.labelWidth < 13 && this.labelmd == 0){
28500                     this.labelmd = this.labelWidth;
28501                 }
28502
28503                 if(this.labellg > 0){
28504                     content[0].cls += ' col-lg-' + this.labellg;
28505                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28506                 }
28507
28508                 if(this.labelmd > 0){
28509                     content[0].cls += ' col-md-' + this.labelmd;
28510                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28511                 }
28512
28513                 if(this.labelsm > 0){
28514                     content[0].cls += ' col-sm-' + this.labelsm;
28515                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28516                 }
28517
28518                 if(this.labelxs > 0){
28519                     content[0].cls += ' col-xs-' + this.labelxs;
28520                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28521                 }
28522                 
28523             }
28524         }
28525         
28526         var cfg = {
28527             tag : 'div',
28528             cls : 'row clearfix',
28529             cn : content
28530         };
28531         
28532         return cfg;
28533         
28534     },
28535     
28536     initEvents : function()
28537     {
28538         this.managerEl = this.el.select('.roo-document-manager', true).first();
28539         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28540         
28541         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28542         this.selectorEl.hide();
28543         
28544         if(this.multiple){
28545             this.selectorEl.attr('multiple', 'multiple');
28546         }
28547         
28548         this.selectorEl.on('change', this.onFileSelected, this);
28549         
28550         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28551         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28552         
28553         this.uploader.on('click', this.onUploaderClick, this);
28554         
28555         this.renderProgressDialog();
28556         
28557         var _this = this;
28558         
28559         window.addEventListener("resize", function() { _this.refresh(); } );
28560         
28561         this.fireEvent('initial', this);
28562     },
28563     
28564     renderProgressDialog : function()
28565     {
28566         var _this = this;
28567         
28568         this.progressDialog = new Roo.bootstrap.Modal({
28569             cls : 'roo-document-manager-progress-dialog',
28570             allow_close : false,
28571             title : '',
28572             buttons : [
28573                 {
28574                     name  :'cancel',
28575                     weight : 'danger',
28576                     html : 'Cancel'
28577                 }
28578             ], 
28579             listeners : { 
28580                 btnclick : function() {
28581                     _this.uploadCancel();
28582                     this.hide();
28583                 }
28584             }
28585         });
28586          
28587         this.progressDialog.render(Roo.get(document.body));
28588          
28589         this.progress = new Roo.bootstrap.Progress({
28590             cls : 'roo-document-manager-progress',
28591             active : true,
28592             striped : true
28593         });
28594         
28595         this.progress.render(this.progressDialog.getChildContainer());
28596         
28597         this.progressBar = new Roo.bootstrap.ProgressBar({
28598             cls : 'roo-document-manager-progress-bar',
28599             aria_valuenow : 0,
28600             aria_valuemin : 0,
28601             aria_valuemax : 12,
28602             panel : 'success'
28603         });
28604         
28605         this.progressBar.render(this.progress.getChildContainer());
28606     },
28607     
28608     onUploaderClick : function(e)
28609     {
28610         e.preventDefault();
28611      
28612         if(this.fireEvent('beforeselectfile', this) != false){
28613             this.selectorEl.dom.click();
28614         }
28615         
28616     },
28617     
28618     onFileSelected : function(e)
28619     {
28620         e.preventDefault();
28621         
28622         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28623             return;
28624         }
28625         
28626         Roo.each(this.selectorEl.dom.files, function(file){
28627             if(this.fireEvent('inspect', this, file) != false){
28628                 this.files.push(file);
28629             }
28630         }, this);
28631         
28632         this.queue();
28633         
28634     },
28635     
28636     queue : function()
28637     {
28638         this.selectorEl.dom.value = '';
28639         
28640         if(!this.files.length){
28641             return;
28642         }
28643         
28644         if(this.boxes > 0 && this.files.length > this.boxes){
28645             this.files = this.files.slice(0, this.boxes);
28646         }
28647         
28648         this.uploader.show();
28649         
28650         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28651             this.uploader.hide();
28652         }
28653         
28654         var _this = this;
28655         
28656         var files = [];
28657         
28658         var docs = [];
28659         
28660         Roo.each(this.files, function(file){
28661             
28662             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28663                 var f = this.renderPreview(file);
28664                 files.push(f);
28665                 return;
28666             }
28667             
28668             if(file.type.indexOf('image') != -1){
28669                 this.delegates.push(
28670                     (function(){
28671                         _this.process(file);
28672                     }).createDelegate(this)
28673                 );
28674         
28675                 return;
28676             }
28677             
28678             docs.push(
28679                 (function(){
28680                     _this.process(file);
28681                 }).createDelegate(this)
28682             );
28683             
28684         }, this);
28685         
28686         this.files = files;
28687         
28688         this.delegates = this.delegates.concat(docs);
28689         
28690         if(!this.delegates.length){
28691             this.refresh();
28692             return;
28693         }
28694         
28695         this.progressBar.aria_valuemax = this.delegates.length;
28696         
28697         this.arrange();
28698         
28699         return;
28700     },
28701     
28702     arrange : function()
28703     {
28704         if(!this.delegates.length){
28705             this.progressDialog.hide();
28706             this.refresh();
28707             return;
28708         }
28709         
28710         var delegate = this.delegates.shift();
28711         
28712         this.progressDialog.show();
28713         
28714         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28715         
28716         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28717         
28718         delegate();
28719     },
28720     
28721     refresh : function()
28722     {
28723         this.uploader.show();
28724         
28725         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28726             this.uploader.hide();
28727         }
28728         
28729         Roo.isTouch ? this.closable(false) : this.closable(true);
28730         
28731         this.fireEvent('refresh', this);
28732     },
28733     
28734     onRemove : function(e, el, o)
28735     {
28736         e.preventDefault();
28737         
28738         this.fireEvent('remove', this, o);
28739         
28740     },
28741     
28742     remove : function(o)
28743     {
28744         var files = [];
28745         
28746         Roo.each(this.files, function(file){
28747             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28748                 files.push(file);
28749                 return;
28750             }
28751
28752             o.target.remove();
28753
28754         }, this);
28755         
28756         this.files = files;
28757         
28758         this.refresh();
28759     },
28760     
28761     clear : function()
28762     {
28763         Roo.each(this.files, function(file){
28764             if(!file.target){
28765                 return;
28766             }
28767             
28768             file.target.remove();
28769
28770         }, this);
28771         
28772         this.files = [];
28773         
28774         this.refresh();
28775     },
28776     
28777     onClick : function(e, el, o)
28778     {
28779         e.preventDefault();
28780         
28781         this.fireEvent('click', this, o);
28782         
28783     },
28784     
28785     closable : function(closable)
28786     {
28787         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28788             
28789             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28790             
28791             if(closable){
28792                 el.show();
28793                 return;
28794             }
28795             
28796             el.hide();
28797             
28798         }, this);
28799     },
28800     
28801     xhrOnLoad : function(xhr)
28802     {
28803         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28804             el.remove();
28805         }, this);
28806         
28807         if (xhr.readyState !== 4) {
28808             this.arrange();
28809             this.fireEvent('exception', this, xhr);
28810             return;
28811         }
28812
28813         var response = Roo.decode(xhr.responseText);
28814         
28815         if(!response.success){
28816             this.arrange();
28817             this.fireEvent('exception', this, xhr);
28818             return;
28819         }
28820         
28821         var file = this.renderPreview(response.data);
28822         
28823         this.files.push(file);
28824         
28825         this.arrange();
28826         
28827         this.fireEvent('afterupload', this, xhr);
28828         
28829     },
28830     
28831     xhrOnError : function(xhr)
28832     {
28833         Roo.log('xhr on error');
28834         
28835         var response = Roo.decode(xhr.responseText);
28836           
28837         Roo.log(response);
28838         
28839         this.arrange();
28840     },
28841     
28842     process : function(file)
28843     {
28844         if(this.fireEvent('process', this, file) !== false){
28845             if(this.editable && file.type.indexOf('image') != -1){
28846                 this.fireEvent('edit', this, file);
28847                 return;
28848             }
28849
28850             this.uploadStart(file, false);
28851
28852             return;
28853         }
28854         
28855     },
28856     
28857     uploadStart : function(file, crop)
28858     {
28859         this.xhr = new XMLHttpRequest();
28860         
28861         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28862             this.arrange();
28863             return;
28864         }
28865         
28866         file.xhr = this.xhr;
28867             
28868         this.managerEl.createChild({
28869             tag : 'div',
28870             cls : 'roo-document-manager-loading',
28871             cn : [
28872                 {
28873                     tag : 'div',
28874                     tooltip : file.name,
28875                     cls : 'roo-document-manager-thumb',
28876                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28877                 }
28878             ]
28879
28880         });
28881
28882         this.xhr.open(this.method, this.url, true);
28883         
28884         var headers = {
28885             "Accept": "application/json",
28886             "Cache-Control": "no-cache",
28887             "X-Requested-With": "XMLHttpRequest"
28888         };
28889         
28890         for (var headerName in headers) {
28891             var headerValue = headers[headerName];
28892             if (headerValue) {
28893                 this.xhr.setRequestHeader(headerName, headerValue);
28894             }
28895         }
28896         
28897         var _this = this;
28898         
28899         this.xhr.onload = function()
28900         {
28901             _this.xhrOnLoad(_this.xhr);
28902         }
28903         
28904         this.xhr.onerror = function()
28905         {
28906             _this.xhrOnError(_this.xhr);
28907         }
28908         
28909         var formData = new FormData();
28910
28911         formData.append('returnHTML', 'NO');
28912         
28913         if(crop){
28914             formData.append('crop', crop);
28915         }
28916         
28917         formData.append(this.paramName, file, file.name);
28918         
28919         var options = {
28920             file : file, 
28921             manually : false
28922         };
28923         
28924         if(this.fireEvent('prepare', this, formData, options) != false){
28925             
28926             if(options.manually){
28927                 return;
28928             }
28929             
28930             this.xhr.send(formData);
28931             return;
28932         };
28933         
28934         this.uploadCancel();
28935     },
28936     
28937     uploadCancel : function()
28938     {
28939         if (this.xhr) {
28940             this.xhr.abort();
28941         }
28942         
28943         this.delegates = [];
28944         
28945         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28946             el.remove();
28947         }, this);
28948         
28949         this.arrange();
28950     },
28951     
28952     renderPreview : function(file)
28953     {
28954         if(typeof(file.target) != 'undefined' && file.target){
28955             return file;
28956         }
28957         
28958         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28959         
28960         var previewEl = this.managerEl.createChild({
28961             tag : 'div',
28962             cls : 'roo-document-manager-preview',
28963             cn : [
28964                 {
28965                     tag : 'div',
28966                     tooltip : file[this.toolTipName],
28967                     cls : 'roo-document-manager-thumb',
28968                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28969                 },
28970                 {
28971                     tag : 'button',
28972                     cls : 'close',
28973                     html : '<i class="fa fa-times-circle"></i>'
28974                 }
28975             ]
28976         });
28977
28978         var close = previewEl.select('button.close', true).first();
28979
28980         close.on('click', this.onRemove, this, file);
28981
28982         file.target = previewEl;
28983
28984         var image = previewEl.select('img', true).first();
28985         
28986         var _this = this;
28987         
28988         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28989         
28990         image.on('click', this.onClick, this, file);
28991         
28992         return file;
28993         
28994     },
28995     
28996     onPreviewLoad : function(file, image)
28997     {
28998         if(typeof(file.target) == 'undefined' || !file.target){
28999             return;
29000         }
29001         
29002         var width = image.dom.naturalWidth || image.dom.width;
29003         var height = image.dom.naturalHeight || image.dom.height;
29004         
29005         if(width > height){
29006             file.target.addClass('wide');
29007             return;
29008         }
29009         
29010         file.target.addClass('tall');
29011         return;
29012         
29013     },
29014     
29015     uploadFromSource : function(file, crop)
29016     {
29017         this.xhr = new XMLHttpRequest();
29018         
29019         this.managerEl.createChild({
29020             tag : 'div',
29021             cls : 'roo-document-manager-loading',
29022             cn : [
29023                 {
29024                     tag : 'div',
29025                     tooltip : file.name,
29026                     cls : 'roo-document-manager-thumb',
29027                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29028                 }
29029             ]
29030
29031         });
29032
29033         this.xhr.open(this.method, this.url, true);
29034         
29035         var headers = {
29036             "Accept": "application/json",
29037             "Cache-Control": "no-cache",
29038             "X-Requested-With": "XMLHttpRequest"
29039         };
29040         
29041         for (var headerName in headers) {
29042             var headerValue = headers[headerName];
29043             if (headerValue) {
29044                 this.xhr.setRequestHeader(headerName, headerValue);
29045             }
29046         }
29047         
29048         var _this = this;
29049         
29050         this.xhr.onload = function()
29051         {
29052             _this.xhrOnLoad(_this.xhr);
29053         }
29054         
29055         this.xhr.onerror = function()
29056         {
29057             _this.xhrOnError(_this.xhr);
29058         }
29059         
29060         var formData = new FormData();
29061
29062         formData.append('returnHTML', 'NO');
29063         
29064         formData.append('crop', crop);
29065         
29066         if(typeof(file.filename) != 'undefined'){
29067             formData.append('filename', file.filename);
29068         }
29069         
29070         if(typeof(file.mimetype) != 'undefined'){
29071             formData.append('mimetype', file.mimetype);
29072         }
29073         
29074         Roo.log(formData);
29075         
29076         if(this.fireEvent('prepare', this, formData) != false){
29077             this.xhr.send(formData);
29078         };
29079     }
29080 });
29081
29082 /*
29083 * Licence: LGPL
29084 */
29085
29086 /**
29087  * @class Roo.bootstrap.DocumentViewer
29088  * @extends Roo.bootstrap.Component
29089  * Bootstrap DocumentViewer class
29090  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29091  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29092  * 
29093  * @constructor
29094  * Create a new DocumentViewer
29095  * @param {Object} config The config object
29096  */
29097
29098 Roo.bootstrap.DocumentViewer = function(config){
29099     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29100     
29101     this.addEvents({
29102         /**
29103          * @event initial
29104          * Fire after initEvent
29105          * @param {Roo.bootstrap.DocumentViewer} this
29106          */
29107         "initial" : true,
29108         /**
29109          * @event click
29110          * Fire after click
29111          * @param {Roo.bootstrap.DocumentViewer} this
29112          */
29113         "click" : true,
29114         /**
29115          * @event download
29116          * Fire after download button
29117          * @param {Roo.bootstrap.DocumentViewer} this
29118          */
29119         "download" : true,
29120         /**
29121          * @event trash
29122          * Fire after trash button
29123          * @param {Roo.bootstrap.DocumentViewer} this
29124          */
29125         "trash" : true
29126         
29127     });
29128 };
29129
29130 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29131     
29132     showDownload : true,
29133     
29134     showTrash : true,
29135     
29136     getAutoCreate : function()
29137     {
29138         var cfg = {
29139             tag : 'div',
29140             cls : 'roo-document-viewer',
29141             cn : [
29142                 {
29143                     tag : 'div',
29144                     cls : 'roo-document-viewer-body',
29145                     cn : [
29146                         {
29147                             tag : 'div',
29148                             cls : 'roo-document-viewer-thumb',
29149                             cn : [
29150                                 {
29151                                     tag : 'img',
29152                                     cls : 'roo-document-viewer-image'
29153                                 }
29154                             ]
29155                         }
29156                     ]
29157                 },
29158                 {
29159                     tag : 'div',
29160                     cls : 'roo-document-viewer-footer',
29161                     cn : {
29162                         tag : 'div',
29163                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29164                         cn : [
29165                             {
29166                                 tag : 'div',
29167                                 cls : 'btn-group roo-document-viewer-download',
29168                                 cn : [
29169                                     {
29170                                         tag : 'button',
29171                                         cls : 'btn btn-default',
29172                                         html : '<i class="fa fa-download"></i>'
29173                                     }
29174                                 ]
29175                             },
29176                             {
29177                                 tag : 'div',
29178                                 cls : 'btn-group roo-document-viewer-trash',
29179                                 cn : [
29180                                     {
29181                                         tag : 'button',
29182                                         cls : 'btn btn-default',
29183                                         html : '<i class="fa fa-trash"></i>'
29184                                     }
29185                                 ]
29186                             }
29187                         ]
29188                     }
29189                 }
29190             ]
29191         };
29192         
29193         return cfg;
29194     },
29195     
29196     initEvents : function()
29197     {
29198         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29199         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29200         
29201         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29202         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29203         
29204         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29205         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29206         
29207         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29208         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29209         
29210         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29211         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29212         
29213         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29214         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29215         
29216         this.bodyEl.on('click', this.onClick, this);
29217         this.downloadBtn.on('click', this.onDownload, this);
29218         this.trashBtn.on('click', this.onTrash, this);
29219         
29220         this.downloadBtn.hide();
29221         this.trashBtn.hide();
29222         
29223         if(this.showDownload){
29224             this.downloadBtn.show();
29225         }
29226         
29227         if(this.showTrash){
29228             this.trashBtn.show();
29229         }
29230         
29231         if(!this.showDownload && !this.showTrash) {
29232             this.footerEl.hide();
29233         }
29234         
29235     },
29236     
29237     initial : function()
29238     {
29239         this.fireEvent('initial', this);
29240         
29241     },
29242     
29243     onClick : function(e)
29244     {
29245         e.preventDefault();
29246         
29247         this.fireEvent('click', this);
29248     },
29249     
29250     onDownload : function(e)
29251     {
29252         e.preventDefault();
29253         
29254         this.fireEvent('download', this);
29255     },
29256     
29257     onTrash : function(e)
29258     {
29259         e.preventDefault();
29260         
29261         this.fireEvent('trash', this);
29262     }
29263     
29264 });
29265 /*
29266  * - LGPL
29267  *
29268  * nav progress bar
29269  * 
29270  */
29271
29272 /**
29273  * @class Roo.bootstrap.NavProgressBar
29274  * @extends Roo.bootstrap.Component
29275  * Bootstrap NavProgressBar class
29276  * 
29277  * @constructor
29278  * Create a new nav progress bar
29279  * @param {Object} config The config object
29280  */
29281
29282 Roo.bootstrap.NavProgressBar = function(config){
29283     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29284
29285     this.bullets = this.bullets || [];
29286    
29287 //    Roo.bootstrap.NavProgressBar.register(this);
29288      this.addEvents({
29289         /**
29290              * @event changed
29291              * Fires when the active item changes
29292              * @param {Roo.bootstrap.NavProgressBar} this
29293              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29294              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29295          */
29296         'changed': true
29297      });
29298     
29299 };
29300
29301 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29302     
29303     bullets : [],
29304     barItems : [],
29305     
29306     getAutoCreate : function()
29307     {
29308         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29309         
29310         cfg = {
29311             tag : 'div',
29312             cls : 'roo-navigation-bar-group',
29313             cn : [
29314                 {
29315                     tag : 'div',
29316                     cls : 'roo-navigation-top-bar'
29317                 },
29318                 {
29319                     tag : 'div',
29320                     cls : 'roo-navigation-bullets-bar',
29321                     cn : [
29322                         {
29323                             tag : 'ul',
29324                             cls : 'roo-navigation-bar'
29325                         }
29326                     ]
29327                 },
29328                 
29329                 {
29330                     tag : 'div',
29331                     cls : 'roo-navigation-bottom-bar'
29332                 }
29333             ]
29334             
29335         };
29336         
29337         return cfg;
29338         
29339     },
29340     
29341     initEvents: function() 
29342     {
29343         
29344     },
29345     
29346     onRender : function(ct, position) 
29347     {
29348         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29349         
29350         if(this.bullets.length){
29351             Roo.each(this.bullets, function(b){
29352                this.addItem(b);
29353             }, this);
29354         }
29355         
29356         this.format();
29357         
29358     },
29359     
29360     addItem : function(cfg)
29361     {
29362         var item = new Roo.bootstrap.NavProgressItem(cfg);
29363         
29364         item.parentId = this.id;
29365         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29366         
29367         if(cfg.html){
29368             var top = new Roo.bootstrap.Element({
29369                 tag : 'div',
29370                 cls : 'roo-navigation-bar-text'
29371             });
29372             
29373             var bottom = new Roo.bootstrap.Element({
29374                 tag : 'div',
29375                 cls : 'roo-navigation-bar-text'
29376             });
29377             
29378             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29379             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29380             
29381             var topText = new Roo.bootstrap.Element({
29382                 tag : 'span',
29383                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29384             });
29385             
29386             var bottomText = new Roo.bootstrap.Element({
29387                 tag : 'span',
29388                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29389             });
29390             
29391             topText.onRender(top.el, null);
29392             bottomText.onRender(bottom.el, null);
29393             
29394             item.topEl = top;
29395             item.bottomEl = bottom;
29396         }
29397         
29398         this.barItems.push(item);
29399         
29400         return item;
29401     },
29402     
29403     getActive : function()
29404     {
29405         var active = false;
29406         
29407         Roo.each(this.barItems, function(v){
29408             
29409             if (!v.isActive()) {
29410                 return;
29411             }
29412             
29413             active = v;
29414             return false;
29415             
29416         });
29417         
29418         return active;
29419     },
29420     
29421     setActiveItem : function(item)
29422     {
29423         var prev = false;
29424         
29425         Roo.each(this.barItems, function(v){
29426             if (v.rid == item.rid) {
29427                 return ;
29428             }
29429             
29430             if (v.isActive()) {
29431                 v.setActive(false);
29432                 prev = v;
29433             }
29434         });
29435
29436         item.setActive(true);
29437         
29438         this.fireEvent('changed', this, item, prev);
29439     },
29440     
29441     getBarItem: function(rid)
29442     {
29443         var ret = false;
29444         
29445         Roo.each(this.barItems, function(e) {
29446             if (e.rid != rid) {
29447                 return;
29448             }
29449             
29450             ret =  e;
29451             return false;
29452         });
29453         
29454         return ret;
29455     },
29456     
29457     indexOfItem : function(item)
29458     {
29459         var index = false;
29460         
29461         Roo.each(this.barItems, function(v, i){
29462             
29463             if (v.rid != item.rid) {
29464                 return;
29465             }
29466             
29467             index = i;
29468             return false
29469         });
29470         
29471         return index;
29472     },
29473     
29474     setActiveNext : function()
29475     {
29476         var i = this.indexOfItem(this.getActive());
29477         
29478         if (i > this.barItems.length) {
29479             return;
29480         }
29481         
29482         this.setActiveItem(this.barItems[i+1]);
29483     },
29484     
29485     setActivePrev : function()
29486     {
29487         var i = this.indexOfItem(this.getActive());
29488         
29489         if (i  < 1) {
29490             return;
29491         }
29492         
29493         this.setActiveItem(this.barItems[i-1]);
29494     },
29495     
29496     format : function()
29497     {
29498         if(!this.barItems.length){
29499             return;
29500         }
29501      
29502         var width = 100 / this.barItems.length;
29503         
29504         Roo.each(this.barItems, function(i){
29505             i.el.setStyle('width', width + '%');
29506             i.topEl.el.setStyle('width', width + '%');
29507             i.bottomEl.el.setStyle('width', width + '%');
29508         }, this);
29509         
29510     }
29511     
29512 });
29513 /*
29514  * - LGPL
29515  *
29516  * Nav Progress Item
29517  * 
29518  */
29519
29520 /**
29521  * @class Roo.bootstrap.NavProgressItem
29522  * @extends Roo.bootstrap.Component
29523  * Bootstrap NavProgressItem class
29524  * @cfg {String} rid the reference id
29525  * @cfg {Boolean} active (true|false) Is item active default false
29526  * @cfg {Boolean} disabled (true|false) Is item active default false
29527  * @cfg {String} html
29528  * @cfg {String} position (top|bottom) text position default bottom
29529  * @cfg {String} icon show icon instead of number
29530  * 
29531  * @constructor
29532  * Create a new NavProgressItem
29533  * @param {Object} config The config object
29534  */
29535 Roo.bootstrap.NavProgressItem = function(config){
29536     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29537     this.addEvents({
29538         // raw events
29539         /**
29540          * @event click
29541          * The raw click event for the entire grid.
29542          * @param {Roo.bootstrap.NavProgressItem} this
29543          * @param {Roo.EventObject} e
29544          */
29545         "click" : true
29546     });
29547    
29548 };
29549
29550 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29551     
29552     rid : '',
29553     active : false,
29554     disabled : false,
29555     html : '',
29556     position : 'bottom',
29557     icon : false,
29558     
29559     getAutoCreate : function()
29560     {
29561         var iconCls = 'roo-navigation-bar-item-icon';
29562         
29563         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29564         
29565         var cfg = {
29566             tag: 'li',
29567             cls: 'roo-navigation-bar-item',
29568             cn : [
29569                 {
29570                     tag : 'i',
29571                     cls : iconCls
29572                 }
29573             ]
29574         };
29575         
29576         if(this.active){
29577             cfg.cls += ' active';
29578         }
29579         if(this.disabled){
29580             cfg.cls += ' disabled';
29581         }
29582         
29583         return cfg;
29584     },
29585     
29586     disable : function()
29587     {
29588         this.setDisabled(true);
29589     },
29590     
29591     enable : function()
29592     {
29593         this.setDisabled(false);
29594     },
29595     
29596     initEvents: function() 
29597     {
29598         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29599         
29600         this.iconEl.on('click', this.onClick, this);
29601     },
29602     
29603     onClick : function(e)
29604     {
29605         e.preventDefault();
29606         
29607         if(this.disabled){
29608             return;
29609         }
29610         
29611         if(this.fireEvent('click', this, e) === false){
29612             return;
29613         };
29614         
29615         this.parent().setActiveItem(this);
29616     },
29617     
29618     isActive: function () 
29619     {
29620         return this.active;
29621     },
29622     
29623     setActive : function(state)
29624     {
29625         if(this.active == state){
29626             return;
29627         }
29628         
29629         this.active = state;
29630         
29631         if (state) {
29632             this.el.addClass('active');
29633             return;
29634         }
29635         
29636         this.el.removeClass('active');
29637         
29638         return;
29639     },
29640     
29641     setDisabled : function(state)
29642     {
29643         if(this.disabled == state){
29644             return;
29645         }
29646         
29647         this.disabled = state;
29648         
29649         if (state) {
29650             this.el.addClass('disabled');
29651             return;
29652         }
29653         
29654         this.el.removeClass('disabled');
29655     },
29656     
29657     tooltipEl : function()
29658     {
29659         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29660     }
29661 });
29662  
29663
29664  /*
29665  * - LGPL
29666  *
29667  * FieldLabel
29668  * 
29669  */
29670
29671 /**
29672  * @class Roo.bootstrap.FieldLabel
29673  * @extends Roo.bootstrap.Component
29674  * Bootstrap FieldLabel class
29675  * @cfg {String} html contents of the element
29676  * @cfg {String} tag tag of the element default label
29677  * @cfg {String} cls class of the element
29678  * @cfg {String} target label target 
29679  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29680  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29681  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29682  * @cfg {String} iconTooltip default "This field is required"
29683  * 
29684  * @constructor
29685  * Create a new FieldLabel
29686  * @param {Object} config The config object
29687  */
29688
29689 Roo.bootstrap.FieldLabel = function(config){
29690     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29691     
29692     this.addEvents({
29693             /**
29694              * @event invalid
29695              * Fires after the field has been marked as invalid.
29696              * @param {Roo.form.FieldLabel} this
29697              * @param {String} msg The validation message
29698              */
29699             invalid : true,
29700             /**
29701              * @event valid
29702              * Fires after the field has been validated with no errors.
29703              * @param {Roo.form.FieldLabel} this
29704              */
29705             valid : true
29706         });
29707 };
29708
29709 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29710     
29711     tag: 'label',
29712     cls: '',
29713     html: '',
29714     target: '',
29715     allowBlank : true,
29716     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29717     validClass : 'text-success fa fa-lg fa-check',
29718     iconTooltip : 'This field is required',
29719     
29720     getAutoCreate : function(){
29721         
29722         var cfg = {
29723             tag : this.tag,
29724             cls : 'roo-bootstrap-field-label ' + this.cls,
29725             for : this.target,
29726             cn : [
29727                 {
29728                     tag : 'i',
29729                     cls : '',
29730                     tooltip : this.iconTooltip
29731                 },
29732                 {
29733                     tag : 'span',
29734                     html : this.html
29735                 }
29736             ] 
29737         };
29738         
29739         return cfg;
29740     },
29741     
29742     initEvents: function() 
29743     {
29744         Roo.bootstrap.Element.superclass.initEvents.call(this);
29745         
29746         this.iconEl = this.el.select('i', true).first();
29747         
29748         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29749         
29750         Roo.bootstrap.FieldLabel.register(this);
29751     },
29752     
29753     /**
29754      * Mark this field as valid
29755      */
29756     markValid : function()
29757     {
29758         this.iconEl.show();
29759         
29760         this.iconEl.removeClass(this.invalidClass);
29761         
29762         this.iconEl.addClass(this.validClass);
29763         
29764         this.fireEvent('valid', this);
29765     },
29766     
29767     /**
29768      * Mark this field as invalid
29769      * @param {String} msg The validation message
29770      */
29771     markInvalid : function(msg)
29772     {
29773         this.iconEl.show();
29774         
29775         this.iconEl.removeClass(this.validClass);
29776         
29777         this.iconEl.addClass(this.invalidClass);
29778         
29779         this.fireEvent('invalid', this, msg);
29780     }
29781     
29782    
29783 });
29784
29785 Roo.apply(Roo.bootstrap.FieldLabel, {
29786     
29787     groups: {},
29788     
29789      /**
29790     * register a FieldLabel Group
29791     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29792     */
29793     register : function(label)
29794     {
29795         if(this.groups.hasOwnProperty(label.target)){
29796             return;
29797         }
29798      
29799         this.groups[label.target] = label;
29800         
29801     },
29802     /**
29803     * fetch a FieldLabel Group based on the target
29804     * @param {string} target
29805     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29806     */
29807     get: function(target) {
29808         if (typeof(this.groups[target]) == 'undefined') {
29809             return false;
29810         }
29811         
29812         return this.groups[target] ;
29813     }
29814 });
29815
29816  
29817
29818  /*
29819  * - LGPL
29820  *
29821  * page DateSplitField.
29822  * 
29823  */
29824
29825
29826 /**
29827  * @class Roo.bootstrap.DateSplitField
29828  * @extends Roo.bootstrap.Component
29829  * Bootstrap DateSplitField class
29830  * @cfg {string} fieldLabel - the label associated
29831  * @cfg {Number} labelWidth set the width of label (0-12)
29832  * @cfg {String} labelAlign (top|left)
29833  * @cfg {Boolean} dayAllowBlank (true|false) default false
29834  * @cfg {Boolean} monthAllowBlank (true|false) default false
29835  * @cfg {Boolean} yearAllowBlank (true|false) default false
29836  * @cfg {string} dayPlaceholder 
29837  * @cfg {string} monthPlaceholder
29838  * @cfg {string} yearPlaceholder
29839  * @cfg {string} dayFormat default 'd'
29840  * @cfg {string} monthFormat default 'm'
29841  * @cfg {string} yearFormat default 'Y'
29842  * @cfg {Number} labellg set the width of label (1-12)
29843  * @cfg {Number} labelmd set the width of label (1-12)
29844  * @cfg {Number} labelsm set the width of label (1-12)
29845  * @cfg {Number} labelxs set the width of label (1-12)
29846
29847  *     
29848  * @constructor
29849  * Create a new DateSplitField
29850  * @param {Object} config The config object
29851  */
29852
29853 Roo.bootstrap.DateSplitField = function(config){
29854     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29855     
29856     this.addEvents({
29857         // raw events
29858          /**
29859          * @event years
29860          * getting the data of years
29861          * @param {Roo.bootstrap.DateSplitField} this
29862          * @param {Object} years
29863          */
29864         "years" : true,
29865         /**
29866          * @event days
29867          * getting the data of days
29868          * @param {Roo.bootstrap.DateSplitField} this
29869          * @param {Object} days
29870          */
29871         "days" : true,
29872         /**
29873          * @event invalid
29874          * Fires after the field has been marked as invalid.
29875          * @param {Roo.form.Field} this
29876          * @param {String} msg The validation message
29877          */
29878         invalid : true,
29879        /**
29880          * @event valid
29881          * Fires after the field has been validated with no errors.
29882          * @param {Roo.form.Field} this
29883          */
29884         valid : true
29885     });
29886 };
29887
29888 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29889     
29890     fieldLabel : '',
29891     labelAlign : 'top',
29892     labelWidth : 3,
29893     dayAllowBlank : false,
29894     monthAllowBlank : false,
29895     yearAllowBlank : false,
29896     dayPlaceholder : '',
29897     monthPlaceholder : '',
29898     yearPlaceholder : '',
29899     dayFormat : 'd',
29900     monthFormat : 'm',
29901     yearFormat : 'Y',
29902     isFormField : true,
29903     labellg : 0,
29904     labelmd : 0,
29905     labelsm : 0,
29906     labelxs : 0,
29907     
29908     getAutoCreate : function()
29909     {
29910         var cfg = {
29911             tag : 'div',
29912             cls : 'row roo-date-split-field-group',
29913             cn : [
29914                 {
29915                     tag : 'input',
29916                     type : 'hidden',
29917                     cls : 'form-hidden-field roo-date-split-field-group-value',
29918                     name : this.name
29919                 }
29920             ]
29921         };
29922         
29923         var labelCls = 'col-md-12';
29924         var contentCls = 'col-md-4';
29925         
29926         if(this.fieldLabel){
29927             
29928             var label = {
29929                 tag : 'div',
29930                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29931                 cn : [
29932                     {
29933                         tag : 'label',
29934                         html : this.fieldLabel
29935                     }
29936                 ]
29937             };
29938             
29939             if(this.labelAlign == 'left'){
29940             
29941                 if(this.labelWidth > 12){
29942                     label.style = "width: " + this.labelWidth + 'px';
29943                 }
29944
29945                 if(this.labelWidth < 13 && this.labelmd == 0){
29946                     this.labelmd = this.labelWidth;
29947                 }
29948
29949                 if(this.labellg > 0){
29950                     labelCls = ' col-lg-' + this.labellg;
29951                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29952                 }
29953
29954                 if(this.labelmd > 0){
29955                     labelCls = ' col-md-' + this.labelmd;
29956                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29957                 }
29958
29959                 if(this.labelsm > 0){
29960                     labelCls = ' col-sm-' + this.labelsm;
29961                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29962                 }
29963
29964                 if(this.labelxs > 0){
29965                     labelCls = ' col-xs-' + this.labelxs;
29966                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29967                 }
29968             }
29969             
29970             label.cls += ' ' + labelCls;
29971             
29972             cfg.cn.push(label);
29973         }
29974         
29975         Roo.each(['day', 'month', 'year'], function(t){
29976             cfg.cn.push({
29977                 tag : 'div',
29978                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29979             });
29980         }, this);
29981         
29982         return cfg;
29983     },
29984     
29985     inputEl: function ()
29986     {
29987         return this.el.select('.roo-date-split-field-group-value', true).first();
29988     },
29989     
29990     onRender : function(ct, position) 
29991     {
29992         var _this = this;
29993         
29994         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29995         
29996         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29997         
29998         this.dayField = new Roo.bootstrap.ComboBox({
29999             allowBlank : this.dayAllowBlank,
30000             alwaysQuery : true,
30001             displayField : 'value',
30002             editable : false,
30003             fieldLabel : '',
30004             forceSelection : true,
30005             mode : 'local',
30006             placeholder : this.dayPlaceholder,
30007             selectOnFocus : true,
30008             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30009             triggerAction : 'all',
30010             typeAhead : true,
30011             valueField : 'value',
30012             store : new Roo.data.SimpleStore({
30013                 data : (function() {    
30014                     var days = [];
30015                     _this.fireEvent('days', _this, days);
30016                     return days;
30017                 })(),
30018                 fields : [ 'value' ]
30019             }),
30020             listeners : {
30021                 select : function (_self, record, index)
30022                 {
30023                     _this.setValue(_this.getValue());
30024                 }
30025             }
30026         });
30027
30028         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30029         
30030         this.monthField = new Roo.bootstrap.MonthField({
30031             after : '<i class=\"fa fa-calendar\"></i>',
30032             allowBlank : this.monthAllowBlank,
30033             placeholder : this.monthPlaceholder,
30034             readOnly : true,
30035             listeners : {
30036                 render : function (_self)
30037                 {
30038                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30039                         e.preventDefault();
30040                         _self.focus();
30041                     });
30042                 },
30043                 select : function (_self, oldvalue, newvalue)
30044                 {
30045                     _this.setValue(_this.getValue());
30046                 }
30047             }
30048         });
30049         
30050         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30051         
30052         this.yearField = new Roo.bootstrap.ComboBox({
30053             allowBlank : this.yearAllowBlank,
30054             alwaysQuery : true,
30055             displayField : 'value',
30056             editable : false,
30057             fieldLabel : '',
30058             forceSelection : true,
30059             mode : 'local',
30060             placeholder : this.yearPlaceholder,
30061             selectOnFocus : true,
30062             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30063             triggerAction : 'all',
30064             typeAhead : true,
30065             valueField : 'value',
30066             store : new Roo.data.SimpleStore({
30067                 data : (function() {
30068                     var years = [];
30069                     _this.fireEvent('years', _this, years);
30070                     return years;
30071                 })(),
30072                 fields : [ 'value' ]
30073             }),
30074             listeners : {
30075                 select : function (_self, record, index)
30076                 {
30077                     _this.setValue(_this.getValue());
30078                 }
30079             }
30080         });
30081
30082         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30083     },
30084     
30085     setValue : function(v, format)
30086     {
30087         this.inputEl.dom.value = v;
30088         
30089         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30090         
30091         var d = Date.parseDate(v, f);
30092         
30093         if(!d){
30094             this.validate();
30095             return;
30096         }
30097         
30098         this.setDay(d.format(this.dayFormat));
30099         this.setMonth(d.format(this.monthFormat));
30100         this.setYear(d.format(this.yearFormat));
30101         
30102         this.validate();
30103         
30104         return;
30105     },
30106     
30107     setDay : function(v)
30108     {
30109         this.dayField.setValue(v);
30110         this.inputEl.dom.value = this.getValue();
30111         this.validate();
30112         return;
30113     },
30114     
30115     setMonth : function(v)
30116     {
30117         this.monthField.setValue(v, true);
30118         this.inputEl.dom.value = this.getValue();
30119         this.validate();
30120         return;
30121     },
30122     
30123     setYear : function(v)
30124     {
30125         this.yearField.setValue(v);
30126         this.inputEl.dom.value = this.getValue();
30127         this.validate();
30128         return;
30129     },
30130     
30131     getDay : function()
30132     {
30133         return this.dayField.getValue();
30134     },
30135     
30136     getMonth : function()
30137     {
30138         return this.monthField.getValue();
30139     },
30140     
30141     getYear : function()
30142     {
30143         return this.yearField.getValue();
30144     },
30145     
30146     getValue : function()
30147     {
30148         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30149         
30150         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30151         
30152         return date;
30153     },
30154     
30155     reset : function()
30156     {
30157         this.setDay('');
30158         this.setMonth('');
30159         this.setYear('');
30160         this.inputEl.dom.value = '';
30161         this.validate();
30162         return;
30163     },
30164     
30165     validate : function()
30166     {
30167         var d = this.dayField.validate();
30168         var m = this.monthField.validate();
30169         var y = this.yearField.validate();
30170         
30171         var valid = true;
30172         
30173         if(
30174                 (!this.dayAllowBlank && !d) ||
30175                 (!this.monthAllowBlank && !m) ||
30176                 (!this.yearAllowBlank && !y)
30177         ){
30178             valid = false;
30179         }
30180         
30181         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30182             return valid;
30183         }
30184         
30185         if(valid){
30186             this.markValid();
30187             return valid;
30188         }
30189         
30190         this.markInvalid();
30191         
30192         return valid;
30193     },
30194     
30195     markValid : function()
30196     {
30197         
30198         var label = this.el.select('label', true).first();
30199         var icon = this.el.select('i.fa-star', true).first();
30200
30201         if(label && icon){
30202             icon.remove();
30203         }
30204         
30205         this.fireEvent('valid', this);
30206     },
30207     
30208      /**
30209      * Mark this field as invalid
30210      * @param {String} msg The validation message
30211      */
30212     markInvalid : function(msg)
30213     {
30214         
30215         var label = this.el.select('label', true).first();
30216         var icon = this.el.select('i.fa-star', true).first();
30217
30218         if(label && !icon){
30219             this.el.select('.roo-date-split-field-label', true).createChild({
30220                 tag : 'i',
30221                 cls : 'text-danger fa fa-lg fa-star',
30222                 tooltip : 'This field is required',
30223                 style : 'margin-right:5px;'
30224             }, label, true);
30225         }
30226         
30227         this.fireEvent('invalid', this, msg);
30228     },
30229     
30230     clearInvalid : function()
30231     {
30232         var label = this.el.select('label', true).first();
30233         var icon = this.el.select('i.fa-star', true).first();
30234
30235         if(label && icon){
30236             icon.remove();
30237         }
30238         
30239         this.fireEvent('valid', this);
30240     },
30241     
30242     getName: function()
30243     {
30244         return this.name;
30245     }
30246     
30247 });
30248
30249  /**
30250  *
30251  * This is based on 
30252  * http://masonry.desandro.com
30253  *
30254  * The idea is to render all the bricks based on vertical width...
30255  *
30256  * The original code extends 'outlayer' - we might need to use that....
30257  * 
30258  */
30259
30260
30261 /**
30262  * @class Roo.bootstrap.LayoutMasonry
30263  * @extends Roo.bootstrap.Component
30264  * Bootstrap Layout Masonry class
30265  * 
30266  * @constructor
30267  * Create a new Element
30268  * @param {Object} config The config object
30269  */
30270
30271 Roo.bootstrap.LayoutMasonry = function(config){
30272     
30273     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30274     
30275     this.bricks = [];
30276     
30277     Roo.bootstrap.LayoutMasonry.register(this);
30278     
30279     this.addEvents({
30280         // raw events
30281         /**
30282          * @event layout
30283          * Fire after layout the items
30284          * @param {Roo.bootstrap.LayoutMasonry} this
30285          * @param {Roo.EventObject} e
30286          */
30287         "layout" : true
30288     });
30289     
30290 };
30291
30292 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30293     
30294     /**
30295      * @cfg {Boolean} isLayoutInstant = no animation?
30296      */   
30297     isLayoutInstant : false, // needed?
30298    
30299     /**
30300      * @cfg {Number} boxWidth  width of the columns
30301      */   
30302     boxWidth : 450,
30303     
30304       /**
30305      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30306      */   
30307     boxHeight : 0,
30308     
30309     /**
30310      * @cfg {Number} padWidth padding below box..
30311      */   
30312     padWidth : 10, 
30313     
30314     /**
30315      * @cfg {Number} gutter gutter width..
30316      */   
30317     gutter : 10,
30318     
30319      /**
30320      * @cfg {Number} maxCols maximum number of columns
30321      */   
30322     
30323     maxCols: 0,
30324     
30325     /**
30326      * @cfg {Boolean} isAutoInitial defalut true
30327      */   
30328     isAutoInitial : true, 
30329     
30330     containerWidth: 0,
30331     
30332     /**
30333      * @cfg {Boolean} isHorizontal defalut false
30334      */   
30335     isHorizontal : false, 
30336
30337     currentSize : null,
30338     
30339     tag: 'div',
30340     
30341     cls: '',
30342     
30343     bricks: null, //CompositeElement
30344     
30345     cols : 1,
30346     
30347     _isLayoutInited : false,
30348     
30349 //    isAlternative : false, // only use for vertical layout...
30350     
30351     /**
30352      * @cfg {Number} alternativePadWidth padding below box..
30353      */   
30354     alternativePadWidth : 50,
30355     
30356     selectedBrick : [],
30357     
30358     getAutoCreate : function(){
30359         
30360         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30361         
30362         var cfg = {
30363             tag: this.tag,
30364             cls: 'blog-masonary-wrapper ' + this.cls,
30365             cn : {
30366                 cls : 'mas-boxes masonary'
30367             }
30368         };
30369         
30370         return cfg;
30371     },
30372     
30373     getChildContainer: function( )
30374     {
30375         if (this.boxesEl) {
30376             return this.boxesEl;
30377         }
30378         
30379         this.boxesEl = this.el.select('.mas-boxes').first();
30380         
30381         return this.boxesEl;
30382     },
30383     
30384     
30385     initEvents : function()
30386     {
30387         var _this = this;
30388         
30389         if(this.isAutoInitial){
30390             Roo.log('hook children rendered');
30391             this.on('childrenrendered', function() {
30392                 Roo.log('children rendered');
30393                 _this.initial();
30394             } ,this);
30395         }
30396     },
30397     
30398     initial : function()
30399     {
30400         this.selectedBrick = [];
30401         
30402         this.currentSize = this.el.getBox(true);
30403         
30404         Roo.EventManager.onWindowResize(this.resize, this); 
30405
30406         if(!this.isAutoInitial){
30407             this.layout();
30408             return;
30409         }
30410         
30411         this.layout();
30412         
30413         return;
30414         //this.layout.defer(500,this);
30415         
30416     },
30417     
30418     resize : function()
30419     {
30420         var cs = this.el.getBox(true);
30421         
30422         if (
30423                 this.currentSize.width == cs.width && 
30424                 this.currentSize.x == cs.x && 
30425                 this.currentSize.height == cs.height && 
30426                 this.currentSize.y == cs.y 
30427         ) {
30428             Roo.log("no change in with or X or Y");
30429             return;
30430         }
30431         
30432         this.currentSize = cs;
30433         
30434         this.layout();
30435         
30436     },
30437     
30438     layout : function()
30439     {   
30440         this._resetLayout();
30441         
30442         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30443         
30444         this.layoutItems( isInstant );
30445       
30446         this._isLayoutInited = true;
30447         
30448         this.fireEvent('layout', this);
30449         
30450     },
30451     
30452     _resetLayout : function()
30453     {
30454         if(this.isHorizontal){
30455             this.horizontalMeasureColumns();
30456             return;
30457         }
30458         
30459         this.verticalMeasureColumns();
30460         
30461     },
30462     
30463     verticalMeasureColumns : function()
30464     {
30465         this.getContainerWidth();
30466         
30467 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30468 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30469 //            return;
30470 //        }
30471         
30472         var boxWidth = this.boxWidth + this.padWidth;
30473         
30474         if(this.containerWidth < this.boxWidth){
30475             boxWidth = this.containerWidth
30476         }
30477         
30478         var containerWidth = this.containerWidth;
30479         
30480         var cols = Math.floor(containerWidth / boxWidth);
30481         
30482         this.cols = Math.max( cols, 1 );
30483         
30484         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30485         
30486         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30487         
30488         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30489         
30490         this.colWidth = boxWidth + avail - this.padWidth;
30491         
30492         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30493         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30494     },
30495     
30496     horizontalMeasureColumns : function()
30497     {
30498         this.getContainerWidth();
30499         
30500         var boxWidth = this.boxWidth;
30501         
30502         if(this.containerWidth < boxWidth){
30503             boxWidth = this.containerWidth;
30504         }
30505         
30506         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30507         
30508         this.el.setHeight(boxWidth);
30509         
30510     },
30511     
30512     getContainerWidth : function()
30513     {
30514         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30515     },
30516     
30517     layoutItems : function( isInstant )
30518     {
30519         Roo.log(this.bricks);
30520         
30521         var items = Roo.apply([], this.bricks);
30522         
30523         if(this.isHorizontal){
30524             this._horizontalLayoutItems( items , isInstant );
30525             return;
30526         }
30527         
30528 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30529 //            this._verticalAlternativeLayoutItems( items , isInstant );
30530 //            return;
30531 //        }
30532         
30533         this._verticalLayoutItems( items , isInstant );
30534         
30535     },
30536     
30537     _verticalLayoutItems : function ( items , isInstant)
30538     {
30539         if ( !items || !items.length ) {
30540             return;
30541         }
30542         
30543         var standard = [
30544             ['xs', 'xs', 'xs', 'tall'],
30545             ['xs', 'xs', 'tall'],
30546             ['xs', 'xs', 'sm'],
30547             ['xs', 'xs', 'xs'],
30548             ['xs', 'tall'],
30549             ['xs', 'sm'],
30550             ['xs', 'xs'],
30551             ['xs'],
30552             
30553             ['sm', 'xs', 'xs'],
30554             ['sm', 'xs'],
30555             ['sm'],
30556             
30557             ['tall', 'xs', 'xs', 'xs'],
30558             ['tall', 'xs', 'xs'],
30559             ['tall', 'xs'],
30560             ['tall']
30561             
30562         ];
30563         
30564         var queue = [];
30565         
30566         var boxes = [];
30567         
30568         var box = [];
30569         
30570         Roo.each(items, function(item, k){
30571             
30572             switch (item.size) {
30573                 // these layouts take up a full box,
30574                 case 'md' :
30575                 case 'md-left' :
30576                 case 'md-right' :
30577                 case 'wide' :
30578                     
30579                     if(box.length){
30580                         boxes.push(box);
30581                         box = [];
30582                     }
30583                     
30584                     boxes.push([item]);
30585                     
30586                     break;
30587                     
30588                 case 'xs' :
30589                 case 'sm' :
30590                 case 'tall' :
30591                     
30592                     box.push(item);
30593                     
30594                     break;
30595                 default :
30596                     break;
30597                     
30598             }
30599             
30600         }, this);
30601         
30602         if(box.length){
30603             boxes.push(box);
30604             box = [];
30605         }
30606         
30607         var filterPattern = function(box, length)
30608         {
30609             if(!box.length){
30610                 return;
30611             }
30612             
30613             var match = false;
30614             
30615             var pattern = box.slice(0, length);
30616             
30617             var format = [];
30618             
30619             Roo.each(pattern, function(i){
30620                 format.push(i.size);
30621             }, this);
30622             
30623             Roo.each(standard, function(s){
30624                 
30625                 if(String(s) != String(format)){
30626                     return;
30627                 }
30628                 
30629                 match = true;
30630                 return false;
30631                 
30632             }, this);
30633             
30634             if(!match && length == 1){
30635                 return;
30636             }
30637             
30638             if(!match){
30639                 filterPattern(box, length - 1);
30640                 return;
30641             }
30642                 
30643             queue.push(pattern);
30644
30645             box = box.slice(length, box.length);
30646
30647             filterPattern(box, 4);
30648
30649             return;
30650             
30651         }
30652         
30653         Roo.each(boxes, function(box, k){
30654             
30655             if(!box.length){
30656                 return;
30657             }
30658             
30659             if(box.length == 1){
30660                 queue.push(box);
30661                 return;
30662             }
30663             
30664             filterPattern(box, 4);
30665             
30666         }, this);
30667         
30668         this._processVerticalLayoutQueue( queue, isInstant );
30669         
30670     },
30671     
30672 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30673 //    {
30674 //        if ( !items || !items.length ) {
30675 //            return;
30676 //        }
30677 //
30678 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30679 //        
30680 //    },
30681     
30682     _horizontalLayoutItems : function ( items , isInstant)
30683     {
30684         if ( !items || !items.length || items.length < 3) {
30685             return;
30686         }
30687         
30688         items.reverse();
30689         
30690         var eItems = items.slice(0, 3);
30691         
30692         items = items.slice(3, items.length);
30693         
30694         var standard = [
30695             ['xs', 'xs', 'xs', 'wide'],
30696             ['xs', 'xs', 'wide'],
30697             ['xs', 'xs', 'sm'],
30698             ['xs', 'xs', 'xs'],
30699             ['xs', 'wide'],
30700             ['xs', 'sm'],
30701             ['xs', 'xs'],
30702             ['xs'],
30703             
30704             ['sm', 'xs', 'xs'],
30705             ['sm', 'xs'],
30706             ['sm'],
30707             
30708             ['wide', 'xs', 'xs', 'xs'],
30709             ['wide', 'xs', 'xs'],
30710             ['wide', 'xs'],
30711             ['wide'],
30712             
30713             ['wide-thin']
30714         ];
30715         
30716         var queue = [];
30717         
30718         var boxes = [];
30719         
30720         var box = [];
30721         
30722         Roo.each(items, function(item, k){
30723             
30724             switch (item.size) {
30725                 case 'md' :
30726                 case 'md-left' :
30727                 case 'md-right' :
30728                 case 'tall' :
30729                     
30730                     if(box.length){
30731                         boxes.push(box);
30732                         box = [];
30733                     }
30734                     
30735                     boxes.push([item]);
30736                     
30737                     break;
30738                     
30739                 case 'xs' :
30740                 case 'sm' :
30741                 case 'wide' :
30742                 case 'wide-thin' :
30743                     
30744                     box.push(item);
30745                     
30746                     break;
30747                 default :
30748                     break;
30749                     
30750             }
30751             
30752         }, this);
30753         
30754         if(box.length){
30755             boxes.push(box);
30756             box = [];
30757         }
30758         
30759         var filterPattern = function(box, length)
30760         {
30761             if(!box.length){
30762                 return;
30763             }
30764             
30765             var match = false;
30766             
30767             var pattern = box.slice(0, length);
30768             
30769             var format = [];
30770             
30771             Roo.each(pattern, function(i){
30772                 format.push(i.size);
30773             }, this);
30774             
30775             Roo.each(standard, function(s){
30776                 
30777                 if(String(s) != String(format)){
30778                     return;
30779                 }
30780                 
30781                 match = true;
30782                 return false;
30783                 
30784             }, this);
30785             
30786             if(!match && length == 1){
30787                 return;
30788             }
30789             
30790             if(!match){
30791                 filterPattern(box, length - 1);
30792                 return;
30793             }
30794                 
30795             queue.push(pattern);
30796
30797             box = box.slice(length, box.length);
30798
30799             filterPattern(box, 4);
30800
30801             return;
30802             
30803         }
30804         
30805         Roo.each(boxes, function(box, k){
30806             
30807             if(!box.length){
30808                 return;
30809             }
30810             
30811             if(box.length == 1){
30812                 queue.push(box);
30813                 return;
30814             }
30815             
30816             filterPattern(box, 4);
30817             
30818         }, this);
30819         
30820         
30821         var prune = [];
30822         
30823         var pos = this.el.getBox(true);
30824         
30825         var minX = pos.x;
30826         
30827         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30828         
30829         var hit_end = false;
30830         
30831         Roo.each(queue, function(box){
30832             
30833             if(hit_end){
30834                 
30835                 Roo.each(box, function(b){
30836                 
30837                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30838                     b.el.hide();
30839
30840                 }, this);
30841
30842                 return;
30843             }
30844             
30845             var mx = 0;
30846             
30847             Roo.each(box, function(b){
30848                 
30849                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30850                 b.el.show();
30851
30852                 mx = Math.max(mx, b.x);
30853                 
30854             }, this);
30855             
30856             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30857             
30858             if(maxX < minX){
30859                 
30860                 Roo.each(box, function(b){
30861                 
30862                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30863                     b.el.hide();
30864                     
30865                 }, this);
30866                 
30867                 hit_end = true;
30868                 
30869                 return;
30870             }
30871             
30872             prune.push(box);
30873             
30874         }, this);
30875         
30876         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30877     },
30878     
30879     /** Sets position of item in DOM
30880     * @param {Element} item
30881     * @param {Number} x - horizontal position
30882     * @param {Number} y - vertical position
30883     * @param {Boolean} isInstant - disables transitions
30884     */
30885     _processVerticalLayoutQueue : function( queue, isInstant )
30886     {
30887         var pos = this.el.getBox(true);
30888         var x = pos.x;
30889         var y = pos.y;
30890         var maxY = [];
30891         
30892         for (var i = 0; i < this.cols; i++){
30893             maxY[i] = pos.y;
30894         }
30895         
30896         Roo.each(queue, function(box, k){
30897             
30898             var col = k % this.cols;
30899             
30900             Roo.each(box, function(b,kk){
30901                 
30902                 b.el.position('absolute');
30903                 
30904                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30905                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30906                 
30907                 if(b.size == 'md-left' || b.size == 'md-right'){
30908                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30909                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30910                 }
30911                 
30912                 b.el.setWidth(width);
30913                 b.el.setHeight(height);
30914                 // iframe?
30915                 b.el.select('iframe',true).setSize(width,height);
30916                 
30917             }, this);
30918             
30919             for (var i = 0; i < this.cols; i++){
30920                 
30921                 if(maxY[i] < maxY[col]){
30922                     col = i;
30923                     continue;
30924                 }
30925                 
30926                 col = Math.min(col, i);
30927                 
30928             }
30929             
30930             x = pos.x + col * (this.colWidth + this.padWidth);
30931             
30932             y = maxY[col];
30933             
30934             var positions = [];
30935             
30936             switch (box.length){
30937                 case 1 :
30938                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30939                     break;
30940                 case 2 :
30941                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30942                     break;
30943                 case 3 :
30944                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30945                     break;
30946                 case 4 :
30947                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30948                     break;
30949                 default :
30950                     break;
30951             }
30952             
30953             Roo.each(box, function(b,kk){
30954                 
30955                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30956                 
30957                 var sz = b.el.getSize();
30958                 
30959                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30960                 
30961             }, this);
30962             
30963         }, this);
30964         
30965         var mY = 0;
30966         
30967         for (var i = 0; i < this.cols; i++){
30968             mY = Math.max(mY, maxY[i]);
30969         }
30970         
30971         this.el.setHeight(mY - pos.y);
30972         
30973     },
30974     
30975 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30976 //    {
30977 //        var pos = this.el.getBox(true);
30978 //        var x = pos.x;
30979 //        var y = pos.y;
30980 //        var maxX = pos.right;
30981 //        
30982 //        var maxHeight = 0;
30983 //        
30984 //        Roo.each(items, function(item, k){
30985 //            
30986 //            var c = k % 2;
30987 //            
30988 //            item.el.position('absolute');
30989 //                
30990 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30991 //
30992 //            item.el.setWidth(width);
30993 //
30994 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30995 //
30996 //            item.el.setHeight(height);
30997 //            
30998 //            if(c == 0){
30999 //                item.el.setXY([x, y], isInstant ? false : true);
31000 //            } else {
31001 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31002 //            }
31003 //            
31004 //            y = y + height + this.alternativePadWidth;
31005 //            
31006 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31007 //            
31008 //        }, this);
31009 //        
31010 //        this.el.setHeight(maxHeight);
31011 //        
31012 //    },
31013     
31014     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31015     {
31016         var pos = this.el.getBox(true);
31017         
31018         var minX = pos.x;
31019         var minY = pos.y;
31020         
31021         var maxX = pos.right;
31022         
31023         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31024         
31025         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31026         
31027         Roo.each(queue, function(box, k){
31028             
31029             Roo.each(box, function(b, kk){
31030                 
31031                 b.el.position('absolute');
31032                 
31033                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31034                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31035                 
31036                 if(b.size == 'md-left' || b.size == 'md-right'){
31037                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31038                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31039                 }
31040                 
31041                 b.el.setWidth(width);
31042                 b.el.setHeight(height);
31043                 
31044             }, this);
31045             
31046             if(!box.length){
31047                 return;
31048             }
31049             
31050             var positions = [];
31051             
31052             switch (box.length){
31053                 case 1 :
31054                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31055                     break;
31056                 case 2 :
31057                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31058                     break;
31059                 case 3 :
31060                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31061                     break;
31062                 case 4 :
31063                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31064                     break;
31065                 default :
31066                     break;
31067             }
31068             
31069             Roo.each(box, function(b,kk){
31070                 
31071                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31072                 
31073                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31074                 
31075             }, this);
31076             
31077         }, this);
31078         
31079     },
31080     
31081     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31082     {
31083         Roo.each(eItems, function(b,k){
31084             
31085             b.size = (k == 0) ? 'sm' : 'xs';
31086             b.x = (k == 0) ? 2 : 1;
31087             b.y = (k == 0) ? 2 : 1;
31088             
31089             b.el.position('absolute');
31090             
31091             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31092                 
31093             b.el.setWidth(width);
31094             
31095             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31096             
31097             b.el.setHeight(height);
31098             
31099         }, this);
31100
31101         var positions = [];
31102         
31103         positions.push({
31104             x : maxX - this.unitWidth * 2 - this.gutter,
31105             y : minY
31106         });
31107         
31108         positions.push({
31109             x : maxX - this.unitWidth,
31110             y : minY + (this.unitWidth + this.gutter) * 2
31111         });
31112         
31113         positions.push({
31114             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31115             y : minY
31116         });
31117         
31118         Roo.each(eItems, function(b,k){
31119             
31120             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31121
31122         }, this);
31123         
31124     },
31125     
31126     getVerticalOneBoxColPositions : function(x, y, box)
31127     {
31128         var pos = [];
31129         
31130         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31131         
31132         if(box[0].size == 'md-left'){
31133             rand = 0;
31134         }
31135         
31136         if(box[0].size == 'md-right'){
31137             rand = 1;
31138         }
31139         
31140         pos.push({
31141             x : x + (this.unitWidth + this.gutter) * rand,
31142             y : y
31143         });
31144         
31145         return pos;
31146     },
31147     
31148     getVerticalTwoBoxColPositions : function(x, y, box)
31149     {
31150         var pos = [];
31151         
31152         if(box[0].size == 'xs'){
31153             
31154             pos.push({
31155                 x : x,
31156                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31157             });
31158
31159             pos.push({
31160                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31161                 y : y
31162             });
31163             
31164             return pos;
31165             
31166         }
31167         
31168         pos.push({
31169             x : x,
31170             y : y
31171         });
31172
31173         pos.push({
31174             x : x + (this.unitWidth + this.gutter) * 2,
31175             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31176         });
31177         
31178         return pos;
31179         
31180     },
31181     
31182     getVerticalThreeBoxColPositions : function(x, y, box)
31183     {
31184         var pos = [];
31185         
31186         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31187             
31188             pos.push({
31189                 x : x,
31190                 y : y
31191             });
31192
31193             pos.push({
31194                 x : x + (this.unitWidth + this.gutter) * 1,
31195                 y : y
31196             });
31197             
31198             pos.push({
31199                 x : x + (this.unitWidth + this.gutter) * 2,
31200                 y : y
31201             });
31202             
31203             return pos;
31204             
31205         }
31206         
31207         if(box[0].size == 'xs' && box[1].size == 'xs'){
31208             
31209             pos.push({
31210                 x : x,
31211                 y : y
31212             });
31213
31214             pos.push({
31215                 x : x,
31216                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31217             });
31218             
31219             pos.push({
31220                 x : x + (this.unitWidth + this.gutter) * 1,
31221                 y : y
31222             });
31223             
31224             return pos;
31225             
31226         }
31227         
31228         pos.push({
31229             x : x,
31230             y : y
31231         });
31232
31233         pos.push({
31234             x : x + (this.unitWidth + this.gutter) * 2,
31235             y : y
31236         });
31237
31238         pos.push({
31239             x : x + (this.unitWidth + this.gutter) * 2,
31240             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31241         });
31242             
31243         return pos;
31244         
31245     },
31246     
31247     getVerticalFourBoxColPositions : function(x, y, box)
31248     {
31249         var pos = [];
31250         
31251         if(box[0].size == 'xs'){
31252             
31253             pos.push({
31254                 x : x,
31255                 y : y
31256             });
31257
31258             pos.push({
31259                 x : x,
31260                 y : y + (this.unitHeight + this.gutter) * 1
31261             });
31262             
31263             pos.push({
31264                 x : x,
31265                 y : y + (this.unitHeight + this.gutter) * 2
31266             });
31267             
31268             pos.push({
31269                 x : x + (this.unitWidth + this.gutter) * 1,
31270                 y : y
31271             });
31272             
31273             return pos;
31274             
31275         }
31276         
31277         pos.push({
31278             x : x,
31279             y : y
31280         });
31281
31282         pos.push({
31283             x : x + (this.unitWidth + this.gutter) * 2,
31284             y : y
31285         });
31286
31287         pos.push({
31288             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31289             y : y + (this.unitHeight + this.gutter) * 1
31290         });
31291
31292         pos.push({
31293             x : x + (this.unitWidth + this.gutter) * 2,
31294             y : y + (this.unitWidth + this.gutter) * 2
31295         });
31296
31297         return pos;
31298         
31299     },
31300     
31301     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31302     {
31303         var pos = [];
31304         
31305         if(box[0].size == 'md-left'){
31306             pos.push({
31307                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31308                 y : minY
31309             });
31310             
31311             return pos;
31312         }
31313         
31314         if(box[0].size == 'md-right'){
31315             pos.push({
31316                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31317                 y : minY + (this.unitWidth + this.gutter) * 1
31318             });
31319             
31320             return pos;
31321         }
31322         
31323         var rand = Math.floor(Math.random() * (4 - box[0].y));
31324         
31325         pos.push({
31326             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31327             y : minY + (this.unitWidth + this.gutter) * rand
31328         });
31329         
31330         return pos;
31331         
31332     },
31333     
31334     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31335     {
31336         var pos = [];
31337         
31338         if(box[0].size == 'xs'){
31339             
31340             pos.push({
31341                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31342                 y : minY
31343             });
31344
31345             pos.push({
31346                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31347                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31348             });
31349             
31350             return pos;
31351             
31352         }
31353         
31354         pos.push({
31355             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31356             y : minY
31357         });
31358
31359         pos.push({
31360             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31361             y : minY + (this.unitWidth + this.gutter) * 2
31362         });
31363         
31364         return pos;
31365         
31366     },
31367     
31368     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31369     {
31370         var pos = [];
31371         
31372         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31373             
31374             pos.push({
31375                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31376                 y : minY
31377             });
31378
31379             pos.push({
31380                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31381                 y : minY + (this.unitWidth + this.gutter) * 1
31382             });
31383             
31384             pos.push({
31385                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31386                 y : minY + (this.unitWidth + this.gutter) * 2
31387             });
31388             
31389             return pos;
31390             
31391         }
31392         
31393         if(box[0].size == 'xs' && box[1].size == 'xs'){
31394             
31395             pos.push({
31396                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31397                 y : minY
31398             });
31399
31400             pos.push({
31401                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31402                 y : minY
31403             });
31404             
31405             pos.push({
31406                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31407                 y : minY + (this.unitWidth + this.gutter) * 1
31408             });
31409             
31410             return pos;
31411             
31412         }
31413         
31414         pos.push({
31415             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31416             y : minY
31417         });
31418
31419         pos.push({
31420             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31421             y : minY + (this.unitWidth + this.gutter) * 2
31422         });
31423
31424         pos.push({
31425             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31426             y : minY + (this.unitWidth + this.gutter) * 2
31427         });
31428             
31429         return pos;
31430         
31431     },
31432     
31433     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31434     {
31435         var pos = [];
31436         
31437         if(box[0].size == 'xs'){
31438             
31439             pos.push({
31440                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31441                 y : minY
31442             });
31443
31444             pos.push({
31445                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31446                 y : minY
31447             });
31448             
31449             pos.push({
31450                 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),
31451                 y : minY
31452             });
31453             
31454             pos.push({
31455                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31456                 y : minY + (this.unitWidth + this.gutter) * 1
31457             });
31458             
31459             return pos;
31460             
31461         }
31462         
31463         pos.push({
31464             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31465             y : minY
31466         });
31467         
31468         pos.push({
31469             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31470             y : minY + (this.unitWidth + this.gutter) * 2
31471         });
31472         
31473         pos.push({
31474             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31475             y : minY + (this.unitWidth + this.gutter) * 2
31476         });
31477         
31478         pos.push({
31479             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),
31480             y : minY + (this.unitWidth + this.gutter) * 2
31481         });
31482
31483         return pos;
31484         
31485     },
31486     
31487     /**
31488     * remove a Masonry Brick
31489     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31490     */
31491     removeBrick : function(brick_id)
31492     {
31493         if (!brick_id) {
31494             return;
31495         }
31496         
31497         for (var i = 0; i<this.bricks.length; i++) {
31498             if (this.bricks[i].id == brick_id) {
31499                 this.bricks.splice(i,1);
31500                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31501                 this.initial();
31502             }
31503         }
31504     },
31505     
31506     /**
31507     * adds a Masonry Brick
31508     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31509     */
31510     addBrick : function(cfg)
31511     {
31512         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31513         //this.register(cn);
31514         cn.parentId = this.id;
31515         cn.onRender(this.el, null);
31516         return cn;
31517     },
31518     
31519     /**
31520     * register a Masonry Brick
31521     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31522     */
31523     
31524     register : function(brick)
31525     {
31526         this.bricks.push(brick);
31527         brick.masonryId = this.id;
31528     },
31529     
31530     /**
31531     * clear all the Masonry Brick
31532     */
31533     clearAll : function()
31534     {
31535         this.bricks = [];
31536         //this.getChildContainer().dom.innerHTML = "";
31537         this.el.dom.innerHTML = '';
31538     },
31539     
31540     getSelected : function()
31541     {
31542         if (!this.selectedBrick) {
31543             return false;
31544         }
31545         
31546         return this.selectedBrick;
31547     }
31548 });
31549
31550 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31551     
31552     groups: {},
31553      /**
31554     * register a Masonry Layout
31555     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31556     */
31557     
31558     register : function(layout)
31559     {
31560         this.groups[layout.id] = layout;
31561     },
31562     /**
31563     * fetch a  Masonry Layout based on the masonry layout ID
31564     * @param {string} the masonry layout to add
31565     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31566     */
31567     
31568     get: function(layout_id) {
31569         if (typeof(this.groups[layout_id]) == 'undefined') {
31570             return false;
31571         }
31572         return this.groups[layout_id] ;
31573     }
31574     
31575     
31576     
31577 });
31578
31579  
31580
31581  /**
31582  *
31583  * This is based on 
31584  * http://masonry.desandro.com
31585  *
31586  * The idea is to render all the bricks based on vertical width...
31587  *
31588  * The original code extends 'outlayer' - we might need to use that....
31589  * 
31590  */
31591
31592
31593 /**
31594  * @class Roo.bootstrap.LayoutMasonryAuto
31595  * @extends Roo.bootstrap.Component
31596  * Bootstrap Layout Masonry class
31597  * 
31598  * @constructor
31599  * Create a new Element
31600  * @param {Object} config The config object
31601  */
31602
31603 Roo.bootstrap.LayoutMasonryAuto = function(config){
31604     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31605 };
31606
31607 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31608     
31609       /**
31610      * @cfg {Boolean} isFitWidth  - resize the width..
31611      */   
31612     isFitWidth : false,  // options..
31613     /**
31614      * @cfg {Boolean} isOriginLeft = left align?
31615      */   
31616     isOriginLeft : true,
31617     /**
31618      * @cfg {Boolean} isOriginTop = top align?
31619      */   
31620     isOriginTop : false,
31621     /**
31622      * @cfg {Boolean} isLayoutInstant = no animation?
31623      */   
31624     isLayoutInstant : false, // needed?
31625     /**
31626      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31627      */   
31628     isResizingContainer : true,
31629     /**
31630      * @cfg {Number} columnWidth  width of the columns 
31631      */   
31632     
31633     columnWidth : 0,
31634     
31635     /**
31636      * @cfg {Number} maxCols maximum number of columns
31637      */   
31638     
31639     maxCols: 0,
31640     /**
31641      * @cfg {Number} padHeight padding below box..
31642      */   
31643     
31644     padHeight : 10, 
31645     
31646     /**
31647      * @cfg {Boolean} isAutoInitial defalut true
31648      */   
31649     
31650     isAutoInitial : true, 
31651     
31652     // private?
31653     gutter : 0,
31654     
31655     containerWidth: 0,
31656     initialColumnWidth : 0,
31657     currentSize : null,
31658     
31659     colYs : null, // array.
31660     maxY : 0,
31661     padWidth: 10,
31662     
31663     
31664     tag: 'div',
31665     cls: '',
31666     bricks: null, //CompositeElement
31667     cols : 0, // array?
31668     // element : null, // wrapped now this.el
31669     _isLayoutInited : null, 
31670     
31671     
31672     getAutoCreate : function(){
31673         
31674         var cfg = {
31675             tag: this.tag,
31676             cls: 'blog-masonary-wrapper ' + this.cls,
31677             cn : {
31678                 cls : 'mas-boxes masonary'
31679             }
31680         };
31681         
31682         return cfg;
31683     },
31684     
31685     getChildContainer: function( )
31686     {
31687         if (this.boxesEl) {
31688             return this.boxesEl;
31689         }
31690         
31691         this.boxesEl = this.el.select('.mas-boxes').first();
31692         
31693         return this.boxesEl;
31694     },
31695     
31696     
31697     initEvents : function()
31698     {
31699         var _this = this;
31700         
31701         if(this.isAutoInitial){
31702             Roo.log('hook children rendered');
31703             this.on('childrenrendered', function() {
31704                 Roo.log('children rendered');
31705                 _this.initial();
31706             } ,this);
31707         }
31708         
31709     },
31710     
31711     initial : function()
31712     {
31713         this.reloadItems();
31714
31715         this.currentSize = this.el.getBox(true);
31716
31717         /// was window resize... - let's see if this works..
31718         Roo.EventManager.onWindowResize(this.resize, this); 
31719
31720         if(!this.isAutoInitial){
31721             this.layout();
31722             return;
31723         }
31724         
31725         this.layout.defer(500,this);
31726     },
31727     
31728     reloadItems: function()
31729     {
31730         this.bricks = this.el.select('.masonry-brick', true);
31731         
31732         this.bricks.each(function(b) {
31733             //Roo.log(b.getSize());
31734             if (!b.attr('originalwidth')) {
31735                 b.attr('originalwidth',  b.getSize().width);
31736             }
31737             
31738         });
31739         
31740         Roo.log(this.bricks.elements.length);
31741     },
31742     
31743     resize : function()
31744     {
31745         Roo.log('resize');
31746         var cs = this.el.getBox(true);
31747         
31748         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31749             Roo.log("no change in with or X");
31750             return;
31751         }
31752         this.currentSize = cs;
31753         this.layout();
31754     },
31755     
31756     layout : function()
31757     {
31758          Roo.log('layout');
31759         this._resetLayout();
31760         //this._manageStamps();
31761       
31762         // don't animate first layout
31763         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31764         this.layoutItems( isInstant );
31765       
31766         // flag for initalized
31767         this._isLayoutInited = true;
31768     },
31769     
31770     layoutItems : function( isInstant )
31771     {
31772         //var items = this._getItemsForLayout( this.items );
31773         // original code supports filtering layout items.. we just ignore it..
31774         
31775         this._layoutItems( this.bricks , isInstant );
31776       
31777         this._postLayout();
31778     },
31779     _layoutItems : function ( items , isInstant)
31780     {
31781        //this.fireEvent( 'layout', this, items );
31782     
31783
31784         if ( !items || !items.elements.length ) {
31785           // no items, emit event with empty array
31786             return;
31787         }
31788
31789         var queue = [];
31790         items.each(function(item) {
31791             Roo.log("layout item");
31792             Roo.log(item);
31793             // get x/y object from method
31794             var position = this._getItemLayoutPosition( item );
31795             // enqueue
31796             position.item = item;
31797             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31798             queue.push( position );
31799         }, this);
31800       
31801         this._processLayoutQueue( queue );
31802     },
31803     /** Sets position of item in DOM
31804     * @param {Element} item
31805     * @param {Number} x - horizontal position
31806     * @param {Number} y - vertical position
31807     * @param {Boolean} isInstant - disables transitions
31808     */
31809     _processLayoutQueue : function( queue )
31810     {
31811         for ( var i=0, len = queue.length; i < len; i++ ) {
31812             var obj = queue[i];
31813             obj.item.position('absolute');
31814             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31815         }
31816     },
31817       
31818     
31819     /**
31820     * Any logic you want to do after each layout,
31821     * i.e. size the container
31822     */
31823     _postLayout : function()
31824     {
31825         this.resizeContainer();
31826     },
31827     
31828     resizeContainer : function()
31829     {
31830         if ( !this.isResizingContainer ) {
31831             return;
31832         }
31833         var size = this._getContainerSize();
31834         if ( size ) {
31835             this.el.setSize(size.width,size.height);
31836             this.boxesEl.setSize(size.width,size.height);
31837         }
31838     },
31839     
31840     
31841     
31842     _resetLayout : function()
31843     {
31844         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31845         this.colWidth = this.el.getWidth();
31846         //this.gutter = this.el.getWidth(); 
31847         
31848         this.measureColumns();
31849
31850         // reset column Y
31851         var i = this.cols;
31852         this.colYs = [];
31853         while (i--) {
31854             this.colYs.push( 0 );
31855         }
31856     
31857         this.maxY = 0;
31858     },
31859
31860     measureColumns : function()
31861     {
31862         this.getContainerWidth();
31863       // if columnWidth is 0, default to outerWidth of first item
31864         if ( !this.columnWidth ) {
31865             var firstItem = this.bricks.first();
31866             Roo.log(firstItem);
31867             this.columnWidth  = this.containerWidth;
31868             if (firstItem && firstItem.attr('originalwidth') ) {
31869                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31870             }
31871             // columnWidth fall back to item of first element
31872             Roo.log("set column width?");
31873                         this.initialColumnWidth = this.columnWidth  ;
31874
31875             // if first elem has no width, default to size of container
31876             
31877         }
31878         
31879         
31880         if (this.initialColumnWidth) {
31881             this.columnWidth = this.initialColumnWidth;
31882         }
31883         
31884         
31885             
31886         // column width is fixed at the top - however if container width get's smaller we should
31887         // reduce it...
31888         
31889         // this bit calcs how man columns..
31890             
31891         var columnWidth = this.columnWidth += this.gutter;
31892       
31893         // calculate columns
31894         var containerWidth = this.containerWidth + this.gutter;
31895         
31896         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31897         // fix rounding errors, typically with gutters
31898         var excess = columnWidth - containerWidth % columnWidth;
31899         
31900         
31901         // if overshoot is less than a pixel, round up, otherwise floor it
31902         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31903         cols = Math[ mathMethod ]( cols );
31904         this.cols = Math.max( cols, 1 );
31905         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31906         
31907          // padding positioning..
31908         var totalColWidth = this.cols * this.columnWidth;
31909         var padavail = this.containerWidth - totalColWidth;
31910         // so for 2 columns - we need 3 'pads'
31911         
31912         var padNeeded = (1+this.cols) * this.padWidth;
31913         
31914         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31915         
31916         this.columnWidth += padExtra
31917         //this.padWidth = Math.floor(padavail /  ( this.cols));
31918         
31919         // adjust colum width so that padding is fixed??
31920         
31921         // we have 3 columns ... total = width * 3
31922         // we have X left over... that should be used by 
31923         
31924         //if (this.expandC) {
31925             
31926         //}
31927         
31928         
31929         
31930     },
31931     
31932     getContainerWidth : function()
31933     {
31934        /* // container is parent if fit width
31935         var container = this.isFitWidth ? this.element.parentNode : this.element;
31936         // check that this.size and size are there
31937         // IE8 triggers resize on body size change, so they might not be
31938         
31939         var size = getSize( container );  //FIXME
31940         this.containerWidth = size && size.innerWidth; //FIXME
31941         */
31942          
31943         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31944         
31945     },
31946     
31947     _getItemLayoutPosition : function( item )  // what is item?
31948     {
31949         // we resize the item to our columnWidth..
31950       
31951         item.setWidth(this.columnWidth);
31952         item.autoBoxAdjust  = false;
31953         
31954         var sz = item.getSize();
31955  
31956         // how many columns does this brick span
31957         var remainder = this.containerWidth % this.columnWidth;
31958         
31959         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31960         // round if off by 1 pixel, otherwise use ceil
31961         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31962         colSpan = Math.min( colSpan, this.cols );
31963         
31964         // normally this should be '1' as we dont' currently allow multi width columns..
31965         
31966         var colGroup = this._getColGroup( colSpan );
31967         // get the minimum Y value from the columns
31968         var minimumY = Math.min.apply( Math, colGroup );
31969         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31970         
31971         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31972          
31973         // position the brick
31974         var position = {
31975             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31976             y: this.currentSize.y + minimumY + this.padHeight
31977         };
31978         
31979         Roo.log(position);
31980         // apply setHeight to necessary columns
31981         var setHeight = minimumY + sz.height + this.padHeight;
31982         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31983         
31984         var setSpan = this.cols + 1 - colGroup.length;
31985         for ( var i = 0; i < setSpan; i++ ) {
31986           this.colYs[ shortColIndex + i ] = setHeight ;
31987         }
31988       
31989         return position;
31990     },
31991     
31992     /**
31993      * @param {Number} colSpan - number of columns the element spans
31994      * @returns {Array} colGroup
31995      */
31996     _getColGroup : function( colSpan )
31997     {
31998         if ( colSpan < 2 ) {
31999           // if brick spans only one column, use all the column Ys
32000           return this.colYs;
32001         }
32002       
32003         var colGroup = [];
32004         // how many different places could this brick fit horizontally
32005         var groupCount = this.cols + 1 - colSpan;
32006         // for each group potential horizontal position
32007         for ( var i = 0; i < groupCount; i++ ) {
32008           // make an array of colY values for that one group
32009           var groupColYs = this.colYs.slice( i, i + colSpan );
32010           // and get the max value of the array
32011           colGroup[i] = Math.max.apply( Math, groupColYs );
32012         }
32013         return colGroup;
32014     },
32015     /*
32016     _manageStamp : function( stamp )
32017     {
32018         var stampSize =  stamp.getSize();
32019         var offset = stamp.getBox();
32020         // get the columns that this stamp affects
32021         var firstX = this.isOriginLeft ? offset.x : offset.right;
32022         var lastX = firstX + stampSize.width;
32023         var firstCol = Math.floor( firstX / this.columnWidth );
32024         firstCol = Math.max( 0, firstCol );
32025         
32026         var lastCol = Math.floor( lastX / this.columnWidth );
32027         // lastCol should not go over if multiple of columnWidth #425
32028         lastCol -= lastX % this.columnWidth ? 0 : 1;
32029         lastCol = Math.min( this.cols - 1, lastCol );
32030         
32031         // set colYs to bottom of the stamp
32032         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32033             stampSize.height;
32034             
32035         for ( var i = firstCol; i <= lastCol; i++ ) {
32036           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32037         }
32038     },
32039     */
32040     
32041     _getContainerSize : function()
32042     {
32043         this.maxY = Math.max.apply( Math, this.colYs );
32044         var size = {
32045             height: this.maxY
32046         };
32047       
32048         if ( this.isFitWidth ) {
32049             size.width = this._getContainerFitWidth();
32050         }
32051       
32052         return size;
32053     },
32054     
32055     _getContainerFitWidth : function()
32056     {
32057         var unusedCols = 0;
32058         // count unused columns
32059         var i = this.cols;
32060         while ( --i ) {
32061           if ( this.colYs[i] !== 0 ) {
32062             break;
32063           }
32064           unusedCols++;
32065         }
32066         // fit container to columns that have been used
32067         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32068     },
32069     
32070     needsResizeLayout : function()
32071     {
32072         var previousWidth = this.containerWidth;
32073         this.getContainerWidth();
32074         return previousWidth !== this.containerWidth;
32075     }
32076  
32077 });
32078
32079  
32080
32081  /*
32082  * - LGPL
32083  *
32084  * element
32085  * 
32086  */
32087
32088 /**
32089  * @class Roo.bootstrap.MasonryBrick
32090  * @extends Roo.bootstrap.Component
32091  * Bootstrap MasonryBrick class
32092  * 
32093  * @constructor
32094  * Create a new MasonryBrick
32095  * @param {Object} config The config object
32096  */
32097
32098 Roo.bootstrap.MasonryBrick = function(config){
32099     
32100     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32101     
32102     Roo.bootstrap.MasonryBrick.register(this);
32103     
32104     this.addEvents({
32105         // raw events
32106         /**
32107          * @event click
32108          * When a MasonryBrick is clcik
32109          * @param {Roo.bootstrap.MasonryBrick} this
32110          * @param {Roo.EventObject} e
32111          */
32112         "click" : true
32113     });
32114 };
32115
32116 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32117     
32118     /**
32119      * @cfg {String} title
32120      */   
32121     title : '',
32122     /**
32123      * @cfg {String} html
32124      */   
32125     html : '',
32126     /**
32127      * @cfg {String} bgimage
32128      */   
32129     bgimage : '',
32130     /**
32131      * @cfg {String} videourl
32132      */   
32133     videourl : '',
32134     /**
32135      * @cfg {String} cls
32136      */   
32137     cls : '',
32138     /**
32139      * @cfg {String} href
32140      */   
32141     href : '',
32142     /**
32143      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32144      */   
32145     size : 'xs',
32146     
32147     /**
32148      * @cfg {String} placetitle (center|bottom)
32149      */   
32150     placetitle : '',
32151     
32152     /**
32153      * @cfg {Boolean} isFitContainer defalut true
32154      */   
32155     isFitContainer : true, 
32156     
32157     /**
32158      * @cfg {Boolean} preventDefault defalut false
32159      */   
32160     preventDefault : false, 
32161     
32162     /**
32163      * @cfg {Boolean} inverse defalut false
32164      */   
32165     maskInverse : false, 
32166     
32167     getAutoCreate : function()
32168     {
32169         if(!this.isFitContainer){
32170             return this.getSplitAutoCreate();
32171         }
32172         
32173         var cls = 'masonry-brick masonry-brick-full';
32174         
32175         if(this.href.length){
32176             cls += ' masonry-brick-link';
32177         }
32178         
32179         if(this.bgimage.length){
32180             cls += ' masonry-brick-image';
32181         }
32182         
32183         if(this.maskInverse){
32184             cls += ' mask-inverse';
32185         }
32186         
32187         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32188             cls += ' enable-mask';
32189         }
32190         
32191         if(this.size){
32192             cls += ' masonry-' + this.size + '-brick';
32193         }
32194         
32195         if(this.placetitle.length){
32196             
32197             switch (this.placetitle) {
32198                 case 'center' :
32199                     cls += ' masonry-center-title';
32200                     break;
32201                 case 'bottom' :
32202                     cls += ' masonry-bottom-title';
32203                     break;
32204                 default:
32205                     break;
32206             }
32207             
32208         } else {
32209             if(!this.html.length && !this.bgimage.length){
32210                 cls += ' masonry-center-title';
32211             }
32212
32213             if(!this.html.length && this.bgimage.length){
32214                 cls += ' masonry-bottom-title';
32215             }
32216         }
32217         
32218         if(this.cls){
32219             cls += ' ' + this.cls;
32220         }
32221         
32222         var cfg = {
32223             tag: (this.href.length) ? 'a' : 'div',
32224             cls: cls,
32225             cn: [
32226                 {
32227                     tag: 'div',
32228                     cls: 'masonry-brick-mask'
32229                 },
32230                 {
32231                     tag: 'div',
32232                     cls: 'masonry-brick-paragraph',
32233                     cn: []
32234                 }
32235             ]
32236         };
32237         
32238         if(this.href.length){
32239             cfg.href = this.href;
32240         }
32241         
32242         var cn = cfg.cn[1].cn;
32243         
32244         if(this.title.length){
32245             cn.push({
32246                 tag: 'h4',
32247                 cls: 'masonry-brick-title',
32248                 html: this.title
32249             });
32250         }
32251         
32252         if(this.html.length){
32253             cn.push({
32254                 tag: 'p',
32255                 cls: 'masonry-brick-text',
32256                 html: this.html
32257             });
32258         }
32259         
32260         if (!this.title.length && !this.html.length) {
32261             cfg.cn[1].cls += ' hide';
32262         }
32263         
32264         if(this.bgimage.length){
32265             cfg.cn.push({
32266                 tag: 'img',
32267                 cls: 'masonry-brick-image-view',
32268                 src: this.bgimage
32269             });
32270         }
32271         
32272         if(this.videourl.length){
32273             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32274             // youtube support only?
32275             cfg.cn.push({
32276                 tag: 'iframe',
32277                 cls: 'masonry-brick-image-view',
32278                 src: vurl,
32279                 frameborder : 0,
32280                 allowfullscreen : true
32281             });
32282         }
32283         
32284         return cfg;
32285         
32286     },
32287     
32288     getSplitAutoCreate : function()
32289     {
32290         var cls = 'masonry-brick masonry-brick-split';
32291         
32292         if(this.href.length){
32293             cls += ' masonry-brick-link';
32294         }
32295         
32296         if(this.bgimage.length){
32297             cls += ' masonry-brick-image';
32298         }
32299         
32300         if(this.size){
32301             cls += ' masonry-' + this.size + '-brick';
32302         }
32303         
32304         switch (this.placetitle) {
32305             case 'center' :
32306                 cls += ' masonry-center-title';
32307                 break;
32308             case 'bottom' :
32309                 cls += ' masonry-bottom-title';
32310                 break;
32311             default:
32312                 if(!this.bgimage.length){
32313                     cls += ' masonry-center-title';
32314                 }
32315
32316                 if(this.bgimage.length){
32317                     cls += ' masonry-bottom-title';
32318                 }
32319                 break;
32320         }
32321         
32322         if(this.cls){
32323             cls += ' ' + this.cls;
32324         }
32325         
32326         var cfg = {
32327             tag: (this.href.length) ? 'a' : 'div',
32328             cls: cls,
32329             cn: [
32330                 {
32331                     tag: 'div',
32332                     cls: 'masonry-brick-split-head',
32333                     cn: [
32334                         {
32335                             tag: 'div',
32336                             cls: 'masonry-brick-paragraph',
32337                             cn: []
32338                         }
32339                     ]
32340                 },
32341                 {
32342                     tag: 'div',
32343                     cls: 'masonry-brick-split-body',
32344                     cn: []
32345                 }
32346             ]
32347         };
32348         
32349         if(this.href.length){
32350             cfg.href = this.href;
32351         }
32352         
32353         if(this.title.length){
32354             cfg.cn[0].cn[0].cn.push({
32355                 tag: 'h4',
32356                 cls: 'masonry-brick-title',
32357                 html: this.title
32358             });
32359         }
32360         
32361         if(this.html.length){
32362             cfg.cn[1].cn.push({
32363                 tag: 'p',
32364                 cls: 'masonry-brick-text',
32365                 html: this.html
32366             });
32367         }
32368
32369         if(this.bgimage.length){
32370             cfg.cn[0].cn.push({
32371                 tag: 'img',
32372                 cls: 'masonry-brick-image-view',
32373                 src: this.bgimage
32374             });
32375         }
32376         
32377         if(this.videourl.length){
32378             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32379             // youtube support only?
32380             cfg.cn[0].cn.cn.push({
32381                 tag: 'iframe',
32382                 cls: 'masonry-brick-image-view',
32383                 src: vurl,
32384                 frameborder : 0,
32385                 allowfullscreen : true
32386             });
32387         }
32388         
32389         return cfg;
32390     },
32391     
32392     initEvents: function() 
32393     {
32394         switch (this.size) {
32395             case 'xs' :
32396                 this.x = 1;
32397                 this.y = 1;
32398                 break;
32399             case 'sm' :
32400                 this.x = 2;
32401                 this.y = 2;
32402                 break;
32403             case 'md' :
32404             case 'md-left' :
32405             case 'md-right' :
32406                 this.x = 3;
32407                 this.y = 3;
32408                 break;
32409             case 'tall' :
32410                 this.x = 2;
32411                 this.y = 3;
32412                 break;
32413             case 'wide' :
32414                 this.x = 3;
32415                 this.y = 2;
32416                 break;
32417             case 'wide-thin' :
32418                 this.x = 3;
32419                 this.y = 1;
32420                 break;
32421                         
32422             default :
32423                 break;
32424         }
32425         
32426         if(Roo.isTouch){
32427             this.el.on('touchstart', this.onTouchStart, this);
32428             this.el.on('touchmove', this.onTouchMove, this);
32429             this.el.on('touchend', this.onTouchEnd, this);
32430             this.el.on('contextmenu', this.onContextMenu, this);
32431         } else {
32432             this.el.on('mouseenter'  ,this.enter, this);
32433             this.el.on('mouseleave', this.leave, this);
32434             this.el.on('click', this.onClick, this);
32435         }
32436         
32437         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32438             this.parent().bricks.push(this);   
32439         }
32440         
32441     },
32442     
32443     onClick: function(e, el)
32444     {
32445         var time = this.endTimer - this.startTimer;
32446         // Roo.log(e.preventDefault());
32447         if(Roo.isTouch){
32448             if(time > 1000){
32449                 e.preventDefault();
32450                 return;
32451             }
32452         }
32453         
32454         if(!this.preventDefault){
32455             return;
32456         }
32457         
32458         e.preventDefault();
32459         
32460         if (this.activcClass != '') {
32461             this.selectBrick();
32462         }
32463         
32464         this.fireEvent('click', this);
32465     },
32466     
32467     enter: function(e, el)
32468     {
32469         e.preventDefault();
32470         
32471         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32472             return;
32473         }
32474         
32475         if(this.bgimage.length && this.html.length){
32476             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32477         }
32478     },
32479     
32480     leave: function(e, el)
32481     {
32482         e.preventDefault();
32483         
32484         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32485             return;
32486         }
32487         
32488         if(this.bgimage.length && this.html.length){
32489             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32490         }
32491     },
32492     
32493     onTouchStart: function(e, el)
32494     {
32495 //        e.preventDefault();
32496         
32497         this.touchmoved = false;
32498         
32499         if(!this.isFitContainer){
32500             return;
32501         }
32502         
32503         if(!this.bgimage.length || !this.html.length){
32504             return;
32505         }
32506         
32507         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32508         
32509         this.timer = new Date().getTime();
32510         
32511     },
32512     
32513     onTouchMove: function(e, el)
32514     {
32515         this.touchmoved = true;
32516     },
32517     
32518     onContextMenu : function(e,el)
32519     {
32520         e.preventDefault();
32521         e.stopPropagation();
32522         return false;
32523     },
32524     
32525     onTouchEnd: function(e, el)
32526     {
32527 //        e.preventDefault();
32528         
32529         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32530         
32531             this.leave(e,el);
32532             
32533             return;
32534         }
32535         
32536         if(!this.bgimage.length || !this.html.length){
32537             
32538             if(this.href.length){
32539                 window.location.href = this.href;
32540             }
32541             
32542             return;
32543         }
32544         
32545         if(!this.isFitContainer){
32546             return;
32547         }
32548         
32549         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32550         
32551         window.location.href = this.href;
32552     },
32553     
32554     //selection on single brick only
32555     selectBrick : function() {
32556         
32557         if (!this.parentId) {
32558             return;
32559         }
32560         
32561         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32562         var index = m.selectedBrick.indexOf(this.id);
32563         
32564         if ( index > -1) {
32565             m.selectedBrick.splice(index,1);
32566             this.el.removeClass(this.activeClass);
32567             return;
32568         }
32569         
32570         for(var i = 0; i < m.selectedBrick.length; i++) {
32571             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32572             b.el.removeClass(b.activeClass);
32573         }
32574         
32575         m.selectedBrick = [];
32576         
32577         m.selectedBrick.push(this.id);
32578         this.el.addClass(this.activeClass);
32579         return;
32580     }
32581     
32582 });
32583
32584 Roo.apply(Roo.bootstrap.MasonryBrick, {
32585     
32586     //groups: {},
32587     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32588      /**
32589     * register a Masonry Brick
32590     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32591     */
32592     
32593     register : function(brick)
32594     {
32595         //this.groups[brick.id] = brick;
32596         this.groups.add(brick.id, brick);
32597     },
32598     /**
32599     * fetch a  masonry brick based on the masonry brick ID
32600     * @param {string} the masonry brick to add
32601     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32602     */
32603     
32604     get: function(brick_id) 
32605     {
32606         // if (typeof(this.groups[brick_id]) == 'undefined') {
32607         //     return false;
32608         // }
32609         // return this.groups[brick_id] ;
32610         
32611         if(this.groups.key(brick_id)) {
32612             return this.groups.key(brick_id);
32613         }
32614         
32615         return false;
32616     }
32617     
32618     
32619     
32620 });
32621
32622  /*
32623  * - LGPL
32624  *
32625  * element
32626  * 
32627  */
32628
32629 /**
32630  * @class Roo.bootstrap.Brick
32631  * @extends Roo.bootstrap.Component
32632  * Bootstrap Brick class
32633  * 
32634  * @constructor
32635  * Create a new Brick
32636  * @param {Object} config The config object
32637  */
32638
32639 Roo.bootstrap.Brick = function(config){
32640     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32641     
32642     this.addEvents({
32643         // raw events
32644         /**
32645          * @event click
32646          * When a Brick is click
32647          * @param {Roo.bootstrap.Brick} this
32648          * @param {Roo.EventObject} e
32649          */
32650         "click" : true
32651     });
32652 };
32653
32654 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32655     
32656     /**
32657      * @cfg {String} title
32658      */   
32659     title : '',
32660     /**
32661      * @cfg {String} html
32662      */   
32663     html : '',
32664     /**
32665      * @cfg {String} bgimage
32666      */   
32667     bgimage : '',
32668     /**
32669      * @cfg {String} cls
32670      */   
32671     cls : '',
32672     /**
32673      * @cfg {String} href
32674      */   
32675     href : '',
32676     /**
32677      * @cfg {String} video
32678      */   
32679     video : '',
32680     /**
32681      * @cfg {Boolean} square
32682      */   
32683     square : true,
32684     
32685     getAutoCreate : function()
32686     {
32687         var cls = 'roo-brick';
32688         
32689         if(this.href.length){
32690             cls += ' roo-brick-link';
32691         }
32692         
32693         if(this.bgimage.length){
32694             cls += ' roo-brick-image';
32695         }
32696         
32697         if(!this.html.length && !this.bgimage.length){
32698             cls += ' roo-brick-center-title';
32699         }
32700         
32701         if(!this.html.length && this.bgimage.length){
32702             cls += ' roo-brick-bottom-title';
32703         }
32704         
32705         if(this.cls){
32706             cls += ' ' + this.cls;
32707         }
32708         
32709         var cfg = {
32710             tag: (this.href.length) ? 'a' : 'div',
32711             cls: cls,
32712             cn: [
32713                 {
32714                     tag: 'div',
32715                     cls: 'roo-brick-paragraph',
32716                     cn: []
32717                 }
32718             ]
32719         };
32720         
32721         if(this.href.length){
32722             cfg.href = this.href;
32723         }
32724         
32725         var cn = cfg.cn[0].cn;
32726         
32727         if(this.title.length){
32728             cn.push({
32729                 tag: 'h4',
32730                 cls: 'roo-brick-title',
32731                 html: this.title
32732             });
32733         }
32734         
32735         if(this.html.length){
32736             cn.push({
32737                 tag: 'p',
32738                 cls: 'roo-brick-text',
32739                 html: this.html
32740             });
32741         } else {
32742             cn.cls += ' hide';
32743         }
32744         
32745         if(this.bgimage.length){
32746             cfg.cn.push({
32747                 tag: 'img',
32748                 cls: 'roo-brick-image-view',
32749                 src: this.bgimage
32750             });
32751         }
32752         
32753         return cfg;
32754     },
32755     
32756     initEvents: function() 
32757     {
32758         if(this.title.length || this.html.length){
32759             this.el.on('mouseenter'  ,this.enter, this);
32760             this.el.on('mouseleave', this.leave, this);
32761         }
32762         
32763         Roo.EventManager.onWindowResize(this.resize, this); 
32764         
32765         if(this.bgimage.length){
32766             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32767             this.imageEl.on('load', this.onImageLoad, this);
32768             return;
32769         }
32770         
32771         this.resize();
32772     },
32773     
32774     onImageLoad : function()
32775     {
32776         this.resize();
32777     },
32778     
32779     resize : function()
32780     {
32781         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32782         
32783         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32784         
32785         if(this.bgimage.length){
32786             var image = this.el.select('.roo-brick-image-view', true).first();
32787             
32788             image.setWidth(paragraph.getWidth());
32789             
32790             if(this.square){
32791                 image.setHeight(paragraph.getWidth());
32792             }
32793             
32794             this.el.setHeight(image.getHeight());
32795             paragraph.setHeight(image.getHeight());
32796             
32797         }
32798         
32799     },
32800     
32801     enter: function(e, el)
32802     {
32803         e.preventDefault();
32804         
32805         if(this.bgimage.length){
32806             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32807             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32808         }
32809     },
32810     
32811     leave: function(e, el)
32812     {
32813         e.preventDefault();
32814         
32815         if(this.bgimage.length){
32816             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32817             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32818         }
32819     }
32820     
32821 });
32822
32823  
32824
32825  /*
32826  * - LGPL
32827  *
32828  * Input
32829  * 
32830  */
32831
32832 /**
32833  * @class Roo.bootstrap.NumberField
32834  * @extends Roo.bootstrap.Input
32835  * Bootstrap NumberField class
32836  * 
32837  * 
32838  * 
32839  * 
32840  * @constructor
32841  * Create a new NumberField
32842  * @param {Object} config The config object
32843  */
32844
32845 Roo.bootstrap.NumberField = function(config){
32846     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32847 };
32848
32849 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32850     
32851     /**
32852      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32853      */
32854     allowDecimals : true,
32855     /**
32856      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32857      */
32858     decimalSeparator : ".",
32859     /**
32860      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32861      */
32862     decimalPrecision : 2,
32863     /**
32864      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32865      */
32866     allowNegative : true,
32867     /**
32868      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32869      */
32870     minValue : Number.NEGATIVE_INFINITY,
32871     /**
32872      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32873      */
32874     maxValue : Number.MAX_VALUE,
32875     /**
32876      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32877      */
32878     minText : "The minimum value for this field is {0}",
32879     /**
32880      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32881      */
32882     maxText : "The maximum value for this field is {0}",
32883     /**
32884      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32885      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32886      */
32887     nanText : "{0} is not a valid number",
32888     /**
32889      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32890      */
32891     castInt : true,
32892
32893     // private
32894     initEvents : function()
32895     {   
32896         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32897         
32898         var allowed = "0123456789";
32899         
32900         if(this.allowDecimals){
32901             allowed += this.decimalSeparator;
32902         }
32903         
32904         if(this.allowNegative){
32905             allowed += "-";
32906         }
32907         
32908         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32909         
32910         var keyPress = function(e){
32911             
32912             var k = e.getKey();
32913             
32914             var c = e.getCharCode();
32915             
32916             if(
32917                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32918                     allowed.indexOf(String.fromCharCode(c)) === -1
32919             ){
32920                 e.stopEvent();
32921                 return;
32922             }
32923             
32924             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32925                 return;
32926             }
32927             
32928             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32929                 e.stopEvent();
32930             }
32931         };
32932         
32933         this.el.on("keypress", keyPress, this);
32934     },
32935     
32936     validateValue : function(value)
32937     {
32938         
32939         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32940             return false;
32941         }
32942         
32943         var num = this.parseValue(value);
32944         
32945         if(isNaN(num)){
32946             this.markInvalid(String.format(this.nanText, value));
32947             return false;
32948         }
32949         
32950         if(num < this.minValue){
32951             this.markInvalid(String.format(this.minText, this.minValue));
32952             return false;
32953         }
32954         
32955         if(num > this.maxValue){
32956             this.markInvalid(String.format(this.maxText, this.maxValue));
32957             return false;
32958         }
32959         
32960         return true;
32961     },
32962
32963     getValue : function()
32964     {
32965         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32966     },
32967
32968     parseValue : function(value)
32969     {
32970         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32971         return isNaN(value) ? '' : value;
32972     },
32973
32974     fixPrecision : function(value)
32975     {
32976         var nan = isNaN(value);
32977         
32978         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32979             return nan ? '' : value;
32980         }
32981         return parseFloat(value).toFixed(this.decimalPrecision);
32982     },
32983
32984     setValue : function(v)
32985     {
32986         v = this.fixPrecision(v);
32987         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32988     },
32989
32990     decimalPrecisionFcn : function(v)
32991     {
32992         return Math.floor(v);
32993     },
32994
32995     beforeBlur : function()
32996     {
32997         if(!this.castInt){
32998             return;
32999         }
33000         
33001         var v = this.parseValue(this.getRawValue());
33002         if(v){
33003             this.setValue(v);
33004         }
33005     }
33006     
33007 });
33008
33009  
33010
33011 /*
33012 * Licence: LGPL
33013 */
33014
33015 /**
33016  * @class Roo.bootstrap.DocumentSlider
33017  * @extends Roo.bootstrap.Component
33018  * Bootstrap DocumentSlider class
33019  * 
33020  * @constructor
33021  * Create a new DocumentViewer
33022  * @param {Object} config The config object
33023  */
33024
33025 Roo.bootstrap.DocumentSlider = function(config){
33026     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33027     
33028     this.files = [];
33029     
33030     this.addEvents({
33031         /**
33032          * @event initial
33033          * Fire after initEvent
33034          * @param {Roo.bootstrap.DocumentSlider} this
33035          */
33036         "initial" : true,
33037         /**
33038          * @event update
33039          * Fire after update
33040          * @param {Roo.bootstrap.DocumentSlider} this
33041          */
33042         "update" : true,
33043         /**
33044          * @event click
33045          * Fire after click
33046          * @param {Roo.bootstrap.DocumentSlider} this
33047          */
33048         "click" : true
33049     });
33050 };
33051
33052 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33053     
33054     files : false,
33055     
33056     indicator : 0,
33057     
33058     getAutoCreate : function()
33059     {
33060         var cfg = {
33061             tag : 'div',
33062             cls : 'roo-document-slider',
33063             cn : [
33064                 {
33065                     tag : 'div',
33066                     cls : 'roo-document-slider-header',
33067                     cn : [
33068                         {
33069                             tag : 'div',
33070                             cls : 'roo-document-slider-header-title'
33071                         }
33072                     ]
33073                 },
33074                 {
33075                     tag : 'div',
33076                     cls : 'roo-document-slider-body',
33077                     cn : [
33078                         {
33079                             tag : 'div',
33080                             cls : 'roo-document-slider-prev',
33081                             cn : [
33082                                 {
33083                                     tag : 'i',
33084                                     cls : 'fa fa-chevron-left'
33085                                 }
33086                             ]
33087                         },
33088                         {
33089                             tag : 'div',
33090                             cls : 'roo-document-slider-thumb',
33091                             cn : [
33092                                 {
33093                                     tag : 'img',
33094                                     cls : 'roo-document-slider-image'
33095                                 }
33096                             ]
33097                         },
33098                         {
33099                             tag : 'div',
33100                             cls : 'roo-document-slider-next',
33101                             cn : [
33102                                 {
33103                                     tag : 'i',
33104                                     cls : 'fa fa-chevron-right'
33105                                 }
33106                             ]
33107                         }
33108                     ]
33109                 }
33110             ]
33111         };
33112         
33113         return cfg;
33114     },
33115     
33116     initEvents : function()
33117     {
33118         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33119         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33120         
33121         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33122         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33123         
33124         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33125         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33126         
33127         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33128         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33129         
33130         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33131         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33132         
33133         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33134         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33135         
33136         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33137         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33138         
33139         this.thumbEl.on('click', this.onClick, this);
33140         
33141         this.prevIndicator.on('click', this.prev, this);
33142         
33143         this.nextIndicator.on('click', this.next, this);
33144         
33145     },
33146     
33147     initial : function()
33148     {
33149         if(this.files.length){
33150             this.indicator = 1;
33151             this.update()
33152         }
33153         
33154         this.fireEvent('initial', this);
33155     },
33156     
33157     update : function()
33158     {
33159         this.imageEl.attr('src', this.files[this.indicator - 1]);
33160         
33161         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33162         
33163         this.prevIndicator.show();
33164         
33165         if(this.indicator == 1){
33166             this.prevIndicator.hide();
33167         }
33168         
33169         this.nextIndicator.show();
33170         
33171         if(this.indicator == this.files.length){
33172             this.nextIndicator.hide();
33173         }
33174         
33175         this.thumbEl.scrollTo('top');
33176         
33177         this.fireEvent('update', this);
33178     },
33179     
33180     onClick : function(e)
33181     {
33182         e.preventDefault();
33183         
33184         this.fireEvent('click', this);
33185     },
33186     
33187     prev : function(e)
33188     {
33189         e.preventDefault();
33190         
33191         this.indicator = Math.max(1, this.indicator - 1);
33192         
33193         this.update();
33194     },
33195     
33196     next : function(e)
33197     {
33198         e.preventDefault();
33199         
33200         this.indicator = Math.min(this.files.length, this.indicator + 1);
33201         
33202         this.update();
33203     }
33204 });
33205 /*
33206  * - LGPL
33207  *
33208  * RadioSet
33209  *
33210  *
33211  */
33212
33213 /**
33214  * @class Roo.bootstrap.RadioSet
33215  * @extends Roo.bootstrap.Input
33216  * Bootstrap RadioSet class
33217  * @cfg {String} indicatorpos (left|right) default left
33218  * @cfg {Boolean} inline (true|false) inline the element (default true)
33219  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33220  * @constructor
33221  * Create a new RadioSet
33222  * @param {Object} config The config object
33223  */
33224
33225 Roo.bootstrap.RadioSet = function(config){
33226     
33227     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33228     
33229     this.radioes = [];
33230     
33231     Roo.bootstrap.RadioSet.register(this);
33232     
33233     this.addEvents({
33234         /**
33235         * @event check
33236         * Fires when the element is checked or unchecked.
33237         * @param {Roo.bootstrap.RadioSet} this This radio
33238         * @param {Roo.bootstrap.Radio} item The checked item
33239         */
33240        check : true
33241     });
33242     
33243 };
33244
33245 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33246
33247     radioes : false,
33248     
33249     inline : true,
33250     
33251     weight : '',
33252     
33253     indicatorpos : 'left',
33254     
33255     getAutoCreate : function()
33256     {
33257         var label = {
33258             tag : 'label',
33259             cls : 'roo-radio-set-label',
33260             cn : [
33261                 {
33262                     tag : 'span',
33263                     html : this.fieldLabel
33264                 }
33265             ]
33266         };
33267         
33268         if(this.indicatorpos == 'left'){
33269             label.cn.unshift({
33270                 tag : 'i',
33271                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33272                 tooltip : 'This field is required'
33273             });
33274         } else {
33275             label.cn.push({
33276                 tag : 'i',
33277                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33278                 tooltip : 'This field is required'
33279             });
33280         }
33281         
33282         var items = {
33283             tag : 'div',
33284             cls : 'roo-radio-set-items'
33285         };
33286         
33287         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33288         
33289         if (align === 'left' && this.fieldLabel.length) {
33290             
33291             items = {
33292                 cls : "roo-radio-set-right", 
33293                 cn: [
33294                     items
33295                 ]
33296             };
33297             
33298             if(this.labelWidth > 12){
33299                 label.style = "width: " + this.labelWidth + 'px';
33300             }
33301             
33302             if(this.labelWidth < 13 && this.labelmd == 0){
33303                 this.labelmd = this.labelWidth;
33304             }
33305             
33306             if(this.labellg > 0){
33307                 label.cls += ' col-lg-' + this.labellg;
33308                 items.cls += ' col-lg-' + (12 - this.labellg);
33309             }
33310             
33311             if(this.labelmd > 0){
33312                 label.cls += ' col-md-' + this.labelmd;
33313                 items.cls += ' col-md-' + (12 - this.labelmd);
33314             }
33315             
33316             if(this.labelsm > 0){
33317                 label.cls += ' col-sm-' + this.labelsm;
33318                 items.cls += ' col-sm-' + (12 - this.labelsm);
33319             }
33320             
33321             if(this.labelxs > 0){
33322                 label.cls += ' col-xs-' + this.labelxs;
33323                 items.cls += ' col-xs-' + (12 - this.labelxs);
33324             }
33325         }
33326         
33327         var cfg = {
33328             tag : 'div',
33329             cls : 'roo-radio-set',
33330             cn : [
33331                 {
33332                     tag : 'input',
33333                     cls : 'roo-radio-set-input',
33334                     type : 'hidden',
33335                     name : this.name,
33336                     value : this.value ? this.value :  ''
33337                 },
33338                 label,
33339                 items
33340             ]
33341         };
33342         
33343         if(this.weight.length){
33344             cfg.cls += ' roo-radio-' + this.weight;
33345         }
33346         
33347         if(this.inline) {
33348             cfg.cls += ' roo-radio-set-inline';
33349         }
33350         
33351         var settings=this;
33352         ['xs','sm','md','lg'].map(function(size){
33353             if (settings[size]) {
33354                 cfg.cls += ' col-' + size + '-' + settings[size];
33355             }
33356         });
33357         
33358         return cfg;
33359         
33360     },
33361
33362     initEvents : function()
33363     {
33364         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33365         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33366         
33367         if(!this.fieldLabel.length){
33368             this.labelEl.hide();
33369         }
33370         
33371         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33372         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33373         
33374         this.indicatorEl().addClass('invisible');
33375         
33376         this.originalValue = this.getValue();
33377         
33378     },
33379     
33380     inputEl: function ()
33381     {
33382         return this.el.select('.roo-radio-set-input', true).first();
33383     },
33384     
33385     getChildContainer : function()
33386     {
33387         return this.itemsEl;
33388     },
33389     
33390     register : function(item)
33391     {
33392         this.radioes.push(item);
33393         
33394     },
33395     
33396     validate : function()
33397     {   
33398         var valid = false;
33399         
33400         Roo.each(this.radioes, function(i){
33401             if(!i.checked){
33402                 return;
33403             }
33404             
33405             valid = true;
33406             return false;
33407         });
33408         
33409         if(this.allowBlank) {
33410             return true;
33411         }
33412         
33413         if(this.disabled || valid){
33414             this.markValid();
33415             return true;
33416         }
33417         
33418         this.markInvalid();
33419         return false;
33420         
33421     },
33422     
33423     markValid : function()
33424     {
33425         if(this.labelEl.isVisible(true)){
33426             this.indicatorEl().removeClass('visible');
33427             this.indicatorEl().addClass('invisible');
33428         }
33429         
33430         this.el.removeClass([this.invalidClass, this.validClass]);
33431         this.el.addClass(this.validClass);
33432         
33433         this.fireEvent('valid', this);
33434     },
33435     
33436     markInvalid : function(msg)
33437     {
33438         if(this.allowBlank || this.disabled){
33439             return;
33440         }
33441         
33442         if(this.labelEl.isVisible(true)){
33443             this.indicatorEl().removeClass('invisible');
33444             this.indicatorEl().addClass('visible');
33445         }
33446         
33447         this.el.removeClass([this.invalidClass, this.validClass]);
33448         this.el.addClass(this.invalidClass);
33449         
33450         this.fireEvent('invalid', this, msg);
33451         
33452     },
33453     
33454     setValue : function(v, suppressEvent)
33455     {   
33456         this.value = v;
33457         if(this.rendered){
33458             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33459         }
33460         
33461         Roo.each(this.radioes, function(i){
33462             
33463             i.checked = false;
33464             i.el.removeClass('checked');
33465             
33466             if(i.value === v || i.value.toString() === v.toString()){
33467                 i.checked = true;
33468                 i.el.addClass('checked');
33469                 
33470                 if(suppressEvent !== true){
33471                     this.fireEvent('check', this, i);
33472                 }
33473             }
33474             
33475         }, this);
33476         
33477         this.validate();
33478     },
33479     
33480     clearInvalid : function(){
33481         
33482         if(!this.el || this.preventMark){
33483             return;
33484         }
33485         
33486         this.el.removeClass([this.invalidClass]);
33487         
33488         this.fireEvent('valid', this);
33489     }
33490     
33491 });
33492
33493 Roo.apply(Roo.bootstrap.RadioSet, {
33494     
33495     groups: {},
33496     
33497     register : function(set)
33498     {
33499         this.groups[set.name] = set;
33500     },
33501     
33502     get: function(name) 
33503     {
33504         if (typeof(this.groups[name]) == 'undefined') {
33505             return false;
33506         }
33507         
33508         return this.groups[name] ;
33509     }
33510     
33511 });
33512 /*
33513  * Based on:
33514  * Ext JS Library 1.1.1
33515  * Copyright(c) 2006-2007, Ext JS, LLC.
33516  *
33517  * Originally Released Under LGPL - original licence link has changed is not relivant.
33518  *
33519  * Fork - LGPL
33520  * <script type="text/javascript">
33521  */
33522
33523
33524 /**
33525  * @class Roo.bootstrap.SplitBar
33526  * @extends Roo.util.Observable
33527  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33528  * <br><br>
33529  * Usage:
33530  * <pre><code>
33531 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33532                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33533 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33534 split.minSize = 100;
33535 split.maxSize = 600;
33536 split.animate = true;
33537 split.on('moved', splitterMoved);
33538 </code></pre>
33539  * @constructor
33540  * Create a new SplitBar
33541  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33542  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33543  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33544  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33545                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33546                         position of the SplitBar).
33547  */
33548 Roo.bootstrap.SplitBar = function(cfg){
33549     
33550     /** @private */
33551     
33552     //{
33553     //  dragElement : elm
33554     //  resizingElement: el,
33555         // optional..
33556     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33557     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33558         // existingProxy ???
33559     //}
33560     
33561     this.el = Roo.get(cfg.dragElement, true);
33562     this.el.dom.unselectable = "on";
33563     /** @private */
33564     this.resizingEl = Roo.get(cfg.resizingElement, true);
33565
33566     /**
33567      * @private
33568      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33569      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33570      * @type Number
33571      */
33572     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33573     
33574     /**
33575      * The minimum size of the resizing element. (Defaults to 0)
33576      * @type Number
33577      */
33578     this.minSize = 0;
33579     
33580     /**
33581      * The maximum size of the resizing element. (Defaults to 2000)
33582      * @type Number
33583      */
33584     this.maxSize = 2000;
33585     
33586     /**
33587      * Whether to animate the transition to the new size
33588      * @type Boolean
33589      */
33590     this.animate = false;
33591     
33592     /**
33593      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33594      * @type Boolean
33595      */
33596     this.useShim = false;
33597     
33598     /** @private */
33599     this.shim = null;
33600     
33601     if(!cfg.existingProxy){
33602         /** @private */
33603         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33604     }else{
33605         this.proxy = Roo.get(cfg.existingProxy).dom;
33606     }
33607     /** @private */
33608     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33609     
33610     /** @private */
33611     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33612     
33613     /** @private */
33614     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33615     
33616     /** @private */
33617     this.dragSpecs = {};
33618     
33619     /**
33620      * @private The adapter to use to positon and resize elements
33621      */
33622     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33623     this.adapter.init(this);
33624     
33625     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33626         /** @private */
33627         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33628         this.el.addClass("roo-splitbar-h");
33629     }else{
33630         /** @private */
33631         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33632         this.el.addClass("roo-splitbar-v");
33633     }
33634     
33635     this.addEvents({
33636         /**
33637          * @event resize
33638          * Fires when the splitter is moved (alias for {@link #event-moved})
33639          * @param {Roo.bootstrap.SplitBar} this
33640          * @param {Number} newSize the new width or height
33641          */
33642         "resize" : true,
33643         /**
33644          * @event moved
33645          * Fires when the splitter is moved
33646          * @param {Roo.bootstrap.SplitBar} this
33647          * @param {Number} newSize the new width or height
33648          */
33649         "moved" : true,
33650         /**
33651          * @event beforeresize
33652          * Fires before the splitter is dragged
33653          * @param {Roo.bootstrap.SplitBar} this
33654          */
33655         "beforeresize" : true,
33656
33657         "beforeapply" : true
33658     });
33659
33660     Roo.util.Observable.call(this);
33661 };
33662
33663 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33664     onStartProxyDrag : function(x, y){
33665         this.fireEvent("beforeresize", this);
33666         if(!this.overlay){
33667             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33668             o.unselectable();
33669             o.enableDisplayMode("block");
33670             // all splitbars share the same overlay
33671             Roo.bootstrap.SplitBar.prototype.overlay = o;
33672         }
33673         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33674         this.overlay.show();
33675         Roo.get(this.proxy).setDisplayed("block");
33676         var size = this.adapter.getElementSize(this);
33677         this.activeMinSize = this.getMinimumSize();;
33678         this.activeMaxSize = this.getMaximumSize();;
33679         var c1 = size - this.activeMinSize;
33680         var c2 = Math.max(this.activeMaxSize - size, 0);
33681         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33682             this.dd.resetConstraints();
33683             this.dd.setXConstraint(
33684                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33685                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33686             );
33687             this.dd.setYConstraint(0, 0);
33688         }else{
33689             this.dd.resetConstraints();
33690             this.dd.setXConstraint(0, 0);
33691             this.dd.setYConstraint(
33692                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33693                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33694             );
33695          }
33696         this.dragSpecs.startSize = size;
33697         this.dragSpecs.startPoint = [x, y];
33698         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33699     },
33700     
33701     /** 
33702      * @private Called after the drag operation by the DDProxy
33703      */
33704     onEndProxyDrag : function(e){
33705         Roo.get(this.proxy).setDisplayed(false);
33706         var endPoint = Roo.lib.Event.getXY(e);
33707         if(this.overlay){
33708             this.overlay.hide();
33709         }
33710         var newSize;
33711         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33712             newSize = this.dragSpecs.startSize + 
33713                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33714                     endPoint[0] - this.dragSpecs.startPoint[0] :
33715                     this.dragSpecs.startPoint[0] - endPoint[0]
33716                 );
33717         }else{
33718             newSize = this.dragSpecs.startSize + 
33719                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33720                     endPoint[1] - this.dragSpecs.startPoint[1] :
33721                     this.dragSpecs.startPoint[1] - endPoint[1]
33722                 );
33723         }
33724         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33725         if(newSize != this.dragSpecs.startSize){
33726             if(this.fireEvent('beforeapply', this, newSize) !== false){
33727                 this.adapter.setElementSize(this, newSize);
33728                 this.fireEvent("moved", this, newSize);
33729                 this.fireEvent("resize", this, newSize);
33730             }
33731         }
33732     },
33733     
33734     /**
33735      * Get the adapter this SplitBar uses
33736      * @return The adapter object
33737      */
33738     getAdapter : function(){
33739         return this.adapter;
33740     },
33741     
33742     /**
33743      * Set the adapter this SplitBar uses
33744      * @param {Object} adapter A SplitBar adapter object
33745      */
33746     setAdapter : function(adapter){
33747         this.adapter = adapter;
33748         this.adapter.init(this);
33749     },
33750     
33751     /**
33752      * Gets the minimum size for the resizing element
33753      * @return {Number} The minimum size
33754      */
33755     getMinimumSize : function(){
33756         return this.minSize;
33757     },
33758     
33759     /**
33760      * Sets the minimum size for the resizing element
33761      * @param {Number} minSize The minimum size
33762      */
33763     setMinimumSize : function(minSize){
33764         this.minSize = minSize;
33765     },
33766     
33767     /**
33768      * Gets the maximum size for the resizing element
33769      * @return {Number} The maximum size
33770      */
33771     getMaximumSize : function(){
33772         return this.maxSize;
33773     },
33774     
33775     /**
33776      * Sets the maximum size for the resizing element
33777      * @param {Number} maxSize The maximum size
33778      */
33779     setMaximumSize : function(maxSize){
33780         this.maxSize = maxSize;
33781     },
33782     
33783     /**
33784      * Sets the initialize size for the resizing element
33785      * @param {Number} size The initial size
33786      */
33787     setCurrentSize : function(size){
33788         var oldAnimate = this.animate;
33789         this.animate = false;
33790         this.adapter.setElementSize(this, size);
33791         this.animate = oldAnimate;
33792     },
33793     
33794     /**
33795      * Destroy this splitbar. 
33796      * @param {Boolean} removeEl True to remove the element
33797      */
33798     destroy : function(removeEl){
33799         if(this.shim){
33800             this.shim.remove();
33801         }
33802         this.dd.unreg();
33803         this.proxy.parentNode.removeChild(this.proxy);
33804         if(removeEl){
33805             this.el.remove();
33806         }
33807     }
33808 });
33809
33810 /**
33811  * @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.
33812  */
33813 Roo.bootstrap.SplitBar.createProxy = function(dir){
33814     var proxy = new Roo.Element(document.createElement("div"));
33815     proxy.unselectable();
33816     var cls = 'roo-splitbar-proxy';
33817     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33818     document.body.appendChild(proxy.dom);
33819     return proxy.dom;
33820 };
33821
33822 /** 
33823  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33824  * Default Adapter. It assumes the splitter and resizing element are not positioned
33825  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33826  */
33827 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33828 };
33829
33830 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33831     // do nothing for now
33832     init : function(s){
33833     
33834     },
33835     /**
33836      * Called before drag operations to get the current size of the resizing element. 
33837      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33838      */
33839      getElementSize : function(s){
33840         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33841             return s.resizingEl.getWidth();
33842         }else{
33843             return s.resizingEl.getHeight();
33844         }
33845     },
33846     
33847     /**
33848      * Called after drag operations to set the size of the resizing element.
33849      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33850      * @param {Number} newSize The new size to set
33851      * @param {Function} onComplete A function to be invoked when resizing is complete
33852      */
33853     setElementSize : function(s, newSize, onComplete){
33854         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33855             if(!s.animate){
33856                 s.resizingEl.setWidth(newSize);
33857                 if(onComplete){
33858                     onComplete(s, newSize);
33859                 }
33860             }else{
33861                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33862             }
33863         }else{
33864             
33865             if(!s.animate){
33866                 s.resizingEl.setHeight(newSize);
33867                 if(onComplete){
33868                     onComplete(s, newSize);
33869                 }
33870             }else{
33871                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33872             }
33873         }
33874     }
33875 };
33876
33877 /** 
33878  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33879  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33880  * Adapter that  moves the splitter element to align with the resized sizing element. 
33881  * Used with an absolute positioned SplitBar.
33882  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33883  * document.body, make sure you assign an id to the body element.
33884  */
33885 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33886     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33887     this.container = Roo.get(container);
33888 };
33889
33890 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33891     init : function(s){
33892         this.basic.init(s);
33893     },
33894     
33895     getElementSize : function(s){
33896         return this.basic.getElementSize(s);
33897     },
33898     
33899     setElementSize : function(s, newSize, onComplete){
33900         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33901     },
33902     
33903     moveSplitter : function(s){
33904         var yes = Roo.bootstrap.SplitBar;
33905         switch(s.placement){
33906             case yes.LEFT:
33907                 s.el.setX(s.resizingEl.getRight());
33908                 break;
33909             case yes.RIGHT:
33910                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33911                 break;
33912             case yes.TOP:
33913                 s.el.setY(s.resizingEl.getBottom());
33914                 break;
33915             case yes.BOTTOM:
33916                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33917                 break;
33918         }
33919     }
33920 };
33921
33922 /**
33923  * Orientation constant - Create a vertical SplitBar
33924  * @static
33925  * @type Number
33926  */
33927 Roo.bootstrap.SplitBar.VERTICAL = 1;
33928
33929 /**
33930  * Orientation constant - Create a horizontal SplitBar
33931  * @static
33932  * @type Number
33933  */
33934 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33935
33936 /**
33937  * Placement constant - The resizing element is to the left of the splitter element
33938  * @static
33939  * @type Number
33940  */
33941 Roo.bootstrap.SplitBar.LEFT = 1;
33942
33943 /**
33944  * Placement constant - The resizing element is to the right of the splitter element
33945  * @static
33946  * @type Number
33947  */
33948 Roo.bootstrap.SplitBar.RIGHT = 2;
33949
33950 /**
33951  * Placement constant - The resizing element is positioned above the splitter element
33952  * @static
33953  * @type Number
33954  */
33955 Roo.bootstrap.SplitBar.TOP = 3;
33956
33957 /**
33958  * Placement constant - The resizing element is positioned under splitter element
33959  * @static
33960  * @type Number
33961  */
33962 Roo.bootstrap.SplitBar.BOTTOM = 4;
33963 Roo.namespace("Roo.bootstrap.layout");/*
33964  * Based on:
33965  * Ext JS Library 1.1.1
33966  * Copyright(c) 2006-2007, Ext JS, LLC.
33967  *
33968  * Originally Released Under LGPL - original licence link has changed is not relivant.
33969  *
33970  * Fork - LGPL
33971  * <script type="text/javascript">
33972  */
33973
33974 /**
33975  * @class Roo.bootstrap.layout.Manager
33976  * @extends Roo.bootstrap.Component
33977  * Base class for layout managers.
33978  */
33979 Roo.bootstrap.layout.Manager = function(config)
33980 {
33981     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33982
33983
33984
33985
33986
33987     /** false to disable window resize monitoring @type Boolean */
33988     this.monitorWindowResize = true;
33989     this.regions = {};
33990     this.addEvents({
33991         /**
33992          * @event layout
33993          * Fires when a layout is performed.
33994          * @param {Roo.LayoutManager} this
33995          */
33996         "layout" : true,
33997         /**
33998          * @event regionresized
33999          * Fires when the user resizes a region.
34000          * @param {Roo.LayoutRegion} region The resized region
34001          * @param {Number} newSize The new size (width for east/west, height for north/south)
34002          */
34003         "regionresized" : true,
34004         /**
34005          * @event regioncollapsed
34006          * Fires when a region is collapsed.
34007          * @param {Roo.LayoutRegion} region The collapsed region
34008          */
34009         "regioncollapsed" : true,
34010         /**
34011          * @event regionexpanded
34012          * Fires when a region is expanded.
34013          * @param {Roo.LayoutRegion} region The expanded region
34014          */
34015         "regionexpanded" : true
34016     });
34017     this.updating = false;
34018
34019     if (config.el) {
34020         this.el = Roo.get(config.el);
34021         this.initEvents();
34022     }
34023
34024 };
34025
34026 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34027
34028
34029     regions : null,
34030
34031     monitorWindowResize : true,
34032
34033
34034     updating : false,
34035
34036
34037     onRender : function(ct, position)
34038     {
34039         if(!this.el){
34040             this.el = Roo.get(ct);
34041             this.initEvents();
34042         }
34043         //this.fireEvent('render',this);
34044     },
34045
34046
34047     initEvents: function()
34048     {
34049
34050
34051         // ie scrollbar fix
34052         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34053             document.body.scroll = "no";
34054         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34055             this.el.position('relative');
34056         }
34057         this.id = this.el.id;
34058         this.el.addClass("roo-layout-container");
34059         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34060         if(this.el.dom != document.body ) {
34061             this.el.on('resize', this.layout,this);
34062             this.el.on('show', this.layout,this);
34063         }
34064
34065     },
34066
34067     /**
34068      * Returns true if this layout is currently being updated
34069      * @return {Boolean}
34070      */
34071     isUpdating : function(){
34072         return this.updating;
34073     },
34074
34075     /**
34076      * Suspend the LayoutManager from doing auto-layouts while
34077      * making multiple add or remove calls
34078      */
34079     beginUpdate : function(){
34080         this.updating = true;
34081     },
34082
34083     /**
34084      * Restore auto-layouts and optionally disable the manager from performing a layout
34085      * @param {Boolean} noLayout true to disable a layout update
34086      */
34087     endUpdate : function(noLayout){
34088         this.updating = false;
34089         if(!noLayout){
34090             this.layout();
34091         }
34092     },
34093
34094     layout: function(){
34095         // abstract...
34096     },
34097
34098     onRegionResized : function(region, newSize){
34099         this.fireEvent("regionresized", region, newSize);
34100         this.layout();
34101     },
34102
34103     onRegionCollapsed : function(region){
34104         this.fireEvent("regioncollapsed", region);
34105     },
34106
34107     onRegionExpanded : function(region){
34108         this.fireEvent("regionexpanded", region);
34109     },
34110
34111     /**
34112      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34113      * performs box-model adjustments.
34114      * @return {Object} The size as an object {width: (the width), height: (the height)}
34115      */
34116     getViewSize : function()
34117     {
34118         var size;
34119         if(this.el.dom != document.body){
34120             size = this.el.getSize();
34121         }else{
34122             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34123         }
34124         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34125         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34126         return size;
34127     },
34128
34129     /**
34130      * Returns the Element this layout is bound to.
34131      * @return {Roo.Element}
34132      */
34133     getEl : function(){
34134         return this.el;
34135     },
34136
34137     /**
34138      * Returns the specified region.
34139      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34140      * @return {Roo.LayoutRegion}
34141      */
34142     getRegion : function(target){
34143         return this.regions[target.toLowerCase()];
34144     },
34145
34146     onWindowResize : function(){
34147         if(this.monitorWindowResize){
34148             this.layout();
34149         }
34150     }
34151 });
34152 /*
34153  * Based on:
34154  * Ext JS Library 1.1.1
34155  * Copyright(c) 2006-2007, Ext JS, LLC.
34156  *
34157  * Originally Released Under LGPL - original licence link has changed is not relivant.
34158  *
34159  * Fork - LGPL
34160  * <script type="text/javascript">
34161  */
34162 /**
34163  * @class Roo.bootstrap.layout.Border
34164  * @extends Roo.bootstrap.layout.Manager
34165  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34166  * please see: examples/bootstrap/nested.html<br><br>
34167  
34168 <b>The container the layout is rendered into can be either the body element or any other element.
34169 If it is not the body element, the container needs to either be an absolute positioned element,
34170 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34171 the container size if it is not the body element.</b>
34172
34173 * @constructor
34174 * Create a new Border
34175 * @param {Object} config Configuration options
34176  */
34177 Roo.bootstrap.layout.Border = function(config){
34178     config = config || {};
34179     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34180     
34181     
34182     
34183     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34184         if(config[region]){
34185             config[region].region = region;
34186             this.addRegion(config[region]);
34187         }
34188     },this);
34189     
34190 };
34191
34192 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34193
34194 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34195     /**
34196      * Creates and adds a new region if it doesn't already exist.
34197      * @param {String} target The target region key (north, south, east, west or center).
34198      * @param {Object} config The regions config object
34199      * @return {BorderLayoutRegion} The new region
34200      */
34201     addRegion : function(config)
34202     {
34203         if(!this.regions[config.region]){
34204             var r = this.factory(config);
34205             this.bindRegion(r);
34206         }
34207         return this.regions[config.region];
34208     },
34209
34210     // private (kinda)
34211     bindRegion : function(r){
34212         this.regions[r.config.region] = r;
34213         
34214         r.on("visibilitychange",    this.layout, this);
34215         r.on("paneladded",          this.layout, this);
34216         r.on("panelremoved",        this.layout, this);
34217         r.on("invalidated",         this.layout, this);
34218         r.on("resized",             this.onRegionResized, this);
34219         r.on("collapsed",           this.onRegionCollapsed, this);
34220         r.on("expanded",            this.onRegionExpanded, this);
34221     },
34222
34223     /**
34224      * Performs a layout update.
34225      */
34226     layout : function()
34227     {
34228         if(this.updating) {
34229             return;
34230         }
34231         
34232         // render all the rebions if they have not been done alreayd?
34233         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34234             if(this.regions[region] && !this.regions[region].bodyEl){
34235                 this.regions[region].onRender(this.el)
34236             }
34237         },this);
34238         
34239         var size = this.getViewSize();
34240         var w = size.width;
34241         var h = size.height;
34242         var centerW = w;
34243         var centerH = h;
34244         var centerY = 0;
34245         var centerX = 0;
34246         //var x = 0, y = 0;
34247
34248         var rs = this.regions;
34249         var north = rs["north"];
34250         var south = rs["south"]; 
34251         var west = rs["west"];
34252         var east = rs["east"];
34253         var center = rs["center"];
34254         //if(this.hideOnLayout){ // not supported anymore
34255             //c.el.setStyle("display", "none");
34256         //}
34257         if(north && north.isVisible()){
34258             var b = north.getBox();
34259             var m = north.getMargins();
34260             b.width = w - (m.left+m.right);
34261             b.x = m.left;
34262             b.y = m.top;
34263             centerY = b.height + b.y + m.bottom;
34264             centerH -= centerY;
34265             north.updateBox(this.safeBox(b));
34266         }
34267         if(south && south.isVisible()){
34268             var b = south.getBox();
34269             var m = south.getMargins();
34270             b.width = w - (m.left+m.right);
34271             b.x = m.left;
34272             var totalHeight = (b.height + m.top + m.bottom);
34273             b.y = h - totalHeight + m.top;
34274             centerH -= totalHeight;
34275             south.updateBox(this.safeBox(b));
34276         }
34277         if(west && west.isVisible()){
34278             var b = west.getBox();
34279             var m = west.getMargins();
34280             b.height = centerH - (m.top+m.bottom);
34281             b.x = m.left;
34282             b.y = centerY + m.top;
34283             var totalWidth = (b.width + m.left + m.right);
34284             centerX += totalWidth;
34285             centerW -= totalWidth;
34286             west.updateBox(this.safeBox(b));
34287         }
34288         if(east && east.isVisible()){
34289             var b = east.getBox();
34290             var m = east.getMargins();
34291             b.height = centerH - (m.top+m.bottom);
34292             var totalWidth = (b.width + m.left + m.right);
34293             b.x = w - totalWidth + m.left;
34294             b.y = centerY + m.top;
34295             centerW -= totalWidth;
34296             east.updateBox(this.safeBox(b));
34297         }
34298         if(center){
34299             var m = center.getMargins();
34300             var centerBox = {
34301                 x: centerX + m.left,
34302                 y: centerY + m.top,
34303                 width: centerW - (m.left+m.right),
34304                 height: centerH - (m.top+m.bottom)
34305             };
34306             //if(this.hideOnLayout){
34307                 //center.el.setStyle("display", "block");
34308             //}
34309             center.updateBox(this.safeBox(centerBox));
34310         }
34311         this.el.repaint();
34312         this.fireEvent("layout", this);
34313     },
34314
34315     // private
34316     safeBox : function(box){
34317         box.width = Math.max(0, box.width);
34318         box.height = Math.max(0, box.height);
34319         return box;
34320     },
34321
34322     /**
34323      * Adds a ContentPanel (or subclass) to this layout.
34324      * @param {String} target The target region key (north, south, east, west or center).
34325      * @param {Roo.ContentPanel} panel The panel to add
34326      * @return {Roo.ContentPanel} The added panel
34327      */
34328     add : function(target, panel){
34329          
34330         target = target.toLowerCase();
34331         return this.regions[target].add(panel);
34332     },
34333
34334     /**
34335      * Remove a ContentPanel (or subclass) to this layout.
34336      * @param {String} target The target region key (north, south, east, west or center).
34337      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34338      * @return {Roo.ContentPanel} The removed panel
34339      */
34340     remove : function(target, panel){
34341         target = target.toLowerCase();
34342         return this.regions[target].remove(panel);
34343     },
34344
34345     /**
34346      * Searches all regions for a panel with the specified id
34347      * @param {String} panelId
34348      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34349      */
34350     findPanel : function(panelId){
34351         var rs = this.regions;
34352         for(var target in rs){
34353             if(typeof rs[target] != "function"){
34354                 var p = rs[target].getPanel(panelId);
34355                 if(p){
34356                     return p;
34357                 }
34358             }
34359         }
34360         return null;
34361     },
34362
34363     /**
34364      * Searches all regions for a panel with the specified id and activates (shows) it.
34365      * @param {String/ContentPanel} panelId The panels id or the panel itself
34366      * @return {Roo.ContentPanel} The shown panel or null
34367      */
34368     showPanel : function(panelId) {
34369       var rs = this.regions;
34370       for(var target in rs){
34371          var r = rs[target];
34372          if(typeof r != "function"){
34373             if(r.hasPanel(panelId)){
34374                return r.showPanel(panelId);
34375             }
34376          }
34377       }
34378       return null;
34379    },
34380
34381    /**
34382      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34383      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34384      */
34385    /*
34386     restoreState : function(provider){
34387         if(!provider){
34388             provider = Roo.state.Manager;
34389         }
34390         var sm = new Roo.LayoutStateManager();
34391         sm.init(this, provider);
34392     },
34393 */
34394  
34395  
34396     /**
34397      * Adds a xtype elements to the layout.
34398      * <pre><code>
34399
34400 layout.addxtype({
34401        xtype : 'ContentPanel',
34402        region: 'west',
34403        items: [ .... ]
34404    }
34405 );
34406
34407 layout.addxtype({
34408         xtype : 'NestedLayoutPanel',
34409         region: 'west',
34410         layout: {
34411            center: { },
34412            west: { }   
34413         },
34414         items : [ ... list of content panels or nested layout panels.. ]
34415    }
34416 );
34417 </code></pre>
34418      * @param {Object} cfg Xtype definition of item to add.
34419      */
34420     addxtype : function(cfg)
34421     {
34422         // basically accepts a pannel...
34423         // can accept a layout region..!?!?
34424         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34425         
34426         
34427         // theory?  children can only be panels??
34428         
34429         //if (!cfg.xtype.match(/Panel$/)) {
34430         //    return false;
34431         //}
34432         var ret = false;
34433         
34434         if (typeof(cfg.region) == 'undefined') {
34435             Roo.log("Failed to add Panel, region was not set");
34436             Roo.log(cfg);
34437             return false;
34438         }
34439         var region = cfg.region;
34440         delete cfg.region;
34441         
34442           
34443         var xitems = [];
34444         if (cfg.items) {
34445             xitems = cfg.items;
34446             delete cfg.items;
34447         }
34448         var nb = false;
34449         
34450         switch(cfg.xtype) 
34451         {
34452             case 'Content':  // ContentPanel (el, cfg)
34453             case 'Scroll':  // ContentPanel (el, cfg)
34454             case 'View': 
34455                 cfg.autoCreate = true;
34456                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34457                 //} else {
34458                 //    var el = this.el.createChild();
34459                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34460                 //}
34461                 
34462                 this.add(region, ret);
34463                 break;
34464             
34465             /*
34466             case 'TreePanel': // our new panel!
34467                 cfg.el = this.el.createChild();
34468                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34469                 this.add(region, ret);
34470                 break;
34471             */
34472             
34473             case 'Nest': 
34474                 // create a new Layout (which is  a Border Layout...
34475                 
34476                 var clayout = cfg.layout;
34477                 clayout.el  = this.el.createChild();
34478                 clayout.items   = clayout.items  || [];
34479                 
34480                 delete cfg.layout;
34481                 
34482                 // replace this exitems with the clayout ones..
34483                 xitems = clayout.items;
34484                  
34485                 // force background off if it's in center...
34486                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34487                     cfg.background = false;
34488                 }
34489                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34490                 
34491                 
34492                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34493                 //console.log('adding nested layout panel '  + cfg.toSource());
34494                 this.add(region, ret);
34495                 nb = {}; /// find first...
34496                 break;
34497             
34498             case 'Grid':
34499                 
34500                 // needs grid and region
34501                 
34502                 //var el = this.getRegion(region).el.createChild();
34503                 /*
34504                  *var el = this.el.createChild();
34505                 // create the grid first...
34506                 cfg.grid.container = el;
34507                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34508                 */
34509                 
34510                 if (region == 'center' && this.active ) {
34511                     cfg.background = false;
34512                 }
34513                 
34514                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34515                 
34516                 this.add(region, ret);
34517                 /*
34518                 if (cfg.background) {
34519                     // render grid on panel activation (if panel background)
34520                     ret.on('activate', function(gp) {
34521                         if (!gp.grid.rendered) {
34522                     //        gp.grid.render(el);
34523                         }
34524                     });
34525                 } else {
34526                   //  cfg.grid.render(el);
34527                 }
34528                 */
34529                 break;
34530            
34531            
34532             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34533                 // it was the old xcomponent building that caused this before.
34534                 // espeically if border is the top element in the tree.
34535                 ret = this;
34536                 break; 
34537                 
34538                     
34539                 
34540                 
34541                 
34542             default:
34543                 /*
34544                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34545                     
34546                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34547                     this.add(region, ret);
34548                 } else {
34549                 */
34550                     Roo.log(cfg);
34551                     throw "Can not add '" + cfg.xtype + "' to Border";
34552                     return null;
34553              
34554                                 
34555              
34556         }
34557         this.beginUpdate();
34558         // add children..
34559         var region = '';
34560         var abn = {};
34561         Roo.each(xitems, function(i)  {
34562             region = nb && i.region ? i.region : false;
34563             
34564             var add = ret.addxtype(i);
34565            
34566             if (region) {
34567                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34568                 if (!i.background) {
34569                     abn[region] = nb[region] ;
34570                 }
34571             }
34572             
34573         });
34574         this.endUpdate();
34575
34576         // make the last non-background panel active..
34577         //if (nb) { Roo.log(abn); }
34578         if (nb) {
34579             
34580             for(var r in abn) {
34581                 region = this.getRegion(r);
34582                 if (region) {
34583                     // tried using nb[r], but it does not work..
34584                      
34585                     region.showPanel(abn[r]);
34586                    
34587                 }
34588             }
34589         }
34590         return ret;
34591         
34592     },
34593     
34594     
34595 // private
34596     factory : function(cfg)
34597     {
34598         
34599         var validRegions = Roo.bootstrap.layout.Border.regions;
34600
34601         var target = cfg.region;
34602         cfg.mgr = this;
34603         
34604         var r = Roo.bootstrap.layout;
34605         Roo.log(target);
34606         switch(target){
34607             case "north":
34608                 return new r.North(cfg);
34609             case "south":
34610                 return new r.South(cfg);
34611             case "east":
34612                 return new r.East(cfg);
34613             case "west":
34614                 return new r.West(cfg);
34615             case "center":
34616                 return new r.Center(cfg);
34617         }
34618         throw 'Layout region "'+target+'" not supported.';
34619     }
34620     
34621     
34622 });
34623  /*
34624  * Based on:
34625  * Ext JS Library 1.1.1
34626  * Copyright(c) 2006-2007, Ext JS, LLC.
34627  *
34628  * Originally Released Under LGPL - original licence link has changed is not relivant.
34629  *
34630  * Fork - LGPL
34631  * <script type="text/javascript">
34632  */
34633  
34634 /**
34635  * @class Roo.bootstrap.layout.Basic
34636  * @extends Roo.util.Observable
34637  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34638  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34639  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34640  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34641  * @cfg {string}   region  the region that it inhabits..
34642  * @cfg {bool}   skipConfig skip config?
34643  * 
34644
34645  */
34646 Roo.bootstrap.layout.Basic = function(config){
34647     
34648     this.mgr = config.mgr;
34649     
34650     this.position = config.region;
34651     
34652     var skipConfig = config.skipConfig;
34653     
34654     this.events = {
34655         /**
34656          * @scope Roo.BasicLayoutRegion
34657          */
34658         
34659         /**
34660          * @event beforeremove
34661          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34662          * @param {Roo.LayoutRegion} this
34663          * @param {Roo.ContentPanel} panel The panel
34664          * @param {Object} e The cancel event object
34665          */
34666         "beforeremove" : true,
34667         /**
34668          * @event invalidated
34669          * Fires when the layout for this region is changed.
34670          * @param {Roo.LayoutRegion} this
34671          */
34672         "invalidated" : true,
34673         /**
34674          * @event visibilitychange
34675          * Fires when this region is shown or hidden 
34676          * @param {Roo.LayoutRegion} this
34677          * @param {Boolean} visibility true or false
34678          */
34679         "visibilitychange" : true,
34680         /**
34681          * @event paneladded
34682          * Fires when a panel is added. 
34683          * @param {Roo.LayoutRegion} this
34684          * @param {Roo.ContentPanel} panel The panel
34685          */
34686         "paneladded" : true,
34687         /**
34688          * @event panelremoved
34689          * Fires when a panel is removed. 
34690          * @param {Roo.LayoutRegion} this
34691          * @param {Roo.ContentPanel} panel The panel
34692          */
34693         "panelremoved" : true,
34694         /**
34695          * @event beforecollapse
34696          * Fires when this region before collapse.
34697          * @param {Roo.LayoutRegion} this
34698          */
34699         "beforecollapse" : true,
34700         /**
34701          * @event collapsed
34702          * Fires when this region is collapsed.
34703          * @param {Roo.LayoutRegion} this
34704          */
34705         "collapsed" : true,
34706         /**
34707          * @event expanded
34708          * Fires when this region is expanded.
34709          * @param {Roo.LayoutRegion} this
34710          */
34711         "expanded" : true,
34712         /**
34713          * @event slideshow
34714          * Fires when this region is slid into view.
34715          * @param {Roo.LayoutRegion} this
34716          */
34717         "slideshow" : true,
34718         /**
34719          * @event slidehide
34720          * Fires when this region slides out of view. 
34721          * @param {Roo.LayoutRegion} this
34722          */
34723         "slidehide" : true,
34724         /**
34725          * @event panelactivated
34726          * Fires when a panel is activated. 
34727          * @param {Roo.LayoutRegion} this
34728          * @param {Roo.ContentPanel} panel The activated panel
34729          */
34730         "panelactivated" : true,
34731         /**
34732          * @event resized
34733          * Fires when the user resizes this region. 
34734          * @param {Roo.LayoutRegion} this
34735          * @param {Number} newSize The new size (width for east/west, height for north/south)
34736          */
34737         "resized" : true
34738     };
34739     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34740     this.panels = new Roo.util.MixedCollection();
34741     this.panels.getKey = this.getPanelId.createDelegate(this);
34742     this.box = null;
34743     this.activePanel = null;
34744     // ensure listeners are added...
34745     
34746     if (config.listeners || config.events) {
34747         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34748             listeners : config.listeners || {},
34749             events : config.events || {}
34750         });
34751     }
34752     
34753     if(skipConfig !== true){
34754         this.applyConfig(config);
34755     }
34756 };
34757
34758 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34759 {
34760     getPanelId : function(p){
34761         return p.getId();
34762     },
34763     
34764     applyConfig : function(config){
34765         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34766         this.config = config;
34767         
34768     },
34769     
34770     /**
34771      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34772      * the width, for horizontal (north, south) the height.
34773      * @param {Number} newSize The new width or height
34774      */
34775     resizeTo : function(newSize){
34776         var el = this.el ? this.el :
34777                  (this.activePanel ? this.activePanel.getEl() : null);
34778         if(el){
34779             switch(this.position){
34780                 case "east":
34781                 case "west":
34782                     el.setWidth(newSize);
34783                     this.fireEvent("resized", this, newSize);
34784                 break;
34785                 case "north":
34786                 case "south":
34787                     el.setHeight(newSize);
34788                     this.fireEvent("resized", this, newSize);
34789                 break;                
34790             }
34791         }
34792     },
34793     
34794     getBox : function(){
34795         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34796     },
34797     
34798     getMargins : function(){
34799         return this.margins;
34800     },
34801     
34802     updateBox : function(box){
34803         this.box = box;
34804         var el = this.activePanel.getEl();
34805         el.dom.style.left = box.x + "px";
34806         el.dom.style.top = box.y + "px";
34807         this.activePanel.setSize(box.width, box.height);
34808     },
34809     
34810     /**
34811      * Returns the container element for this region.
34812      * @return {Roo.Element}
34813      */
34814     getEl : function(){
34815         return this.activePanel;
34816     },
34817     
34818     /**
34819      * Returns true if this region is currently visible.
34820      * @return {Boolean}
34821      */
34822     isVisible : function(){
34823         return this.activePanel ? true : false;
34824     },
34825     
34826     setActivePanel : function(panel){
34827         panel = this.getPanel(panel);
34828         if(this.activePanel && this.activePanel != panel){
34829             this.activePanel.setActiveState(false);
34830             this.activePanel.getEl().setLeftTop(-10000,-10000);
34831         }
34832         this.activePanel = panel;
34833         panel.setActiveState(true);
34834         if(this.box){
34835             panel.setSize(this.box.width, this.box.height);
34836         }
34837         this.fireEvent("panelactivated", this, panel);
34838         this.fireEvent("invalidated");
34839     },
34840     
34841     /**
34842      * Show the specified panel.
34843      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34844      * @return {Roo.ContentPanel} The shown panel or null
34845      */
34846     showPanel : function(panel){
34847         panel = this.getPanel(panel);
34848         if(panel){
34849             this.setActivePanel(panel);
34850         }
34851         return panel;
34852     },
34853     
34854     /**
34855      * Get the active panel for this region.
34856      * @return {Roo.ContentPanel} The active panel or null
34857      */
34858     getActivePanel : function(){
34859         return this.activePanel;
34860     },
34861     
34862     /**
34863      * Add the passed ContentPanel(s)
34864      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34865      * @return {Roo.ContentPanel} The panel added (if only one was added)
34866      */
34867     add : function(panel){
34868         if(arguments.length > 1){
34869             for(var i = 0, len = arguments.length; i < len; i++) {
34870                 this.add(arguments[i]);
34871             }
34872             return null;
34873         }
34874         if(this.hasPanel(panel)){
34875             this.showPanel(panel);
34876             return panel;
34877         }
34878         var el = panel.getEl();
34879         if(el.dom.parentNode != this.mgr.el.dom){
34880             this.mgr.el.dom.appendChild(el.dom);
34881         }
34882         if(panel.setRegion){
34883             panel.setRegion(this);
34884         }
34885         this.panels.add(panel);
34886         el.setStyle("position", "absolute");
34887         if(!panel.background){
34888             this.setActivePanel(panel);
34889             if(this.config.initialSize && this.panels.getCount()==1){
34890                 this.resizeTo(this.config.initialSize);
34891             }
34892         }
34893         this.fireEvent("paneladded", this, panel);
34894         return panel;
34895     },
34896     
34897     /**
34898      * Returns true if the panel is in this region.
34899      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34900      * @return {Boolean}
34901      */
34902     hasPanel : function(panel){
34903         if(typeof panel == "object"){ // must be panel obj
34904             panel = panel.getId();
34905         }
34906         return this.getPanel(panel) ? true : false;
34907     },
34908     
34909     /**
34910      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34911      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34912      * @param {Boolean} preservePanel Overrides the config preservePanel option
34913      * @return {Roo.ContentPanel} The panel that was removed
34914      */
34915     remove : function(panel, preservePanel){
34916         panel = this.getPanel(panel);
34917         if(!panel){
34918             return null;
34919         }
34920         var e = {};
34921         this.fireEvent("beforeremove", this, panel, e);
34922         if(e.cancel === true){
34923             return null;
34924         }
34925         var panelId = panel.getId();
34926         this.panels.removeKey(panelId);
34927         return panel;
34928     },
34929     
34930     /**
34931      * Returns the panel specified or null if it's not in this region.
34932      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34933      * @return {Roo.ContentPanel}
34934      */
34935     getPanel : function(id){
34936         if(typeof id == "object"){ // must be panel obj
34937             return id;
34938         }
34939         return this.panels.get(id);
34940     },
34941     
34942     /**
34943      * Returns this regions position (north/south/east/west/center).
34944      * @return {String} 
34945      */
34946     getPosition: function(){
34947         return this.position;    
34948     }
34949 });/*
34950  * Based on:
34951  * Ext JS Library 1.1.1
34952  * Copyright(c) 2006-2007, Ext JS, LLC.
34953  *
34954  * Originally Released Under LGPL - original licence link has changed is not relivant.
34955  *
34956  * Fork - LGPL
34957  * <script type="text/javascript">
34958  */
34959  
34960 /**
34961  * @class Roo.bootstrap.layout.Region
34962  * @extends Roo.bootstrap.layout.Basic
34963  * This class represents a region in a layout manager.
34964  
34965  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34966  * @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})
34967  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34968  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34969  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34970  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34971  * @cfg {String}    title           The title for the region (overrides panel titles)
34972  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34973  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34974  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34975  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34976  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34977  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34978  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34979  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34980  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34981  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34982
34983  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34984  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34985  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34986  * @cfg {Number}    width           For East/West panels
34987  * @cfg {Number}    height          For North/South panels
34988  * @cfg {Boolean}   split           To show the splitter
34989  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34990  * 
34991  * @cfg {string}   cls             Extra CSS classes to add to region
34992  * 
34993  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34994  * @cfg {string}   region  the region that it inhabits..
34995  *
34996
34997  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34998  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34999
35000  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35001  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35002  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35003  */
35004 Roo.bootstrap.layout.Region = function(config)
35005 {
35006     this.applyConfig(config);
35007
35008     var mgr = config.mgr;
35009     var pos = config.region;
35010     config.skipConfig = true;
35011     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35012     
35013     if (mgr.el) {
35014         this.onRender(mgr.el);   
35015     }
35016      
35017     this.visible = true;
35018     this.collapsed = false;
35019     this.unrendered_panels = [];
35020 };
35021
35022 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35023
35024     position: '', // set by wrapper (eg. north/south etc..)
35025     unrendered_panels : null,  // unrendered panels.
35026     createBody : function(){
35027         /** This region's body element 
35028         * @type Roo.Element */
35029         this.bodyEl = this.el.createChild({
35030                 tag: "div",
35031                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35032         });
35033     },
35034
35035     onRender: function(ctr, pos)
35036     {
35037         var dh = Roo.DomHelper;
35038         /** This region's container element 
35039         * @type Roo.Element */
35040         this.el = dh.append(ctr.dom, {
35041                 tag: "div",
35042                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35043             }, true);
35044         /** This region's title element 
35045         * @type Roo.Element */
35046     
35047         this.titleEl = dh.append(this.el.dom,
35048             {
35049                     tag: "div",
35050                     unselectable: "on",
35051                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35052                     children:[
35053                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35054                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35055                     ]}, true);
35056         
35057         this.titleEl.enableDisplayMode();
35058         /** This region's title text element 
35059         * @type HTMLElement */
35060         this.titleTextEl = this.titleEl.dom.firstChild;
35061         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35062         /*
35063         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35064         this.closeBtn.enableDisplayMode();
35065         this.closeBtn.on("click", this.closeClicked, this);
35066         this.closeBtn.hide();
35067     */
35068         this.createBody(this.config);
35069         if(this.config.hideWhenEmpty){
35070             this.hide();
35071             this.on("paneladded", this.validateVisibility, this);
35072             this.on("panelremoved", this.validateVisibility, this);
35073         }
35074         if(this.autoScroll){
35075             this.bodyEl.setStyle("overflow", "auto");
35076         }else{
35077             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35078         }
35079         //if(c.titlebar !== false){
35080             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35081                 this.titleEl.hide();
35082             }else{
35083                 this.titleEl.show();
35084                 if(this.config.title){
35085                     this.titleTextEl.innerHTML = this.config.title;
35086                 }
35087             }
35088         //}
35089         if(this.config.collapsed){
35090             this.collapse(true);
35091         }
35092         if(this.config.hidden){
35093             this.hide();
35094         }
35095         
35096         if (this.unrendered_panels && this.unrendered_panels.length) {
35097             for (var i =0;i< this.unrendered_panels.length; i++) {
35098                 this.add(this.unrendered_panels[i]);
35099             }
35100             this.unrendered_panels = null;
35101             
35102         }
35103         
35104     },
35105     
35106     applyConfig : function(c)
35107     {
35108         /*
35109          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35110             var dh = Roo.DomHelper;
35111             if(c.titlebar !== false){
35112                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35113                 this.collapseBtn.on("click", this.collapse, this);
35114                 this.collapseBtn.enableDisplayMode();
35115                 /*
35116                 if(c.showPin === true || this.showPin){
35117                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35118                     this.stickBtn.enableDisplayMode();
35119                     this.stickBtn.on("click", this.expand, this);
35120                     this.stickBtn.hide();
35121                 }
35122                 
35123             }
35124             */
35125             /** This region's collapsed element
35126             * @type Roo.Element */
35127             /*
35128              *
35129             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35130                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35131             ]}, true);
35132             
35133             if(c.floatable !== false){
35134                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35135                this.collapsedEl.on("click", this.collapseClick, this);
35136             }
35137
35138             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35139                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35140                    id: "message", unselectable: "on", style:{"float":"left"}});
35141                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35142              }
35143             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35144             this.expandBtn.on("click", this.expand, this);
35145             
35146         }
35147         
35148         if(this.collapseBtn){
35149             this.collapseBtn.setVisible(c.collapsible == true);
35150         }
35151         
35152         this.cmargins = c.cmargins || this.cmargins ||
35153                          (this.position == "west" || this.position == "east" ?
35154                              {top: 0, left: 2, right:2, bottom: 0} :
35155                              {top: 2, left: 0, right:0, bottom: 2});
35156         */
35157         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35158         
35159         
35160         this.bottomTabs = c.tabPosition != "top";
35161         
35162         this.autoScroll = c.autoScroll || false;
35163         
35164         
35165        
35166         
35167         this.duration = c.duration || .30;
35168         this.slideDuration = c.slideDuration || .45;
35169         this.config = c;
35170        
35171     },
35172     /**
35173      * Returns true if this region is currently visible.
35174      * @return {Boolean}
35175      */
35176     isVisible : function(){
35177         return this.visible;
35178     },
35179
35180     /**
35181      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35182      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35183      */
35184     //setCollapsedTitle : function(title){
35185     //    title = title || "&#160;";
35186      //   if(this.collapsedTitleTextEl){
35187       //      this.collapsedTitleTextEl.innerHTML = title;
35188        // }
35189     //},
35190
35191     getBox : function(){
35192         var b;
35193       //  if(!this.collapsed){
35194             b = this.el.getBox(false, true);
35195        // }else{
35196           //  b = this.collapsedEl.getBox(false, true);
35197         //}
35198         return b;
35199     },
35200
35201     getMargins : function(){
35202         return this.margins;
35203         //return this.collapsed ? this.cmargins : this.margins;
35204     },
35205 /*
35206     highlight : function(){
35207         this.el.addClass("x-layout-panel-dragover");
35208     },
35209
35210     unhighlight : function(){
35211         this.el.removeClass("x-layout-panel-dragover");
35212     },
35213 */
35214     updateBox : function(box)
35215     {
35216         if (!this.bodyEl) {
35217             return; // not rendered yet..
35218         }
35219         
35220         this.box = box;
35221         if(!this.collapsed){
35222             this.el.dom.style.left = box.x + "px";
35223             this.el.dom.style.top = box.y + "px";
35224             this.updateBody(box.width, box.height);
35225         }else{
35226             this.collapsedEl.dom.style.left = box.x + "px";
35227             this.collapsedEl.dom.style.top = box.y + "px";
35228             this.collapsedEl.setSize(box.width, box.height);
35229         }
35230         if(this.tabs){
35231             this.tabs.autoSizeTabs();
35232         }
35233     },
35234
35235     updateBody : function(w, h)
35236     {
35237         if(w !== null){
35238             this.el.setWidth(w);
35239             w -= this.el.getBorderWidth("rl");
35240             if(this.config.adjustments){
35241                 w += this.config.adjustments[0];
35242             }
35243         }
35244         if(h !== null && h > 0){
35245             this.el.setHeight(h);
35246             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35247             h -= this.el.getBorderWidth("tb");
35248             if(this.config.adjustments){
35249                 h += this.config.adjustments[1];
35250             }
35251             this.bodyEl.setHeight(h);
35252             if(this.tabs){
35253                 h = this.tabs.syncHeight(h);
35254             }
35255         }
35256         if(this.panelSize){
35257             w = w !== null ? w : this.panelSize.width;
35258             h = h !== null ? h : this.panelSize.height;
35259         }
35260         if(this.activePanel){
35261             var el = this.activePanel.getEl();
35262             w = w !== null ? w : el.getWidth();
35263             h = h !== null ? h : el.getHeight();
35264             this.panelSize = {width: w, height: h};
35265             this.activePanel.setSize(w, h);
35266         }
35267         if(Roo.isIE && this.tabs){
35268             this.tabs.el.repaint();
35269         }
35270     },
35271
35272     /**
35273      * Returns the container element for this region.
35274      * @return {Roo.Element}
35275      */
35276     getEl : function(){
35277         return this.el;
35278     },
35279
35280     /**
35281      * Hides this region.
35282      */
35283     hide : function(){
35284         //if(!this.collapsed){
35285             this.el.dom.style.left = "-2000px";
35286             this.el.hide();
35287         //}else{
35288          //   this.collapsedEl.dom.style.left = "-2000px";
35289          //   this.collapsedEl.hide();
35290        // }
35291         this.visible = false;
35292         this.fireEvent("visibilitychange", this, false);
35293     },
35294
35295     /**
35296      * Shows this region if it was previously hidden.
35297      */
35298     show : function(){
35299         //if(!this.collapsed){
35300             this.el.show();
35301         //}else{
35302         //    this.collapsedEl.show();
35303        // }
35304         this.visible = true;
35305         this.fireEvent("visibilitychange", this, true);
35306     },
35307 /*
35308     closeClicked : function(){
35309         if(this.activePanel){
35310             this.remove(this.activePanel);
35311         }
35312     },
35313
35314     collapseClick : function(e){
35315         if(this.isSlid){
35316            e.stopPropagation();
35317            this.slideIn();
35318         }else{
35319            e.stopPropagation();
35320            this.slideOut();
35321         }
35322     },
35323 */
35324     /**
35325      * Collapses this region.
35326      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35327      */
35328     /*
35329     collapse : function(skipAnim, skipCheck = false){
35330         if(this.collapsed) {
35331             return;
35332         }
35333         
35334         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35335             
35336             this.collapsed = true;
35337             if(this.split){
35338                 this.split.el.hide();
35339             }
35340             if(this.config.animate && skipAnim !== true){
35341                 this.fireEvent("invalidated", this);
35342                 this.animateCollapse();
35343             }else{
35344                 this.el.setLocation(-20000,-20000);
35345                 this.el.hide();
35346                 this.collapsedEl.show();
35347                 this.fireEvent("collapsed", this);
35348                 this.fireEvent("invalidated", this);
35349             }
35350         }
35351         
35352     },
35353 */
35354     animateCollapse : function(){
35355         // overridden
35356     },
35357
35358     /**
35359      * Expands this region if it was previously collapsed.
35360      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35361      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35362      */
35363     /*
35364     expand : function(e, skipAnim){
35365         if(e) {
35366             e.stopPropagation();
35367         }
35368         if(!this.collapsed || this.el.hasActiveFx()) {
35369             return;
35370         }
35371         if(this.isSlid){
35372             this.afterSlideIn();
35373             skipAnim = true;
35374         }
35375         this.collapsed = false;
35376         if(this.config.animate && skipAnim !== true){
35377             this.animateExpand();
35378         }else{
35379             this.el.show();
35380             if(this.split){
35381                 this.split.el.show();
35382             }
35383             this.collapsedEl.setLocation(-2000,-2000);
35384             this.collapsedEl.hide();
35385             this.fireEvent("invalidated", this);
35386             this.fireEvent("expanded", this);
35387         }
35388     },
35389 */
35390     animateExpand : function(){
35391         // overridden
35392     },
35393
35394     initTabs : function()
35395     {
35396         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35397         
35398         var ts = new Roo.bootstrap.panel.Tabs({
35399                 el: this.bodyEl.dom,
35400                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35401                 disableTooltips: this.config.disableTabTips,
35402                 toolbar : this.config.toolbar
35403             });
35404         
35405         if(this.config.hideTabs){
35406             ts.stripWrap.setDisplayed(false);
35407         }
35408         this.tabs = ts;
35409         ts.resizeTabs = this.config.resizeTabs === true;
35410         ts.minTabWidth = this.config.minTabWidth || 40;
35411         ts.maxTabWidth = this.config.maxTabWidth || 250;
35412         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35413         ts.monitorResize = false;
35414         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35415         ts.bodyEl.addClass('roo-layout-tabs-body');
35416         this.panels.each(this.initPanelAsTab, this);
35417     },
35418
35419     initPanelAsTab : function(panel){
35420         var ti = this.tabs.addTab(
35421             panel.getEl().id,
35422             panel.getTitle(),
35423             null,
35424             this.config.closeOnTab && panel.isClosable(),
35425             panel.tpl
35426         );
35427         if(panel.tabTip !== undefined){
35428             ti.setTooltip(panel.tabTip);
35429         }
35430         ti.on("activate", function(){
35431               this.setActivePanel(panel);
35432         }, this);
35433         
35434         if(this.config.closeOnTab){
35435             ti.on("beforeclose", function(t, e){
35436                 e.cancel = true;
35437                 this.remove(panel);
35438             }, this);
35439         }
35440         
35441         panel.tabItem = ti;
35442         
35443         return ti;
35444     },
35445
35446     updatePanelTitle : function(panel, title)
35447     {
35448         if(this.activePanel == panel){
35449             this.updateTitle(title);
35450         }
35451         if(this.tabs){
35452             var ti = this.tabs.getTab(panel.getEl().id);
35453             ti.setText(title);
35454             if(panel.tabTip !== undefined){
35455                 ti.setTooltip(panel.tabTip);
35456             }
35457         }
35458     },
35459
35460     updateTitle : function(title){
35461         if(this.titleTextEl && !this.config.title){
35462             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35463         }
35464     },
35465
35466     setActivePanel : function(panel)
35467     {
35468         panel = this.getPanel(panel);
35469         if(this.activePanel && this.activePanel != panel){
35470             if(this.activePanel.setActiveState(false) === false){
35471                 return;
35472             }
35473         }
35474         this.activePanel = panel;
35475         panel.setActiveState(true);
35476         if(this.panelSize){
35477             panel.setSize(this.panelSize.width, this.panelSize.height);
35478         }
35479         if(this.closeBtn){
35480             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35481         }
35482         this.updateTitle(panel.getTitle());
35483         if(this.tabs){
35484             this.fireEvent("invalidated", this);
35485         }
35486         this.fireEvent("panelactivated", this, panel);
35487     },
35488
35489     /**
35490      * Shows the specified panel.
35491      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35492      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35493      */
35494     showPanel : function(panel)
35495     {
35496         panel = this.getPanel(panel);
35497         if(panel){
35498             if(this.tabs){
35499                 var tab = this.tabs.getTab(panel.getEl().id);
35500                 if(tab.isHidden()){
35501                     this.tabs.unhideTab(tab.id);
35502                 }
35503                 tab.activate();
35504             }else{
35505                 this.setActivePanel(panel);
35506             }
35507         }
35508         return panel;
35509     },
35510
35511     /**
35512      * Get the active panel for this region.
35513      * @return {Roo.ContentPanel} The active panel or null
35514      */
35515     getActivePanel : function(){
35516         return this.activePanel;
35517     },
35518
35519     validateVisibility : function(){
35520         if(this.panels.getCount() < 1){
35521             this.updateTitle("&#160;");
35522             this.closeBtn.hide();
35523             this.hide();
35524         }else{
35525             if(!this.isVisible()){
35526                 this.show();
35527             }
35528         }
35529     },
35530
35531     /**
35532      * Adds the passed ContentPanel(s) to this region.
35533      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35534      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35535      */
35536     add : function(panel)
35537     {
35538         if(arguments.length > 1){
35539             for(var i = 0, len = arguments.length; i < len; i++) {
35540                 this.add(arguments[i]);
35541             }
35542             return null;
35543         }
35544         
35545         // if we have not been rendered yet, then we can not really do much of this..
35546         if (!this.bodyEl) {
35547             this.unrendered_panels.push(panel);
35548             return panel;
35549         }
35550         
35551         
35552         
35553         
35554         if(this.hasPanel(panel)){
35555             this.showPanel(panel);
35556             return panel;
35557         }
35558         panel.setRegion(this);
35559         this.panels.add(panel);
35560        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35561             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35562             // and hide them... ???
35563             this.bodyEl.dom.appendChild(panel.getEl().dom);
35564             if(panel.background !== true){
35565                 this.setActivePanel(panel);
35566             }
35567             this.fireEvent("paneladded", this, panel);
35568             return panel;
35569         }
35570         */
35571         if(!this.tabs){
35572             this.initTabs();
35573         }else{
35574             this.initPanelAsTab(panel);
35575         }
35576         
35577         
35578         if(panel.background !== true){
35579             this.tabs.activate(panel.getEl().id);
35580         }
35581         this.fireEvent("paneladded", this, panel);
35582         return panel;
35583     },
35584
35585     /**
35586      * Hides the tab for the specified panel.
35587      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35588      */
35589     hidePanel : function(panel){
35590         if(this.tabs && (panel = this.getPanel(panel))){
35591             this.tabs.hideTab(panel.getEl().id);
35592         }
35593     },
35594
35595     /**
35596      * Unhides the tab for a previously hidden panel.
35597      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35598      */
35599     unhidePanel : function(panel){
35600         if(this.tabs && (panel = this.getPanel(panel))){
35601             this.tabs.unhideTab(panel.getEl().id);
35602         }
35603     },
35604
35605     clearPanels : function(){
35606         while(this.panels.getCount() > 0){
35607              this.remove(this.panels.first());
35608         }
35609     },
35610
35611     /**
35612      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35613      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35614      * @param {Boolean} preservePanel Overrides the config preservePanel option
35615      * @return {Roo.ContentPanel} The panel that was removed
35616      */
35617     remove : function(panel, preservePanel)
35618     {
35619         panel = this.getPanel(panel);
35620         if(!panel){
35621             return null;
35622         }
35623         var e = {};
35624         this.fireEvent("beforeremove", this, panel, e);
35625         if(e.cancel === true){
35626             return null;
35627         }
35628         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35629         var panelId = panel.getId();
35630         this.panels.removeKey(panelId);
35631         if(preservePanel){
35632             document.body.appendChild(panel.getEl().dom);
35633         }
35634         if(this.tabs){
35635             this.tabs.removeTab(panel.getEl().id);
35636         }else if (!preservePanel){
35637             this.bodyEl.dom.removeChild(panel.getEl().dom);
35638         }
35639         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35640             var p = this.panels.first();
35641             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35642             tempEl.appendChild(p.getEl().dom);
35643             this.bodyEl.update("");
35644             this.bodyEl.dom.appendChild(p.getEl().dom);
35645             tempEl = null;
35646             this.updateTitle(p.getTitle());
35647             this.tabs = null;
35648             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35649             this.setActivePanel(p);
35650         }
35651         panel.setRegion(null);
35652         if(this.activePanel == panel){
35653             this.activePanel = null;
35654         }
35655         if(this.config.autoDestroy !== false && preservePanel !== true){
35656             try{panel.destroy();}catch(e){}
35657         }
35658         this.fireEvent("panelremoved", this, panel);
35659         return panel;
35660     },
35661
35662     /**
35663      * Returns the TabPanel component used by this region
35664      * @return {Roo.TabPanel}
35665      */
35666     getTabs : function(){
35667         return this.tabs;
35668     },
35669
35670     createTool : function(parentEl, className){
35671         var btn = Roo.DomHelper.append(parentEl, {
35672             tag: "div",
35673             cls: "x-layout-tools-button",
35674             children: [ {
35675                 tag: "div",
35676                 cls: "roo-layout-tools-button-inner " + className,
35677                 html: "&#160;"
35678             }]
35679         }, true);
35680         btn.addClassOnOver("roo-layout-tools-button-over");
35681         return btn;
35682     }
35683 });/*
35684  * Based on:
35685  * Ext JS Library 1.1.1
35686  * Copyright(c) 2006-2007, Ext JS, LLC.
35687  *
35688  * Originally Released Under LGPL - original licence link has changed is not relivant.
35689  *
35690  * Fork - LGPL
35691  * <script type="text/javascript">
35692  */
35693  
35694
35695
35696 /**
35697  * @class Roo.SplitLayoutRegion
35698  * @extends Roo.LayoutRegion
35699  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35700  */
35701 Roo.bootstrap.layout.Split = function(config){
35702     this.cursor = config.cursor;
35703     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35704 };
35705
35706 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35707 {
35708     splitTip : "Drag to resize.",
35709     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35710     useSplitTips : false,
35711
35712     applyConfig : function(config){
35713         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35714     },
35715     
35716     onRender : function(ctr,pos) {
35717         
35718         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35719         if(!this.config.split){
35720             return;
35721         }
35722         if(!this.split){
35723             
35724             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35725                             tag: "div",
35726                             id: this.el.id + "-split",
35727                             cls: "roo-layout-split roo-layout-split-"+this.position,
35728                             html: "&#160;"
35729             });
35730             /** The SplitBar for this region 
35731             * @type Roo.SplitBar */
35732             // does not exist yet...
35733             Roo.log([this.position, this.orientation]);
35734             
35735             this.split = new Roo.bootstrap.SplitBar({
35736                 dragElement : splitEl,
35737                 resizingElement: this.el,
35738                 orientation : this.orientation
35739             });
35740             
35741             this.split.on("moved", this.onSplitMove, this);
35742             this.split.useShim = this.config.useShim === true;
35743             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35744             if(this.useSplitTips){
35745                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35746             }
35747             //if(config.collapsible){
35748             //    this.split.el.on("dblclick", this.collapse,  this);
35749             //}
35750         }
35751         if(typeof this.config.minSize != "undefined"){
35752             this.split.minSize = this.config.minSize;
35753         }
35754         if(typeof this.config.maxSize != "undefined"){
35755             this.split.maxSize = this.config.maxSize;
35756         }
35757         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35758             this.hideSplitter();
35759         }
35760         
35761     },
35762
35763     getHMaxSize : function(){
35764          var cmax = this.config.maxSize || 10000;
35765          var center = this.mgr.getRegion("center");
35766          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35767     },
35768
35769     getVMaxSize : function(){
35770          var cmax = this.config.maxSize || 10000;
35771          var center = this.mgr.getRegion("center");
35772          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35773     },
35774
35775     onSplitMove : function(split, newSize){
35776         this.fireEvent("resized", this, newSize);
35777     },
35778     
35779     /** 
35780      * Returns the {@link Roo.SplitBar} for this region.
35781      * @return {Roo.SplitBar}
35782      */
35783     getSplitBar : function(){
35784         return this.split;
35785     },
35786     
35787     hide : function(){
35788         this.hideSplitter();
35789         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35790     },
35791
35792     hideSplitter : function(){
35793         if(this.split){
35794             this.split.el.setLocation(-2000,-2000);
35795             this.split.el.hide();
35796         }
35797     },
35798
35799     show : function(){
35800         if(this.split){
35801             this.split.el.show();
35802         }
35803         Roo.bootstrap.layout.Split.superclass.show.call(this);
35804     },
35805     
35806     beforeSlide: function(){
35807         if(Roo.isGecko){// firefox overflow auto bug workaround
35808             this.bodyEl.clip();
35809             if(this.tabs) {
35810                 this.tabs.bodyEl.clip();
35811             }
35812             if(this.activePanel){
35813                 this.activePanel.getEl().clip();
35814                 
35815                 if(this.activePanel.beforeSlide){
35816                     this.activePanel.beforeSlide();
35817                 }
35818             }
35819         }
35820     },
35821     
35822     afterSlide : function(){
35823         if(Roo.isGecko){// firefox overflow auto bug workaround
35824             this.bodyEl.unclip();
35825             if(this.tabs) {
35826                 this.tabs.bodyEl.unclip();
35827             }
35828             if(this.activePanel){
35829                 this.activePanel.getEl().unclip();
35830                 if(this.activePanel.afterSlide){
35831                     this.activePanel.afterSlide();
35832                 }
35833             }
35834         }
35835     },
35836
35837     initAutoHide : function(){
35838         if(this.autoHide !== false){
35839             if(!this.autoHideHd){
35840                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35841                 this.autoHideHd = {
35842                     "mouseout": function(e){
35843                         if(!e.within(this.el, true)){
35844                             st.delay(500);
35845                         }
35846                     },
35847                     "mouseover" : function(e){
35848                         st.cancel();
35849                     },
35850                     scope : this
35851                 };
35852             }
35853             this.el.on(this.autoHideHd);
35854         }
35855     },
35856
35857     clearAutoHide : function(){
35858         if(this.autoHide !== false){
35859             this.el.un("mouseout", this.autoHideHd.mouseout);
35860             this.el.un("mouseover", this.autoHideHd.mouseover);
35861         }
35862     },
35863
35864     clearMonitor : function(){
35865         Roo.get(document).un("click", this.slideInIf, this);
35866     },
35867
35868     // these names are backwards but not changed for compat
35869     slideOut : function(){
35870         if(this.isSlid || this.el.hasActiveFx()){
35871             return;
35872         }
35873         this.isSlid = true;
35874         if(this.collapseBtn){
35875             this.collapseBtn.hide();
35876         }
35877         this.closeBtnState = this.closeBtn.getStyle('display');
35878         this.closeBtn.hide();
35879         if(this.stickBtn){
35880             this.stickBtn.show();
35881         }
35882         this.el.show();
35883         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35884         this.beforeSlide();
35885         this.el.setStyle("z-index", 10001);
35886         this.el.slideIn(this.getSlideAnchor(), {
35887             callback: function(){
35888                 this.afterSlide();
35889                 this.initAutoHide();
35890                 Roo.get(document).on("click", this.slideInIf, this);
35891                 this.fireEvent("slideshow", this);
35892             },
35893             scope: this,
35894             block: true
35895         });
35896     },
35897
35898     afterSlideIn : function(){
35899         this.clearAutoHide();
35900         this.isSlid = false;
35901         this.clearMonitor();
35902         this.el.setStyle("z-index", "");
35903         if(this.collapseBtn){
35904             this.collapseBtn.show();
35905         }
35906         this.closeBtn.setStyle('display', this.closeBtnState);
35907         if(this.stickBtn){
35908             this.stickBtn.hide();
35909         }
35910         this.fireEvent("slidehide", this);
35911     },
35912
35913     slideIn : function(cb){
35914         if(!this.isSlid || this.el.hasActiveFx()){
35915             Roo.callback(cb);
35916             return;
35917         }
35918         this.isSlid = false;
35919         this.beforeSlide();
35920         this.el.slideOut(this.getSlideAnchor(), {
35921             callback: function(){
35922                 this.el.setLeftTop(-10000, -10000);
35923                 this.afterSlide();
35924                 this.afterSlideIn();
35925                 Roo.callback(cb);
35926             },
35927             scope: this,
35928             block: true
35929         });
35930     },
35931     
35932     slideInIf : function(e){
35933         if(!e.within(this.el)){
35934             this.slideIn();
35935         }
35936     },
35937
35938     animateCollapse : function(){
35939         this.beforeSlide();
35940         this.el.setStyle("z-index", 20000);
35941         var anchor = this.getSlideAnchor();
35942         this.el.slideOut(anchor, {
35943             callback : function(){
35944                 this.el.setStyle("z-index", "");
35945                 this.collapsedEl.slideIn(anchor, {duration:.3});
35946                 this.afterSlide();
35947                 this.el.setLocation(-10000,-10000);
35948                 this.el.hide();
35949                 this.fireEvent("collapsed", this);
35950             },
35951             scope: this,
35952             block: true
35953         });
35954     },
35955
35956     animateExpand : function(){
35957         this.beforeSlide();
35958         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35959         this.el.setStyle("z-index", 20000);
35960         this.collapsedEl.hide({
35961             duration:.1
35962         });
35963         this.el.slideIn(this.getSlideAnchor(), {
35964             callback : function(){
35965                 this.el.setStyle("z-index", "");
35966                 this.afterSlide();
35967                 if(this.split){
35968                     this.split.el.show();
35969                 }
35970                 this.fireEvent("invalidated", this);
35971                 this.fireEvent("expanded", this);
35972             },
35973             scope: this,
35974             block: true
35975         });
35976     },
35977
35978     anchors : {
35979         "west" : "left",
35980         "east" : "right",
35981         "north" : "top",
35982         "south" : "bottom"
35983     },
35984
35985     sanchors : {
35986         "west" : "l",
35987         "east" : "r",
35988         "north" : "t",
35989         "south" : "b"
35990     },
35991
35992     canchors : {
35993         "west" : "tl-tr",
35994         "east" : "tr-tl",
35995         "north" : "tl-bl",
35996         "south" : "bl-tl"
35997     },
35998
35999     getAnchor : function(){
36000         return this.anchors[this.position];
36001     },
36002
36003     getCollapseAnchor : function(){
36004         return this.canchors[this.position];
36005     },
36006
36007     getSlideAnchor : function(){
36008         return this.sanchors[this.position];
36009     },
36010
36011     getAlignAdj : function(){
36012         var cm = this.cmargins;
36013         switch(this.position){
36014             case "west":
36015                 return [0, 0];
36016             break;
36017             case "east":
36018                 return [0, 0];
36019             break;
36020             case "north":
36021                 return [0, 0];
36022             break;
36023             case "south":
36024                 return [0, 0];
36025             break;
36026         }
36027     },
36028
36029     getExpandAdj : function(){
36030         var c = this.collapsedEl, cm = this.cmargins;
36031         switch(this.position){
36032             case "west":
36033                 return [-(cm.right+c.getWidth()+cm.left), 0];
36034             break;
36035             case "east":
36036                 return [cm.right+c.getWidth()+cm.left, 0];
36037             break;
36038             case "north":
36039                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36040             break;
36041             case "south":
36042                 return [0, cm.top+cm.bottom+c.getHeight()];
36043             break;
36044         }
36045     }
36046 });/*
36047  * Based on:
36048  * Ext JS Library 1.1.1
36049  * Copyright(c) 2006-2007, Ext JS, LLC.
36050  *
36051  * Originally Released Under LGPL - original licence link has changed is not relivant.
36052  *
36053  * Fork - LGPL
36054  * <script type="text/javascript">
36055  */
36056 /*
36057  * These classes are private internal classes
36058  */
36059 Roo.bootstrap.layout.Center = function(config){
36060     config.region = "center";
36061     Roo.bootstrap.layout.Region.call(this, config);
36062     this.visible = true;
36063     this.minWidth = config.minWidth || 20;
36064     this.minHeight = config.minHeight || 20;
36065 };
36066
36067 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36068     hide : function(){
36069         // center panel can't be hidden
36070     },
36071     
36072     show : function(){
36073         // center panel can't be hidden
36074     },
36075     
36076     getMinWidth: function(){
36077         return this.minWidth;
36078     },
36079     
36080     getMinHeight: function(){
36081         return this.minHeight;
36082     }
36083 });
36084
36085
36086
36087
36088  
36089
36090
36091
36092
36093
36094 Roo.bootstrap.layout.North = function(config)
36095 {
36096     config.region = 'north';
36097     config.cursor = 'n-resize';
36098     
36099     Roo.bootstrap.layout.Split.call(this, config);
36100     
36101     
36102     if(this.split){
36103         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36104         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36105         this.split.el.addClass("roo-layout-split-v");
36106     }
36107     var size = config.initialSize || config.height;
36108     if(typeof size != "undefined"){
36109         this.el.setHeight(size);
36110     }
36111 };
36112 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36113 {
36114     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36115     
36116     
36117     
36118     getBox : function(){
36119         if(this.collapsed){
36120             return this.collapsedEl.getBox();
36121         }
36122         var box = this.el.getBox();
36123         if(this.split){
36124             box.height += this.split.el.getHeight();
36125         }
36126         return box;
36127     },
36128     
36129     updateBox : function(box){
36130         if(this.split && !this.collapsed){
36131             box.height -= this.split.el.getHeight();
36132             this.split.el.setLeft(box.x);
36133             this.split.el.setTop(box.y+box.height);
36134             this.split.el.setWidth(box.width);
36135         }
36136         if(this.collapsed){
36137             this.updateBody(box.width, null);
36138         }
36139         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36140     }
36141 });
36142
36143
36144
36145
36146
36147 Roo.bootstrap.layout.South = function(config){
36148     config.region = 'south';
36149     config.cursor = 's-resize';
36150     Roo.bootstrap.layout.Split.call(this, config);
36151     if(this.split){
36152         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36153         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36154         this.split.el.addClass("roo-layout-split-v");
36155     }
36156     var size = config.initialSize || config.height;
36157     if(typeof size != "undefined"){
36158         this.el.setHeight(size);
36159     }
36160 };
36161
36162 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36163     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36164     getBox : function(){
36165         if(this.collapsed){
36166             return this.collapsedEl.getBox();
36167         }
36168         var box = this.el.getBox();
36169         if(this.split){
36170             var sh = this.split.el.getHeight();
36171             box.height += sh;
36172             box.y -= sh;
36173         }
36174         return box;
36175     },
36176     
36177     updateBox : function(box){
36178         if(this.split && !this.collapsed){
36179             var sh = this.split.el.getHeight();
36180             box.height -= sh;
36181             box.y += sh;
36182             this.split.el.setLeft(box.x);
36183             this.split.el.setTop(box.y-sh);
36184             this.split.el.setWidth(box.width);
36185         }
36186         if(this.collapsed){
36187             this.updateBody(box.width, null);
36188         }
36189         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36190     }
36191 });
36192
36193 Roo.bootstrap.layout.East = function(config){
36194     config.region = "east";
36195     config.cursor = "e-resize";
36196     Roo.bootstrap.layout.Split.call(this, config);
36197     if(this.split){
36198         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36199         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36200         this.split.el.addClass("roo-layout-split-h");
36201     }
36202     var size = config.initialSize || config.width;
36203     if(typeof size != "undefined"){
36204         this.el.setWidth(size);
36205     }
36206 };
36207 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36208     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36209     getBox : function(){
36210         if(this.collapsed){
36211             return this.collapsedEl.getBox();
36212         }
36213         var box = this.el.getBox();
36214         if(this.split){
36215             var sw = this.split.el.getWidth();
36216             box.width += sw;
36217             box.x -= sw;
36218         }
36219         return box;
36220     },
36221
36222     updateBox : function(box){
36223         if(this.split && !this.collapsed){
36224             var sw = this.split.el.getWidth();
36225             box.width -= sw;
36226             this.split.el.setLeft(box.x);
36227             this.split.el.setTop(box.y);
36228             this.split.el.setHeight(box.height);
36229             box.x += sw;
36230         }
36231         if(this.collapsed){
36232             this.updateBody(null, box.height);
36233         }
36234         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36235     }
36236 });
36237
36238 Roo.bootstrap.layout.West = function(config){
36239     config.region = "west";
36240     config.cursor = "w-resize";
36241     
36242     Roo.bootstrap.layout.Split.call(this, config);
36243     if(this.split){
36244         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36245         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36246         this.split.el.addClass("roo-layout-split-h");
36247     }
36248     
36249 };
36250 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36251     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36252     
36253     onRender: function(ctr, pos)
36254     {
36255         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36256         var size = this.config.initialSize || this.config.width;
36257         if(typeof size != "undefined"){
36258             this.el.setWidth(size);
36259         }
36260     },
36261     
36262     getBox : function(){
36263         if(this.collapsed){
36264             return this.collapsedEl.getBox();
36265         }
36266         var box = this.el.getBox();
36267         if(this.split){
36268             box.width += this.split.el.getWidth();
36269         }
36270         return box;
36271     },
36272     
36273     updateBox : function(box){
36274         if(this.split && !this.collapsed){
36275             var sw = this.split.el.getWidth();
36276             box.width -= sw;
36277             this.split.el.setLeft(box.x+box.width);
36278             this.split.el.setTop(box.y);
36279             this.split.el.setHeight(box.height);
36280         }
36281         if(this.collapsed){
36282             this.updateBody(null, box.height);
36283         }
36284         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36285     }
36286 });
36287 Roo.namespace("Roo.bootstrap.panel");/*
36288  * Based on:
36289  * Ext JS Library 1.1.1
36290  * Copyright(c) 2006-2007, Ext JS, LLC.
36291  *
36292  * Originally Released Under LGPL - original licence link has changed is not relivant.
36293  *
36294  * Fork - LGPL
36295  * <script type="text/javascript">
36296  */
36297 /**
36298  * @class Roo.ContentPanel
36299  * @extends Roo.util.Observable
36300  * A basic ContentPanel element.
36301  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36302  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36303  * @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
36304  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36305  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36306  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36307  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36308  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36309  * @cfg {String} title          The title for this panel
36310  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36311  * @cfg {String} url            Calls {@link #setUrl} with this value
36312  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36313  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36314  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36315  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36316  * @cfg {Boolean} badges render the badges
36317
36318  * @constructor
36319  * Create a new ContentPanel.
36320  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36321  * @param {String/Object} config A string to set only the title or a config object
36322  * @param {String} content (optional) Set the HTML content for this panel
36323  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36324  */
36325 Roo.bootstrap.panel.Content = function( config){
36326     
36327     this.tpl = config.tpl || false;
36328     
36329     var el = config.el;
36330     var content = config.content;
36331
36332     if(config.autoCreate){ // xtype is available if this is called from factory
36333         el = Roo.id();
36334     }
36335     this.el = Roo.get(el);
36336     if(!this.el && config && config.autoCreate){
36337         if(typeof config.autoCreate == "object"){
36338             if(!config.autoCreate.id){
36339                 config.autoCreate.id = config.id||el;
36340             }
36341             this.el = Roo.DomHelper.append(document.body,
36342                         config.autoCreate, true);
36343         }else{
36344             var elcfg =  {   tag: "div",
36345                             cls: "roo-layout-inactive-content",
36346                             id: config.id||el
36347                             };
36348             if (config.html) {
36349                 elcfg.html = config.html;
36350                 
36351             }
36352                         
36353             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36354         }
36355     } 
36356     this.closable = false;
36357     this.loaded = false;
36358     this.active = false;
36359    
36360       
36361     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36362         
36363         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36364         
36365         this.wrapEl = this.el; //this.el.wrap();
36366         var ti = [];
36367         if (config.toolbar.items) {
36368             ti = config.toolbar.items ;
36369             delete config.toolbar.items ;
36370         }
36371         
36372         var nitems = [];
36373         this.toolbar.render(this.wrapEl, 'before');
36374         for(var i =0;i < ti.length;i++) {
36375           //  Roo.log(['add child', items[i]]);
36376             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36377         }
36378         this.toolbar.items = nitems;
36379         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36380         delete config.toolbar;
36381         
36382     }
36383     /*
36384     // xtype created footer. - not sure if will work as we normally have to render first..
36385     if (this.footer && !this.footer.el && this.footer.xtype) {
36386         if (!this.wrapEl) {
36387             this.wrapEl = this.el.wrap();
36388         }
36389     
36390         this.footer.container = this.wrapEl.createChild();
36391          
36392         this.footer = Roo.factory(this.footer, Roo);
36393         
36394     }
36395     */
36396     
36397      if(typeof config == "string"){
36398         this.title = config;
36399     }else{
36400         Roo.apply(this, config);
36401     }
36402     
36403     if(this.resizeEl){
36404         this.resizeEl = Roo.get(this.resizeEl, true);
36405     }else{
36406         this.resizeEl = this.el;
36407     }
36408     // handle view.xtype
36409     
36410  
36411     
36412     
36413     this.addEvents({
36414         /**
36415          * @event activate
36416          * Fires when this panel is activated. 
36417          * @param {Roo.ContentPanel} this
36418          */
36419         "activate" : true,
36420         /**
36421          * @event deactivate
36422          * Fires when this panel is activated. 
36423          * @param {Roo.ContentPanel} this
36424          */
36425         "deactivate" : true,
36426
36427         /**
36428          * @event resize
36429          * Fires when this panel is resized if fitToFrame is true.
36430          * @param {Roo.ContentPanel} this
36431          * @param {Number} width The width after any component adjustments
36432          * @param {Number} height The height after any component adjustments
36433          */
36434         "resize" : true,
36435         
36436          /**
36437          * @event render
36438          * Fires when this tab is created
36439          * @param {Roo.ContentPanel} this
36440          */
36441         "render" : true
36442         
36443         
36444         
36445     });
36446     
36447
36448     
36449     
36450     if(this.autoScroll){
36451         this.resizeEl.setStyle("overflow", "auto");
36452     } else {
36453         // fix randome scrolling
36454         //this.el.on('scroll', function() {
36455         //    Roo.log('fix random scolling');
36456         //    this.scrollTo('top',0); 
36457         //});
36458     }
36459     content = content || this.content;
36460     if(content){
36461         this.setContent(content);
36462     }
36463     if(config && config.url){
36464         this.setUrl(this.url, this.params, this.loadOnce);
36465     }
36466     
36467     
36468     
36469     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36470     
36471     if (this.view && typeof(this.view.xtype) != 'undefined') {
36472         this.view.el = this.el.appendChild(document.createElement("div"));
36473         this.view = Roo.factory(this.view); 
36474         this.view.render  &&  this.view.render(false, '');  
36475     }
36476     
36477     
36478     this.fireEvent('render', this);
36479 };
36480
36481 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36482     
36483     tabTip : '',
36484     
36485     setRegion : function(region){
36486         this.region = region;
36487         this.setActiveClass(region && !this.background);
36488     },
36489     
36490     
36491     setActiveClass: function(state)
36492     {
36493         if(state){
36494            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36495            this.el.setStyle('position','relative');
36496         }else{
36497            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36498            this.el.setStyle('position', 'absolute');
36499         } 
36500     },
36501     
36502     /**
36503      * Returns the toolbar for this Panel if one was configured. 
36504      * @return {Roo.Toolbar} 
36505      */
36506     getToolbar : function(){
36507         return this.toolbar;
36508     },
36509     
36510     setActiveState : function(active)
36511     {
36512         this.active = active;
36513         this.setActiveClass(active);
36514         if(!active){
36515             if(this.fireEvent("deactivate", this) === false){
36516                 return false;
36517             }
36518             return true;
36519         }
36520         this.fireEvent("activate", this);
36521         return true;
36522     },
36523     /**
36524      * Updates this panel's element
36525      * @param {String} content The new content
36526      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36527     */
36528     setContent : function(content, loadScripts){
36529         this.el.update(content, loadScripts);
36530     },
36531
36532     ignoreResize : function(w, h){
36533         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36534             return true;
36535         }else{
36536             this.lastSize = {width: w, height: h};
36537             return false;
36538         }
36539     },
36540     /**
36541      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36542      * @return {Roo.UpdateManager} The UpdateManager
36543      */
36544     getUpdateManager : function(){
36545         return this.el.getUpdateManager();
36546     },
36547      /**
36548      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36549      * @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:
36550 <pre><code>
36551 panel.load({
36552     url: "your-url.php",
36553     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36554     callback: yourFunction,
36555     scope: yourObject, //(optional scope)
36556     discardUrl: false,
36557     nocache: false,
36558     text: "Loading...",
36559     timeout: 30,
36560     scripts: false
36561 });
36562 </code></pre>
36563      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36564      * 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.
36565      * @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}
36566      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36567      * @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.
36568      * @return {Roo.ContentPanel} this
36569      */
36570     load : function(){
36571         var um = this.el.getUpdateManager();
36572         um.update.apply(um, arguments);
36573         return this;
36574     },
36575
36576
36577     /**
36578      * 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.
36579      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36580      * @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)
36581      * @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)
36582      * @return {Roo.UpdateManager} The UpdateManager
36583      */
36584     setUrl : function(url, params, loadOnce){
36585         if(this.refreshDelegate){
36586             this.removeListener("activate", this.refreshDelegate);
36587         }
36588         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36589         this.on("activate", this.refreshDelegate);
36590         return this.el.getUpdateManager();
36591     },
36592     
36593     _handleRefresh : function(url, params, loadOnce){
36594         if(!loadOnce || !this.loaded){
36595             var updater = this.el.getUpdateManager();
36596             updater.update(url, params, this._setLoaded.createDelegate(this));
36597         }
36598     },
36599     
36600     _setLoaded : function(){
36601         this.loaded = true;
36602     }, 
36603     
36604     /**
36605      * Returns this panel's id
36606      * @return {String} 
36607      */
36608     getId : function(){
36609         return this.el.id;
36610     },
36611     
36612     /** 
36613      * Returns this panel's element - used by regiosn to add.
36614      * @return {Roo.Element} 
36615      */
36616     getEl : function(){
36617         return this.wrapEl || this.el;
36618     },
36619     
36620    
36621     
36622     adjustForComponents : function(width, height)
36623     {
36624         //Roo.log('adjustForComponents ');
36625         if(this.resizeEl != this.el){
36626             width -= this.el.getFrameWidth('lr');
36627             height -= this.el.getFrameWidth('tb');
36628         }
36629         if(this.toolbar){
36630             var te = this.toolbar.getEl();
36631             te.setWidth(width);
36632             height -= te.getHeight();
36633         }
36634         if(this.footer){
36635             var te = this.footer.getEl();
36636             te.setWidth(width);
36637             height -= te.getHeight();
36638         }
36639         
36640         
36641         if(this.adjustments){
36642             width += this.adjustments[0];
36643             height += this.adjustments[1];
36644         }
36645         return {"width": width, "height": height};
36646     },
36647     
36648     setSize : function(width, height){
36649         if(this.fitToFrame && !this.ignoreResize(width, height)){
36650             if(this.fitContainer && this.resizeEl != this.el){
36651                 this.el.setSize(width, height);
36652             }
36653             var size = this.adjustForComponents(width, height);
36654             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36655             this.fireEvent('resize', this, size.width, size.height);
36656         }
36657     },
36658     
36659     /**
36660      * Returns this panel's title
36661      * @return {String} 
36662      */
36663     getTitle : function(){
36664         
36665         if (typeof(this.title) != 'object') {
36666             return this.title;
36667         }
36668         
36669         var t = '';
36670         for (var k in this.title) {
36671             if (!this.title.hasOwnProperty(k)) {
36672                 continue;
36673             }
36674             
36675             if (k.indexOf('-') >= 0) {
36676                 var s = k.split('-');
36677                 for (var i = 0; i<s.length; i++) {
36678                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36679                 }
36680             } else {
36681                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36682             }
36683         }
36684         return t;
36685     },
36686     
36687     /**
36688      * Set this panel's title
36689      * @param {String} title
36690      */
36691     setTitle : function(title){
36692         this.title = title;
36693         if(this.region){
36694             this.region.updatePanelTitle(this, title);
36695         }
36696     },
36697     
36698     /**
36699      * Returns true is this panel was configured to be closable
36700      * @return {Boolean} 
36701      */
36702     isClosable : function(){
36703         return this.closable;
36704     },
36705     
36706     beforeSlide : function(){
36707         this.el.clip();
36708         this.resizeEl.clip();
36709     },
36710     
36711     afterSlide : function(){
36712         this.el.unclip();
36713         this.resizeEl.unclip();
36714     },
36715     
36716     /**
36717      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36718      *   Will fail silently if the {@link #setUrl} method has not been called.
36719      *   This does not activate the panel, just updates its content.
36720      */
36721     refresh : function(){
36722         if(this.refreshDelegate){
36723            this.loaded = false;
36724            this.refreshDelegate();
36725         }
36726     },
36727     
36728     /**
36729      * Destroys this panel
36730      */
36731     destroy : function(){
36732         this.el.removeAllListeners();
36733         var tempEl = document.createElement("span");
36734         tempEl.appendChild(this.el.dom);
36735         tempEl.innerHTML = "";
36736         this.el.remove();
36737         this.el = null;
36738     },
36739     
36740     /**
36741      * form - if the content panel contains a form - this is a reference to it.
36742      * @type {Roo.form.Form}
36743      */
36744     form : false,
36745     /**
36746      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36747      *    This contains a reference to it.
36748      * @type {Roo.View}
36749      */
36750     view : false,
36751     
36752       /**
36753      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36754      * <pre><code>
36755
36756 layout.addxtype({
36757        xtype : 'Form',
36758        items: [ .... ]
36759    }
36760 );
36761
36762 </code></pre>
36763      * @param {Object} cfg Xtype definition of item to add.
36764      */
36765     
36766     
36767     getChildContainer: function () {
36768         return this.getEl();
36769     }
36770     
36771     
36772     /*
36773         var  ret = new Roo.factory(cfg);
36774         return ret;
36775         
36776         
36777         // add form..
36778         if (cfg.xtype.match(/^Form$/)) {
36779             
36780             var el;
36781             //if (this.footer) {
36782             //    el = this.footer.container.insertSibling(false, 'before');
36783             //} else {
36784                 el = this.el.createChild();
36785             //}
36786
36787             this.form = new  Roo.form.Form(cfg);
36788             
36789             
36790             if ( this.form.allItems.length) {
36791                 this.form.render(el.dom);
36792             }
36793             return this.form;
36794         }
36795         // should only have one of theses..
36796         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36797             // views.. should not be just added - used named prop 'view''
36798             
36799             cfg.el = this.el.appendChild(document.createElement("div"));
36800             // factory?
36801             
36802             var ret = new Roo.factory(cfg);
36803              
36804              ret.render && ret.render(false, ''); // render blank..
36805             this.view = ret;
36806             return ret;
36807         }
36808         return false;
36809     }
36810     \*/
36811 });
36812  
36813 /**
36814  * @class Roo.bootstrap.panel.Grid
36815  * @extends Roo.bootstrap.panel.Content
36816  * @constructor
36817  * Create a new GridPanel.
36818  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36819  * @param {Object} config A the config object
36820   
36821  */
36822
36823
36824
36825 Roo.bootstrap.panel.Grid = function(config)
36826 {
36827     
36828       
36829     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36830         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36831
36832     config.el = this.wrapper;
36833     //this.el = this.wrapper;
36834     
36835       if (config.container) {
36836         // ctor'ed from a Border/panel.grid
36837         
36838         
36839         this.wrapper.setStyle("overflow", "hidden");
36840         this.wrapper.addClass('roo-grid-container');
36841
36842     }
36843     
36844     
36845     if(config.toolbar){
36846         var tool_el = this.wrapper.createChild();    
36847         this.toolbar = Roo.factory(config.toolbar);
36848         var ti = [];
36849         if (config.toolbar.items) {
36850             ti = config.toolbar.items ;
36851             delete config.toolbar.items ;
36852         }
36853         
36854         var nitems = [];
36855         this.toolbar.render(tool_el);
36856         for(var i =0;i < ti.length;i++) {
36857           //  Roo.log(['add child', items[i]]);
36858             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36859         }
36860         this.toolbar.items = nitems;
36861         
36862         delete config.toolbar;
36863     }
36864     
36865     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36866     config.grid.scrollBody = true;;
36867     config.grid.monitorWindowResize = false; // turn off autosizing
36868     config.grid.autoHeight = false;
36869     config.grid.autoWidth = false;
36870     
36871     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36872     
36873     if (config.background) {
36874         // render grid on panel activation (if panel background)
36875         this.on('activate', function(gp) {
36876             if (!gp.grid.rendered) {
36877                 gp.grid.render(this.wrapper);
36878                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36879             }
36880         });
36881             
36882     } else {
36883         this.grid.render(this.wrapper);
36884         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36885
36886     }
36887     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36888     // ??? needed ??? config.el = this.wrapper;
36889     
36890     
36891     
36892   
36893     // xtype created footer. - not sure if will work as we normally have to render first..
36894     if (this.footer && !this.footer.el && this.footer.xtype) {
36895         
36896         var ctr = this.grid.getView().getFooterPanel(true);
36897         this.footer.dataSource = this.grid.dataSource;
36898         this.footer = Roo.factory(this.footer, Roo);
36899         this.footer.render(ctr);
36900         
36901     }
36902     
36903     
36904     
36905     
36906      
36907 };
36908
36909 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36910     getId : function(){
36911         return this.grid.id;
36912     },
36913     
36914     /**
36915      * Returns the grid for this panel
36916      * @return {Roo.bootstrap.Table} 
36917      */
36918     getGrid : function(){
36919         return this.grid;    
36920     },
36921     
36922     setSize : function(width, height){
36923         if(!this.ignoreResize(width, height)){
36924             var grid = this.grid;
36925             var size = this.adjustForComponents(width, height);
36926             var gridel = grid.getGridEl();
36927             gridel.setSize(size.width, size.height);
36928             /*
36929             var thd = grid.getGridEl().select('thead',true).first();
36930             var tbd = grid.getGridEl().select('tbody', true).first();
36931             if (tbd) {
36932                 tbd.setSize(width, height - thd.getHeight());
36933             }
36934             */
36935             grid.autoSize();
36936         }
36937     },
36938      
36939     
36940     
36941     beforeSlide : function(){
36942         this.grid.getView().scroller.clip();
36943     },
36944     
36945     afterSlide : function(){
36946         this.grid.getView().scroller.unclip();
36947     },
36948     
36949     destroy : function(){
36950         this.grid.destroy();
36951         delete this.grid;
36952         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36953     }
36954 });
36955
36956 /**
36957  * @class Roo.bootstrap.panel.Nest
36958  * @extends Roo.bootstrap.panel.Content
36959  * @constructor
36960  * Create a new Panel, that can contain a layout.Border.
36961  * 
36962  * 
36963  * @param {Roo.BorderLayout} layout The layout for this panel
36964  * @param {String/Object} config A string to set only the title or a config object
36965  */
36966 Roo.bootstrap.panel.Nest = function(config)
36967 {
36968     // construct with only one argument..
36969     /* FIXME - implement nicer consturctors
36970     if (layout.layout) {
36971         config = layout;
36972         layout = config.layout;
36973         delete config.layout;
36974     }
36975     if (layout.xtype && !layout.getEl) {
36976         // then layout needs constructing..
36977         layout = Roo.factory(layout, Roo);
36978     }
36979     */
36980     
36981     config.el =  config.layout.getEl();
36982     
36983     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36984     
36985     config.layout.monitorWindowResize = false; // turn off autosizing
36986     this.layout = config.layout;
36987     this.layout.getEl().addClass("roo-layout-nested-layout");
36988     
36989     
36990     
36991     
36992 };
36993
36994 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36995
36996     setSize : function(width, height){
36997         if(!this.ignoreResize(width, height)){
36998             var size = this.adjustForComponents(width, height);
36999             var el = this.layout.getEl();
37000             if (size.height < 1) {
37001                 el.setWidth(size.width);   
37002             } else {
37003                 el.setSize(size.width, size.height);
37004             }
37005             var touch = el.dom.offsetWidth;
37006             this.layout.layout();
37007             // ie requires a double layout on the first pass
37008             if(Roo.isIE && !this.initialized){
37009                 this.initialized = true;
37010                 this.layout.layout();
37011             }
37012         }
37013     },
37014     
37015     // activate all subpanels if not currently active..
37016     
37017     setActiveState : function(active){
37018         this.active = active;
37019         this.setActiveClass(active);
37020         
37021         if(!active){
37022             this.fireEvent("deactivate", this);
37023             return;
37024         }
37025         
37026         this.fireEvent("activate", this);
37027         // not sure if this should happen before or after..
37028         if (!this.layout) {
37029             return; // should not happen..
37030         }
37031         var reg = false;
37032         for (var r in this.layout.regions) {
37033             reg = this.layout.getRegion(r);
37034             if (reg.getActivePanel()) {
37035                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37036                 reg.setActivePanel(reg.getActivePanel());
37037                 continue;
37038             }
37039             if (!reg.panels.length) {
37040                 continue;
37041             }
37042             reg.showPanel(reg.getPanel(0));
37043         }
37044         
37045         
37046         
37047         
37048     },
37049     
37050     /**
37051      * Returns the nested BorderLayout for this panel
37052      * @return {Roo.BorderLayout} 
37053      */
37054     getLayout : function(){
37055         return this.layout;
37056     },
37057     
37058      /**
37059      * Adds a xtype elements to the layout of the nested panel
37060      * <pre><code>
37061
37062 panel.addxtype({
37063        xtype : 'ContentPanel',
37064        region: 'west',
37065        items: [ .... ]
37066    }
37067 );
37068
37069 panel.addxtype({
37070         xtype : 'NestedLayoutPanel',
37071         region: 'west',
37072         layout: {
37073            center: { },
37074            west: { }   
37075         },
37076         items : [ ... list of content panels or nested layout panels.. ]
37077    }
37078 );
37079 </code></pre>
37080      * @param {Object} cfg Xtype definition of item to add.
37081      */
37082     addxtype : function(cfg) {
37083         return this.layout.addxtype(cfg);
37084     
37085     }
37086 });        /*
37087  * Based on:
37088  * Ext JS Library 1.1.1
37089  * Copyright(c) 2006-2007, Ext JS, LLC.
37090  *
37091  * Originally Released Under LGPL - original licence link has changed is not relivant.
37092  *
37093  * Fork - LGPL
37094  * <script type="text/javascript">
37095  */
37096 /**
37097  * @class Roo.TabPanel
37098  * @extends Roo.util.Observable
37099  * A lightweight tab container.
37100  * <br><br>
37101  * Usage:
37102  * <pre><code>
37103 // basic tabs 1, built from existing content
37104 var tabs = new Roo.TabPanel("tabs1");
37105 tabs.addTab("script", "View Script");
37106 tabs.addTab("markup", "View Markup");
37107 tabs.activate("script");
37108
37109 // more advanced tabs, built from javascript
37110 var jtabs = new Roo.TabPanel("jtabs");
37111 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37112
37113 // set up the UpdateManager
37114 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37115 var updater = tab2.getUpdateManager();
37116 updater.setDefaultUrl("ajax1.htm");
37117 tab2.on('activate', updater.refresh, updater, true);
37118
37119 // Use setUrl for Ajax loading
37120 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37121 tab3.setUrl("ajax2.htm", null, true);
37122
37123 // Disabled tab
37124 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37125 tab4.disable();
37126
37127 jtabs.activate("jtabs-1");
37128  * </code></pre>
37129  * @constructor
37130  * Create a new TabPanel.
37131  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37132  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37133  */
37134 Roo.bootstrap.panel.Tabs = function(config){
37135     /**
37136     * The container element for this TabPanel.
37137     * @type Roo.Element
37138     */
37139     this.el = Roo.get(config.el);
37140     delete config.el;
37141     if(config){
37142         if(typeof config == "boolean"){
37143             this.tabPosition = config ? "bottom" : "top";
37144         }else{
37145             Roo.apply(this, config);
37146         }
37147     }
37148     
37149     if(this.tabPosition == "bottom"){
37150         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37151         this.el.addClass("roo-tabs-bottom");
37152     }
37153     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37154     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37155     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37156     if(Roo.isIE){
37157         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37158     }
37159     if(this.tabPosition != "bottom"){
37160         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37161          * @type Roo.Element
37162          */
37163         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37164         this.el.addClass("roo-tabs-top");
37165     }
37166     this.items = [];
37167
37168     this.bodyEl.setStyle("position", "relative");
37169
37170     this.active = null;
37171     this.activateDelegate = this.activate.createDelegate(this);
37172
37173     this.addEvents({
37174         /**
37175          * @event tabchange
37176          * Fires when the active tab changes
37177          * @param {Roo.TabPanel} this
37178          * @param {Roo.TabPanelItem} activePanel The new active tab
37179          */
37180         "tabchange": true,
37181         /**
37182          * @event beforetabchange
37183          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37184          * @param {Roo.TabPanel} this
37185          * @param {Object} e Set cancel to true on this object to cancel the tab change
37186          * @param {Roo.TabPanelItem} tab The tab being changed to
37187          */
37188         "beforetabchange" : true
37189     });
37190
37191     Roo.EventManager.onWindowResize(this.onResize, this);
37192     this.cpad = this.el.getPadding("lr");
37193     this.hiddenCount = 0;
37194
37195
37196     // toolbar on the tabbar support...
37197     if (this.toolbar) {
37198         alert("no toolbar support yet");
37199         this.toolbar  = false;
37200         /*
37201         var tcfg = this.toolbar;
37202         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37203         this.toolbar = new Roo.Toolbar(tcfg);
37204         if (Roo.isSafari) {
37205             var tbl = tcfg.container.child('table', true);
37206             tbl.setAttribute('width', '100%');
37207         }
37208         */
37209         
37210     }
37211    
37212
37213
37214     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37215 };
37216
37217 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37218     /*
37219      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37220      */
37221     tabPosition : "top",
37222     /*
37223      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37224      */
37225     currentTabWidth : 0,
37226     /*
37227      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37228      */
37229     minTabWidth : 40,
37230     /*
37231      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37232      */
37233     maxTabWidth : 250,
37234     /*
37235      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37236      */
37237     preferredTabWidth : 175,
37238     /*
37239      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37240      */
37241     resizeTabs : false,
37242     /*
37243      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37244      */
37245     monitorResize : true,
37246     /*
37247      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37248      */
37249     toolbar : false,
37250
37251     /**
37252      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37253      * @param {String} id The id of the div to use <b>or create</b>
37254      * @param {String} text The text for the tab
37255      * @param {String} content (optional) Content to put in the TabPanelItem body
37256      * @param {Boolean} closable (optional) True to create a close icon on the tab
37257      * @return {Roo.TabPanelItem} The created TabPanelItem
37258      */
37259     addTab : function(id, text, content, closable, tpl)
37260     {
37261         var item = new Roo.bootstrap.panel.TabItem({
37262             panel: this,
37263             id : id,
37264             text : text,
37265             closable : closable,
37266             tpl : tpl
37267         });
37268         this.addTabItem(item);
37269         if(content){
37270             item.setContent(content);
37271         }
37272         return item;
37273     },
37274
37275     /**
37276      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37277      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37278      * @return {Roo.TabPanelItem}
37279      */
37280     getTab : function(id){
37281         return this.items[id];
37282     },
37283
37284     /**
37285      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37286      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37287      */
37288     hideTab : function(id){
37289         var t = this.items[id];
37290         if(!t.isHidden()){
37291            t.setHidden(true);
37292            this.hiddenCount++;
37293            this.autoSizeTabs();
37294         }
37295     },
37296
37297     /**
37298      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37299      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37300      */
37301     unhideTab : function(id){
37302         var t = this.items[id];
37303         if(t.isHidden()){
37304            t.setHidden(false);
37305            this.hiddenCount--;
37306            this.autoSizeTabs();
37307         }
37308     },
37309
37310     /**
37311      * Adds an existing {@link Roo.TabPanelItem}.
37312      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37313      */
37314     addTabItem : function(item){
37315         this.items[item.id] = item;
37316         this.items.push(item);
37317       //  if(this.resizeTabs){
37318     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37319   //         this.autoSizeTabs();
37320 //        }else{
37321 //            item.autoSize();
37322        // }
37323     },
37324
37325     /**
37326      * Removes a {@link Roo.TabPanelItem}.
37327      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37328      */
37329     removeTab : function(id){
37330         var items = this.items;
37331         var tab = items[id];
37332         if(!tab) { return; }
37333         var index = items.indexOf(tab);
37334         if(this.active == tab && items.length > 1){
37335             var newTab = this.getNextAvailable(index);
37336             if(newTab) {
37337                 newTab.activate();
37338             }
37339         }
37340         this.stripEl.dom.removeChild(tab.pnode.dom);
37341         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37342             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37343         }
37344         items.splice(index, 1);
37345         delete this.items[tab.id];
37346         tab.fireEvent("close", tab);
37347         tab.purgeListeners();
37348         this.autoSizeTabs();
37349     },
37350
37351     getNextAvailable : function(start){
37352         var items = this.items;
37353         var index = start;
37354         // look for a next tab that will slide over to
37355         // replace the one being removed
37356         while(index < items.length){
37357             var item = items[++index];
37358             if(item && !item.isHidden()){
37359                 return item;
37360             }
37361         }
37362         // if one isn't found select the previous tab (on the left)
37363         index = start;
37364         while(index >= 0){
37365             var item = items[--index];
37366             if(item && !item.isHidden()){
37367                 return item;
37368             }
37369         }
37370         return null;
37371     },
37372
37373     /**
37374      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37375      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37376      */
37377     disableTab : function(id){
37378         var tab = this.items[id];
37379         if(tab && this.active != tab){
37380             tab.disable();
37381         }
37382     },
37383
37384     /**
37385      * Enables a {@link Roo.TabPanelItem} that is disabled.
37386      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37387      */
37388     enableTab : function(id){
37389         var tab = this.items[id];
37390         tab.enable();
37391     },
37392
37393     /**
37394      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37395      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37396      * @return {Roo.TabPanelItem} The TabPanelItem.
37397      */
37398     activate : function(id){
37399         var tab = this.items[id];
37400         if(!tab){
37401             return null;
37402         }
37403         if(tab == this.active || tab.disabled){
37404             return tab;
37405         }
37406         var e = {};
37407         this.fireEvent("beforetabchange", this, e, tab);
37408         if(e.cancel !== true && !tab.disabled){
37409             if(this.active){
37410                 this.active.hide();
37411             }
37412             this.active = this.items[id];
37413             this.active.show();
37414             this.fireEvent("tabchange", this, this.active);
37415         }
37416         return tab;
37417     },
37418
37419     /**
37420      * Gets the active {@link Roo.TabPanelItem}.
37421      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37422      */
37423     getActiveTab : function(){
37424         return this.active;
37425     },
37426
37427     /**
37428      * Updates the tab body element to fit the height of the container element
37429      * for overflow scrolling
37430      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37431      */
37432     syncHeight : function(targetHeight){
37433         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37434         var bm = this.bodyEl.getMargins();
37435         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37436         this.bodyEl.setHeight(newHeight);
37437         return newHeight;
37438     },
37439
37440     onResize : function(){
37441         if(this.monitorResize){
37442             this.autoSizeTabs();
37443         }
37444     },
37445
37446     /**
37447      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37448      */
37449     beginUpdate : function(){
37450         this.updating = true;
37451     },
37452
37453     /**
37454      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37455      */
37456     endUpdate : function(){
37457         this.updating = false;
37458         this.autoSizeTabs();
37459     },
37460
37461     /**
37462      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37463      */
37464     autoSizeTabs : function(){
37465         var count = this.items.length;
37466         var vcount = count - this.hiddenCount;
37467         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37468             return;
37469         }
37470         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37471         var availWidth = Math.floor(w / vcount);
37472         var b = this.stripBody;
37473         if(b.getWidth() > w){
37474             var tabs = this.items;
37475             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37476             if(availWidth < this.minTabWidth){
37477                 /*if(!this.sleft){    // incomplete scrolling code
37478                     this.createScrollButtons();
37479                 }
37480                 this.showScroll();
37481                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37482             }
37483         }else{
37484             if(this.currentTabWidth < this.preferredTabWidth){
37485                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37486             }
37487         }
37488     },
37489
37490     /**
37491      * Returns the number of tabs in this TabPanel.
37492      * @return {Number}
37493      */
37494      getCount : function(){
37495          return this.items.length;
37496      },
37497
37498     /**
37499      * Resizes all the tabs to the passed width
37500      * @param {Number} The new width
37501      */
37502     setTabWidth : function(width){
37503         this.currentTabWidth = width;
37504         for(var i = 0, len = this.items.length; i < len; i++) {
37505                 if(!this.items[i].isHidden()) {
37506                 this.items[i].setWidth(width);
37507             }
37508         }
37509     },
37510
37511     /**
37512      * Destroys this TabPanel
37513      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37514      */
37515     destroy : function(removeEl){
37516         Roo.EventManager.removeResizeListener(this.onResize, this);
37517         for(var i = 0, len = this.items.length; i < len; i++){
37518             this.items[i].purgeListeners();
37519         }
37520         if(removeEl === true){
37521             this.el.update("");
37522             this.el.remove();
37523         }
37524     },
37525     
37526     createStrip : function(container)
37527     {
37528         var strip = document.createElement("nav");
37529         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37530         container.appendChild(strip);
37531         return strip;
37532     },
37533     
37534     createStripList : function(strip)
37535     {
37536         // div wrapper for retard IE
37537         // returns the "tr" element.
37538         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37539         //'<div class="x-tabs-strip-wrap">'+
37540           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37541           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37542         return strip.firstChild; //.firstChild.firstChild.firstChild;
37543     },
37544     createBody : function(container)
37545     {
37546         var body = document.createElement("div");
37547         Roo.id(body, "tab-body");
37548         //Roo.fly(body).addClass("x-tabs-body");
37549         Roo.fly(body).addClass("tab-content");
37550         container.appendChild(body);
37551         return body;
37552     },
37553     createItemBody :function(bodyEl, id){
37554         var body = Roo.getDom(id);
37555         if(!body){
37556             body = document.createElement("div");
37557             body.id = id;
37558         }
37559         //Roo.fly(body).addClass("x-tabs-item-body");
37560         Roo.fly(body).addClass("tab-pane");
37561          bodyEl.insertBefore(body, bodyEl.firstChild);
37562         return body;
37563     },
37564     /** @private */
37565     createStripElements :  function(stripEl, text, closable, tpl)
37566     {
37567         var td = document.createElement("li"); // was td..
37568         
37569         
37570         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37571         
37572         
37573         stripEl.appendChild(td);
37574         /*if(closable){
37575             td.className = "x-tabs-closable";
37576             if(!this.closeTpl){
37577                 this.closeTpl = new Roo.Template(
37578                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37579                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37580                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37581                 );
37582             }
37583             var el = this.closeTpl.overwrite(td, {"text": text});
37584             var close = el.getElementsByTagName("div")[0];
37585             var inner = el.getElementsByTagName("em")[0];
37586             return {"el": el, "close": close, "inner": inner};
37587         } else {
37588         */
37589         // not sure what this is..
37590 //            if(!this.tabTpl){
37591                 //this.tabTpl = new Roo.Template(
37592                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37593                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37594                 //);
37595 //                this.tabTpl = new Roo.Template(
37596 //                   '<a href="#">' +
37597 //                   '<span unselectable="on"' +
37598 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37599 //                            ' >{text}</span></a>'
37600 //                );
37601 //                
37602 //            }
37603
37604
37605             var template = tpl || this.tabTpl || false;
37606             
37607             if(!template){
37608                 
37609                 template = new Roo.Template(
37610                    '<a href="#">' +
37611                    '<span unselectable="on"' +
37612                             (this.disableTooltips ? '' : ' title="{text}"') +
37613                             ' >{text}</span></a>'
37614                 );
37615             }
37616             
37617             switch (typeof(template)) {
37618                 case 'object' :
37619                     break;
37620                 case 'string' :
37621                     template = new Roo.Template(template);
37622                     break;
37623                 default :
37624                     break;
37625             }
37626             
37627             var el = template.overwrite(td, {"text": text});
37628             
37629             var inner = el.getElementsByTagName("span")[0];
37630             
37631             return {"el": el, "inner": inner};
37632             
37633     }
37634         
37635     
37636 });
37637
37638 /**
37639  * @class Roo.TabPanelItem
37640  * @extends Roo.util.Observable
37641  * Represents an individual item (tab plus body) in a TabPanel.
37642  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37643  * @param {String} id The id of this TabPanelItem
37644  * @param {String} text The text for the tab of this TabPanelItem
37645  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37646  */
37647 Roo.bootstrap.panel.TabItem = function(config){
37648     /**
37649      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37650      * @type Roo.TabPanel
37651      */
37652     this.tabPanel = config.panel;
37653     /**
37654      * The id for this TabPanelItem
37655      * @type String
37656      */
37657     this.id = config.id;
37658     /** @private */
37659     this.disabled = false;
37660     /** @private */
37661     this.text = config.text;
37662     /** @private */
37663     this.loaded = false;
37664     this.closable = config.closable;
37665
37666     /**
37667      * The body element for this TabPanelItem.
37668      * @type Roo.Element
37669      */
37670     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37671     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37672     this.bodyEl.setStyle("display", "block");
37673     this.bodyEl.setStyle("zoom", "1");
37674     //this.hideAction();
37675
37676     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37677     /** @private */
37678     this.el = Roo.get(els.el);
37679     this.inner = Roo.get(els.inner, true);
37680     this.textEl = Roo.get(this.el.dom.firstChild, true);
37681     this.pnode = Roo.get(els.el.parentNode, true);
37682 //    this.el.on("mousedown", this.onTabMouseDown, this);
37683     this.el.on("click", this.onTabClick, this);
37684     /** @private */
37685     if(config.closable){
37686         var c = Roo.get(els.close, true);
37687         c.dom.title = this.closeText;
37688         c.addClassOnOver("close-over");
37689         c.on("click", this.closeClick, this);
37690      }
37691
37692     this.addEvents({
37693          /**
37694          * @event activate
37695          * Fires when this tab becomes the active tab.
37696          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37697          * @param {Roo.TabPanelItem} this
37698          */
37699         "activate": true,
37700         /**
37701          * @event beforeclose
37702          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37703          * @param {Roo.TabPanelItem} this
37704          * @param {Object} e Set cancel to true on this object to cancel the close.
37705          */
37706         "beforeclose": true,
37707         /**
37708          * @event close
37709          * Fires when this tab is closed.
37710          * @param {Roo.TabPanelItem} this
37711          */
37712          "close": true,
37713         /**
37714          * @event deactivate
37715          * Fires when this tab is no longer the active tab.
37716          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37717          * @param {Roo.TabPanelItem} this
37718          */
37719          "deactivate" : true
37720     });
37721     this.hidden = false;
37722
37723     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37724 };
37725
37726 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37727            {
37728     purgeListeners : function(){
37729        Roo.util.Observable.prototype.purgeListeners.call(this);
37730        this.el.removeAllListeners();
37731     },
37732     /**
37733      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37734      */
37735     show : function(){
37736         this.pnode.addClass("active");
37737         this.showAction();
37738         if(Roo.isOpera){
37739             this.tabPanel.stripWrap.repaint();
37740         }
37741         this.fireEvent("activate", this.tabPanel, this);
37742     },
37743
37744     /**
37745      * Returns true if this tab is the active tab.
37746      * @return {Boolean}
37747      */
37748     isActive : function(){
37749         return this.tabPanel.getActiveTab() == this;
37750     },
37751
37752     /**
37753      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37754      */
37755     hide : function(){
37756         this.pnode.removeClass("active");
37757         this.hideAction();
37758         this.fireEvent("deactivate", this.tabPanel, this);
37759     },
37760
37761     hideAction : function(){
37762         this.bodyEl.hide();
37763         this.bodyEl.setStyle("position", "absolute");
37764         this.bodyEl.setLeft("-20000px");
37765         this.bodyEl.setTop("-20000px");
37766     },
37767
37768     showAction : function(){
37769         this.bodyEl.setStyle("position", "relative");
37770         this.bodyEl.setTop("");
37771         this.bodyEl.setLeft("");
37772         this.bodyEl.show();
37773     },
37774
37775     /**
37776      * Set the tooltip for the tab.
37777      * @param {String} tooltip The tab's tooltip
37778      */
37779     setTooltip : function(text){
37780         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37781             this.textEl.dom.qtip = text;
37782             this.textEl.dom.removeAttribute('title');
37783         }else{
37784             this.textEl.dom.title = text;
37785         }
37786     },
37787
37788     onTabClick : function(e){
37789         e.preventDefault();
37790         this.tabPanel.activate(this.id);
37791     },
37792
37793     onTabMouseDown : function(e){
37794         e.preventDefault();
37795         this.tabPanel.activate(this.id);
37796     },
37797 /*
37798     getWidth : function(){
37799         return this.inner.getWidth();
37800     },
37801
37802     setWidth : function(width){
37803         var iwidth = width - this.pnode.getPadding("lr");
37804         this.inner.setWidth(iwidth);
37805         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37806         this.pnode.setWidth(width);
37807     },
37808 */
37809     /**
37810      * Show or hide the tab
37811      * @param {Boolean} hidden True to hide or false to show.
37812      */
37813     setHidden : function(hidden){
37814         this.hidden = hidden;
37815         this.pnode.setStyle("display", hidden ? "none" : "");
37816     },
37817
37818     /**
37819      * Returns true if this tab is "hidden"
37820      * @return {Boolean}
37821      */
37822     isHidden : function(){
37823         return this.hidden;
37824     },
37825
37826     /**
37827      * Returns the text for this tab
37828      * @return {String}
37829      */
37830     getText : function(){
37831         return this.text;
37832     },
37833     /*
37834     autoSize : function(){
37835         //this.el.beginMeasure();
37836         this.textEl.setWidth(1);
37837         /*
37838          *  #2804 [new] Tabs in Roojs
37839          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37840          */
37841         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37842         //this.el.endMeasure();
37843     //},
37844
37845     /**
37846      * Sets the text for the tab (Note: this also sets the tooltip text)
37847      * @param {String} text The tab's text and tooltip
37848      */
37849     setText : function(text){
37850         this.text = text;
37851         this.textEl.update(text);
37852         this.setTooltip(text);
37853         //if(!this.tabPanel.resizeTabs){
37854         //    this.autoSize();
37855         //}
37856     },
37857     /**
37858      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37859      */
37860     activate : function(){
37861         this.tabPanel.activate(this.id);
37862     },
37863
37864     /**
37865      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37866      */
37867     disable : function(){
37868         if(this.tabPanel.active != this){
37869             this.disabled = true;
37870             this.pnode.addClass("disabled");
37871         }
37872     },
37873
37874     /**
37875      * Enables this TabPanelItem if it was previously disabled.
37876      */
37877     enable : function(){
37878         this.disabled = false;
37879         this.pnode.removeClass("disabled");
37880     },
37881
37882     /**
37883      * Sets the content for this TabPanelItem.
37884      * @param {String} content The content
37885      * @param {Boolean} loadScripts true to look for and load scripts
37886      */
37887     setContent : function(content, loadScripts){
37888         this.bodyEl.update(content, loadScripts);
37889     },
37890
37891     /**
37892      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37893      * @return {Roo.UpdateManager} The UpdateManager
37894      */
37895     getUpdateManager : function(){
37896         return this.bodyEl.getUpdateManager();
37897     },
37898
37899     /**
37900      * Set a URL to be used to load the content for this TabPanelItem.
37901      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37902      * @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)
37903      * @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)
37904      * @return {Roo.UpdateManager} The UpdateManager
37905      */
37906     setUrl : function(url, params, loadOnce){
37907         if(this.refreshDelegate){
37908             this.un('activate', this.refreshDelegate);
37909         }
37910         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37911         this.on("activate", this.refreshDelegate);
37912         return this.bodyEl.getUpdateManager();
37913     },
37914
37915     /** @private */
37916     _handleRefresh : function(url, params, loadOnce){
37917         if(!loadOnce || !this.loaded){
37918             var updater = this.bodyEl.getUpdateManager();
37919             updater.update(url, params, this._setLoaded.createDelegate(this));
37920         }
37921     },
37922
37923     /**
37924      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37925      *   Will fail silently if the setUrl method has not been called.
37926      *   This does not activate the panel, just updates its content.
37927      */
37928     refresh : function(){
37929         if(this.refreshDelegate){
37930            this.loaded = false;
37931            this.refreshDelegate();
37932         }
37933     },
37934
37935     /** @private */
37936     _setLoaded : function(){
37937         this.loaded = true;
37938     },
37939
37940     /** @private */
37941     closeClick : function(e){
37942         var o = {};
37943         e.stopEvent();
37944         this.fireEvent("beforeclose", this, o);
37945         if(o.cancel !== true){
37946             this.tabPanel.removeTab(this.id);
37947         }
37948     },
37949     /**
37950      * The text displayed in the tooltip for the close icon.
37951      * @type String
37952      */
37953     closeText : "Close this tab"
37954 });
37955 /**
37956 *    This script refer to:
37957 *    Title: International Telephone Input
37958 *    Author: Jack O'Connor
37959 *    Code version:  v12.1.12
37960 *    Availability: https://github.com/jackocnr/intl-tel-input.git
37961 **/
37962
37963 Roo.bootstrap.PhoneInputData = function() {
37964     var d = [
37965       [
37966         "Afghanistan (‫افغانستان‬‎)",
37967         "af",
37968         "93"
37969       ],
37970       [
37971         "Albania (Shqipëri)",
37972         "al",
37973         "355"
37974       ],
37975       [
37976         "Algeria (‫الجزائر‬‎)",
37977         "dz",
37978         "213"
37979       ],
37980       [
37981         "American Samoa",
37982         "as",
37983         "1684"
37984       ],
37985       [
37986         "Andorra",
37987         "ad",
37988         "376"
37989       ],
37990       [
37991         "Angola",
37992         "ao",
37993         "244"
37994       ],
37995       [
37996         "Anguilla",
37997         "ai",
37998         "1264"
37999       ],
38000       [
38001         "Antigua and Barbuda",
38002         "ag",
38003         "1268"
38004       ],
38005       [
38006         "Argentina",
38007         "ar",
38008         "54"
38009       ],
38010       [
38011         "Armenia (Հայաստան)",
38012         "am",
38013         "374"
38014       ],
38015       [
38016         "Aruba",
38017         "aw",
38018         "297"
38019       ],
38020       [
38021         "Australia",
38022         "au",
38023         "61",
38024         0
38025       ],
38026       [
38027         "Austria (Österreich)",
38028         "at",
38029         "43"
38030       ],
38031       [
38032         "Azerbaijan (Azərbaycan)",
38033         "az",
38034         "994"
38035       ],
38036       [
38037         "Bahamas",
38038         "bs",
38039         "1242"
38040       ],
38041       [
38042         "Bahrain (‫البحرين‬‎)",
38043         "bh",
38044         "973"
38045       ],
38046       [
38047         "Bangladesh (বাংলাদেশ)",
38048         "bd",
38049         "880"
38050       ],
38051       [
38052         "Barbados",
38053         "bb",
38054         "1246"
38055       ],
38056       [
38057         "Belarus (Беларусь)",
38058         "by",
38059         "375"
38060       ],
38061       [
38062         "Belgium (België)",
38063         "be",
38064         "32"
38065       ],
38066       [
38067         "Belize",
38068         "bz",
38069         "501"
38070       ],
38071       [
38072         "Benin (Bénin)",
38073         "bj",
38074         "229"
38075       ],
38076       [
38077         "Bermuda",
38078         "bm",
38079         "1441"
38080       ],
38081       [
38082         "Bhutan (འབྲུག)",
38083         "bt",
38084         "975"
38085       ],
38086       [
38087         "Bolivia",
38088         "bo",
38089         "591"
38090       ],
38091       [
38092         "Bosnia and Herzegovina (Босна и Херцеговина)",
38093         "ba",
38094         "387"
38095       ],
38096       [
38097         "Botswana",
38098         "bw",
38099         "267"
38100       ],
38101       [
38102         "Brazil (Brasil)",
38103         "br",
38104         "55"
38105       ],
38106       [
38107         "British Indian Ocean Territory",
38108         "io",
38109         "246"
38110       ],
38111       [
38112         "British Virgin Islands",
38113         "vg",
38114         "1284"
38115       ],
38116       [
38117         "Brunei",
38118         "bn",
38119         "673"
38120       ],
38121       [
38122         "Bulgaria (България)",
38123         "bg",
38124         "359"
38125       ],
38126       [
38127         "Burkina Faso",
38128         "bf",
38129         "226"
38130       ],
38131       [
38132         "Burundi (Uburundi)",
38133         "bi",
38134         "257"
38135       ],
38136       [
38137         "Cambodia (កម្ពុជា)",
38138         "kh",
38139         "855"
38140       ],
38141       [
38142         "Cameroon (Cameroun)",
38143         "cm",
38144         "237"
38145       ],
38146       [
38147         "Canada",
38148         "ca",
38149         "1",
38150         1,
38151         ["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"]
38152       ],
38153       [
38154         "Cape Verde (Kabu Verdi)",
38155         "cv",
38156         "238"
38157       ],
38158       [
38159         "Caribbean Netherlands",
38160         "bq",
38161         "599",
38162         1
38163       ],
38164       [
38165         "Cayman Islands",
38166         "ky",
38167         "1345"
38168       ],
38169       [
38170         "Central African Republic (République centrafricaine)",
38171         "cf",
38172         "236"
38173       ],
38174       [
38175         "Chad (Tchad)",
38176         "td",
38177         "235"
38178       ],
38179       [
38180         "Chile",
38181         "cl",
38182         "56"
38183       ],
38184       [
38185         "China (中国)",
38186         "cn",
38187         "86"
38188       ],
38189       [
38190         "Christmas Island",
38191         "cx",
38192         "61",
38193         2
38194       ],
38195       [
38196         "Cocos (Keeling) Islands",
38197         "cc",
38198         "61",
38199         1
38200       ],
38201       [
38202         "Colombia",
38203         "co",
38204         "57"
38205       ],
38206       [
38207         "Comoros (‫جزر القمر‬‎)",
38208         "km",
38209         "269"
38210       ],
38211       [
38212         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38213         "cd",
38214         "243"
38215       ],
38216       [
38217         "Congo (Republic) (Congo-Brazzaville)",
38218         "cg",
38219         "242"
38220       ],
38221       [
38222         "Cook Islands",
38223         "ck",
38224         "682"
38225       ],
38226       [
38227         "Costa Rica",
38228         "cr",
38229         "506"
38230       ],
38231       [
38232         "Côte d’Ivoire",
38233         "ci",
38234         "225"
38235       ],
38236       [
38237         "Croatia (Hrvatska)",
38238         "hr",
38239         "385"
38240       ],
38241       [
38242         "Cuba",
38243         "cu",
38244         "53"
38245       ],
38246       [
38247         "Curaçao",
38248         "cw",
38249         "599",
38250         0
38251       ],
38252       [
38253         "Cyprus (Κύπρος)",
38254         "cy",
38255         "357"
38256       ],
38257       [
38258         "Czech Republic (Česká republika)",
38259         "cz",
38260         "420"
38261       ],
38262       [
38263         "Denmark (Danmark)",
38264         "dk",
38265         "45"
38266       ],
38267       [
38268         "Djibouti",
38269         "dj",
38270         "253"
38271       ],
38272       [
38273         "Dominica",
38274         "dm",
38275         "1767"
38276       ],
38277       [
38278         "Dominican Republic (República Dominicana)",
38279         "do",
38280         "1",
38281         2,
38282         ["809", "829", "849"]
38283       ],
38284       [
38285         "Ecuador",
38286         "ec",
38287         "593"
38288       ],
38289       [
38290         "Egypt (‫مصر‬‎)",
38291         "eg",
38292         "20"
38293       ],
38294       [
38295         "El Salvador",
38296         "sv",
38297         "503"
38298       ],
38299       [
38300         "Equatorial Guinea (Guinea Ecuatorial)",
38301         "gq",
38302         "240"
38303       ],
38304       [
38305         "Eritrea",
38306         "er",
38307         "291"
38308       ],
38309       [
38310         "Estonia (Eesti)",
38311         "ee",
38312         "372"
38313       ],
38314       [
38315         "Ethiopia",
38316         "et",
38317         "251"
38318       ],
38319       [
38320         "Falkland Islands (Islas Malvinas)",
38321         "fk",
38322         "500"
38323       ],
38324       [
38325         "Faroe Islands (Føroyar)",
38326         "fo",
38327         "298"
38328       ],
38329       [
38330         "Fiji",
38331         "fj",
38332         "679"
38333       ],
38334       [
38335         "Finland (Suomi)",
38336         "fi",
38337         "358",
38338         0
38339       ],
38340       [
38341         "France",
38342         "fr",
38343         "33"
38344       ],
38345       [
38346         "French Guiana (Guyane française)",
38347         "gf",
38348         "594"
38349       ],
38350       [
38351         "French Polynesia (Polynésie française)",
38352         "pf",
38353         "689"
38354       ],
38355       [
38356         "Gabon",
38357         "ga",
38358         "241"
38359       ],
38360       [
38361         "Gambia",
38362         "gm",
38363         "220"
38364       ],
38365       [
38366         "Georgia (საქართველო)",
38367         "ge",
38368         "995"
38369       ],
38370       [
38371         "Germany (Deutschland)",
38372         "de",
38373         "49"
38374       ],
38375       [
38376         "Ghana (Gaana)",
38377         "gh",
38378         "233"
38379       ],
38380       [
38381         "Gibraltar",
38382         "gi",
38383         "350"
38384       ],
38385       [
38386         "Greece (Ελλάδα)",
38387         "gr",
38388         "30"
38389       ],
38390       [
38391         "Greenland (Kalaallit Nunaat)",
38392         "gl",
38393         "299"
38394       ],
38395       [
38396         "Grenada",
38397         "gd",
38398         "1473"
38399       ],
38400       [
38401         "Guadeloupe",
38402         "gp",
38403         "590",
38404         0
38405       ],
38406       [
38407         "Guam",
38408         "gu",
38409         "1671"
38410       ],
38411       [
38412         "Guatemala",
38413         "gt",
38414         "502"
38415       ],
38416       [
38417         "Guernsey",
38418         "gg",
38419         "44",
38420         1
38421       ],
38422       [
38423         "Guinea (Guinée)",
38424         "gn",
38425         "224"
38426       ],
38427       [
38428         "Guinea-Bissau (Guiné Bissau)",
38429         "gw",
38430         "245"
38431       ],
38432       [
38433         "Guyana",
38434         "gy",
38435         "592"
38436       ],
38437       [
38438         "Haiti",
38439         "ht",
38440         "509"
38441       ],
38442       [
38443         "Honduras",
38444         "hn",
38445         "504"
38446       ],
38447       [
38448         "Hong Kong (香港)",
38449         "hk",
38450         "852"
38451       ],
38452       [
38453         "Hungary (Magyarország)",
38454         "hu",
38455         "36"
38456       ],
38457       [
38458         "Iceland (Ísland)",
38459         "is",
38460         "354"
38461       ],
38462       [
38463         "India (भारत)",
38464         "in",
38465         "91"
38466       ],
38467       [
38468         "Indonesia",
38469         "id",
38470         "62"
38471       ],
38472       [
38473         "Iran (‫ایران‬‎)",
38474         "ir",
38475         "98"
38476       ],
38477       [
38478         "Iraq (‫العراق‬‎)",
38479         "iq",
38480         "964"
38481       ],
38482       [
38483         "Ireland",
38484         "ie",
38485         "353"
38486       ],
38487       [
38488         "Isle of Man",
38489         "im",
38490         "44",
38491         2
38492       ],
38493       [
38494         "Israel (‫ישראל‬‎)",
38495         "il",
38496         "972"
38497       ],
38498       [
38499         "Italy (Italia)",
38500         "it",
38501         "39",
38502         0
38503       ],
38504       [
38505         "Jamaica",
38506         "jm",
38507         "1876"
38508       ],
38509       [
38510         "Japan (日本)",
38511         "jp",
38512         "81"
38513       ],
38514       [
38515         "Jersey",
38516         "je",
38517         "44",
38518         3
38519       ],
38520       [
38521         "Jordan (‫الأردن‬‎)",
38522         "jo",
38523         "962"
38524       ],
38525       [
38526         "Kazakhstan (Казахстан)",
38527         "kz",
38528         "7",
38529         1
38530       ],
38531       [
38532         "Kenya",
38533         "ke",
38534         "254"
38535       ],
38536       [
38537         "Kiribati",
38538         "ki",
38539         "686"
38540       ],
38541       [
38542         "Kosovo",
38543         "xk",
38544         "383"
38545       ],
38546       [
38547         "Kuwait (‫الكويت‬‎)",
38548         "kw",
38549         "965"
38550       ],
38551       [
38552         "Kyrgyzstan (Кыргызстан)",
38553         "kg",
38554         "996"
38555       ],
38556       [
38557         "Laos (ລາວ)",
38558         "la",
38559         "856"
38560       ],
38561       [
38562         "Latvia (Latvija)",
38563         "lv",
38564         "371"
38565       ],
38566       [
38567         "Lebanon (‫لبنان‬‎)",
38568         "lb",
38569         "961"
38570       ],
38571       [
38572         "Lesotho",
38573         "ls",
38574         "266"
38575       ],
38576       [
38577         "Liberia",
38578         "lr",
38579         "231"
38580       ],
38581       [
38582         "Libya (‫ليبيا‬‎)",
38583         "ly",
38584         "218"
38585       ],
38586       [
38587         "Liechtenstein",
38588         "li",
38589         "423"
38590       ],
38591       [
38592         "Lithuania (Lietuva)",
38593         "lt",
38594         "370"
38595       ],
38596       [
38597         "Luxembourg",
38598         "lu",
38599         "352"
38600       ],
38601       [
38602         "Macau (澳門)",
38603         "mo",
38604         "853"
38605       ],
38606       [
38607         "Macedonia (FYROM) (Македонија)",
38608         "mk",
38609         "389"
38610       ],
38611       [
38612         "Madagascar (Madagasikara)",
38613         "mg",
38614         "261"
38615       ],
38616       [
38617         "Malawi",
38618         "mw",
38619         "265"
38620       ],
38621       [
38622         "Malaysia",
38623         "my",
38624         "60"
38625       ],
38626       [
38627         "Maldives",
38628         "mv",
38629         "960"
38630       ],
38631       [
38632         "Mali",
38633         "ml",
38634         "223"
38635       ],
38636       [
38637         "Malta",
38638         "mt",
38639         "356"
38640       ],
38641       [
38642         "Marshall Islands",
38643         "mh",
38644         "692"
38645       ],
38646       [
38647         "Martinique",
38648         "mq",
38649         "596"
38650       ],
38651       [
38652         "Mauritania (‫موريتانيا‬‎)",
38653         "mr",
38654         "222"
38655       ],
38656       [
38657         "Mauritius (Moris)",
38658         "mu",
38659         "230"
38660       ],
38661       [
38662         "Mayotte",
38663         "yt",
38664         "262",
38665         1
38666       ],
38667       [
38668         "Mexico (México)",
38669         "mx",
38670         "52"
38671       ],
38672       [
38673         "Micronesia",
38674         "fm",
38675         "691"
38676       ],
38677       [
38678         "Moldova (Republica Moldova)",
38679         "md",
38680         "373"
38681       ],
38682       [
38683         "Monaco",
38684         "mc",
38685         "377"
38686       ],
38687       [
38688         "Mongolia (Монгол)",
38689         "mn",
38690         "976"
38691       ],
38692       [
38693         "Montenegro (Crna Gora)",
38694         "me",
38695         "382"
38696       ],
38697       [
38698         "Montserrat",
38699         "ms",
38700         "1664"
38701       ],
38702       [
38703         "Morocco (‫المغرب‬‎)",
38704         "ma",
38705         "212",
38706         0
38707       ],
38708       [
38709         "Mozambique (Moçambique)",
38710         "mz",
38711         "258"
38712       ],
38713       [
38714         "Myanmar (Burma) (မြန်မာ)",
38715         "mm",
38716         "95"
38717       ],
38718       [
38719         "Namibia (Namibië)",
38720         "na",
38721         "264"
38722       ],
38723       [
38724         "Nauru",
38725         "nr",
38726         "674"
38727       ],
38728       [
38729         "Nepal (नेपाल)",
38730         "np",
38731         "977"
38732       ],
38733       [
38734         "Netherlands (Nederland)",
38735         "nl",
38736         "31"
38737       ],
38738       [
38739         "New Caledonia (Nouvelle-Calédonie)",
38740         "nc",
38741         "687"
38742       ],
38743       [
38744         "New Zealand",
38745         "nz",
38746         "64"
38747       ],
38748       [
38749         "Nicaragua",
38750         "ni",
38751         "505"
38752       ],
38753       [
38754         "Niger (Nijar)",
38755         "ne",
38756         "227"
38757       ],
38758       [
38759         "Nigeria",
38760         "ng",
38761         "234"
38762       ],
38763       [
38764         "Niue",
38765         "nu",
38766         "683"
38767       ],
38768       [
38769         "Norfolk Island",
38770         "nf",
38771         "672"
38772       ],
38773       [
38774         "North Korea (조선 민주주의 인민 공화국)",
38775         "kp",
38776         "850"
38777       ],
38778       [
38779         "Northern Mariana Islands",
38780         "mp",
38781         "1670"
38782       ],
38783       [
38784         "Norway (Norge)",
38785         "no",
38786         "47",
38787         0
38788       ],
38789       [
38790         "Oman (‫عُمان‬‎)",
38791         "om",
38792         "968"
38793       ],
38794       [
38795         "Pakistan (‫پاکستان‬‎)",
38796         "pk",
38797         "92"
38798       ],
38799       [
38800         "Palau",
38801         "pw",
38802         "680"
38803       ],
38804       [
38805         "Palestine (‫فلسطين‬‎)",
38806         "ps",
38807         "970"
38808       ],
38809       [
38810         "Panama (Panamá)",
38811         "pa",
38812         "507"
38813       ],
38814       [
38815         "Papua New Guinea",
38816         "pg",
38817         "675"
38818       ],
38819       [
38820         "Paraguay",
38821         "py",
38822         "595"
38823       ],
38824       [
38825         "Peru (Perú)",
38826         "pe",
38827         "51"
38828       ],
38829       [
38830         "Philippines",
38831         "ph",
38832         "63"
38833       ],
38834       [
38835         "Poland (Polska)",
38836         "pl",
38837         "48"
38838       ],
38839       [
38840         "Portugal",
38841         "pt",
38842         "351"
38843       ],
38844       [
38845         "Puerto Rico",
38846         "pr",
38847         "1",
38848         3,
38849         ["787", "939"]
38850       ],
38851       [
38852         "Qatar (‫قطر‬‎)",
38853         "qa",
38854         "974"
38855       ],
38856       [
38857         "Réunion (La Réunion)",
38858         "re",
38859         "262",
38860         0
38861       ],
38862       [
38863         "Romania (România)",
38864         "ro",
38865         "40"
38866       ],
38867       [
38868         "Russia (Россия)",
38869         "ru",
38870         "7",
38871         0
38872       ],
38873       [
38874         "Rwanda",
38875         "rw",
38876         "250"
38877       ],
38878       [
38879         "Saint Barthélemy",
38880         "bl",
38881         "590",
38882         1
38883       ],
38884       [
38885         "Saint Helena",
38886         "sh",
38887         "290"
38888       ],
38889       [
38890         "Saint Kitts and Nevis",
38891         "kn",
38892         "1869"
38893       ],
38894       [
38895         "Saint Lucia",
38896         "lc",
38897         "1758"
38898       ],
38899       [
38900         "Saint Martin (Saint-Martin (partie française))",
38901         "mf",
38902         "590",
38903         2
38904       ],
38905       [
38906         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
38907         "pm",
38908         "508"
38909       ],
38910       [
38911         "Saint Vincent and the Grenadines",
38912         "vc",
38913         "1784"
38914       ],
38915       [
38916         "Samoa",
38917         "ws",
38918         "685"
38919       ],
38920       [
38921         "San Marino",
38922         "sm",
38923         "378"
38924       ],
38925       [
38926         "São Tomé and Príncipe (São Tomé e Príncipe)",
38927         "st",
38928         "239"
38929       ],
38930       [
38931         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
38932         "sa",
38933         "966"
38934       ],
38935       [
38936         "Senegal (Sénégal)",
38937         "sn",
38938         "221"
38939       ],
38940       [
38941         "Serbia (Србија)",
38942         "rs",
38943         "381"
38944       ],
38945       [
38946         "Seychelles",
38947         "sc",
38948         "248"
38949       ],
38950       [
38951         "Sierra Leone",
38952         "sl",
38953         "232"
38954       ],
38955       [
38956         "Singapore",
38957         "sg",
38958         "65"
38959       ],
38960       [
38961         "Sint Maarten",
38962         "sx",
38963         "1721"
38964       ],
38965       [
38966         "Slovakia (Slovensko)",
38967         "sk",
38968         "421"
38969       ],
38970       [
38971         "Slovenia (Slovenija)",
38972         "si",
38973         "386"
38974       ],
38975       [
38976         "Solomon Islands",
38977         "sb",
38978         "677"
38979       ],
38980       [
38981         "Somalia (Soomaaliya)",
38982         "so",
38983         "252"
38984       ],
38985       [
38986         "South Africa",
38987         "za",
38988         "27"
38989       ],
38990       [
38991         "South Korea (대한민국)",
38992         "kr",
38993         "82"
38994       ],
38995       [
38996         "South Sudan (‫جنوب السودان‬‎)",
38997         "ss",
38998         "211"
38999       ],
39000       [
39001         "Spain (España)",
39002         "es",
39003         "34"
39004       ],
39005       [
39006         "Sri Lanka (ශ්‍රී ලංකාව)",
39007         "lk",
39008         "94"
39009       ],
39010       [
39011         "Sudan (‫السودان‬‎)",
39012         "sd",
39013         "249"
39014       ],
39015       [
39016         "Suriname",
39017         "sr",
39018         "597"
39019       ],
39020       [
39021         "Svalbard and Jan Mayen",
39022         "sj",
39023         "47",
39024         1
39025       ],
39026       [
39027         "Swaziland",
39028         "sz",
39029         "268"
39030       ],
39031       [
39032         "Sweden (Sverige)",
39033         "se",
39034         "46"
39035       ],
39036       [
39037         "Switzerland (Schweiz)",
39038         "ch",
39039         "41"
39040       ],
39041       [
39042         "Syria (‫سوريا‬‎)",
39043         "sy",
39044         "963"
39045       ],
39046       [
39047         "Taiwan (台灣)",
39048         "tw",
39049         "886"
39050       ],
39051       [
39052         "Tajikistan",
39053         "tj",
39054         "992"
39055       ],
39056       [
39057         "Tanzania",
39058         "tz",
39059         "255"
39060       ],
39061       [
39062         "Thailand (ไทย)",
39063         "th",
39064         "66"
39065       ],
39066       [
39067         "Timor-Leste",
39068         "tl",
39069         "670"
39070       ],
39071       [
39072         "Togo",
39073         "tg",
39074         "228"
39075       ],
39076       [
39077         "Tokelau",
39078         "tk",
39079         "690"
39080       ],
39081       [
39082         "Tonga",
39083         "to",
39084         "676"
39085       ],
39086       [
39087         "Trinidad and Tobago",
39088         "tt",
39089         "1868"
39090       ],
39091       [
39092         "Tunisia (‫تونس‬‎)",
39093         "tn",
39094         "216"
39095       ],
39096       [
39097         "Turkey (Türkiye)",
39098         "tr",
39099         "90"
39100       ],
39101       [
39102         "Turkmenistan",
39103         "tm",
39104         "993"
39105       ],
39106       [
39107         "Turks and Caicos Islands",
39108         "tc",
39109         "1649"
39110       ],
39111       [
39112         "Tuvalu",
39113         "tv",
39114         "688"
39115       ],
39116       [
39117         "U.S. Virgin Islands",
39118         "vi",
39119         "1340"
39120       ],
39121       [
39122         "Uganda",
39123         "ug",
39124         "256"
39125       ],
39126       [
39127         "Ukraine (Україна)",
39128         "ua",
39129         "380"
39130       ],
39131       [
39132         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39133         "ae",
39134         "971"
39135       ],
39136       [
39137         "United Kingdom",
39138         "gb",
39139         "44",
39140         0
39141       ],
39142       [
39143         "United States",
39144         "us",
39145         "1",
39146         0
39147       ],
39148       [
39149         "Uruguay",
39150         "uy",
39151         "598"
39152       ],
39153       [
39154         "Uzbekistan (Oʻzbekiston)",
39155         "uz",
39156         "998"
39157       ],
39158       [
39159         "Vanuatu",
39160         "vu",
39161         "678"
39162       ],
39163       [
39164         "Vatican City (Città del Vaticano)",
39165         "va",
39166         "39",
39167         1
39168       ],
39169       [
39170         "Venezuela",
39171         "ve",
39172         "58"
39173       ],
39174       [
39175         "Vietnam (Việt Nam)",
39176         "vn",
39177         "84"
39178       ],
39179       [
39180         "Wallis and Futuna (Wallis-et-Futuna)",
39181         "wf",
39182         "681"
39183       ],
39184       [
39185         "Western Sahara (‫الصحراء الغربية‬‎)",
39186         "eh",
39187         "212",
39188         1
39189       ],
39190       [
39191         "Yemen (‫اليمن‬‎)",
39192         "ye",
39193         "967"
39194       ],
39195       [
39196         "Zambia",
39197         "zm",
39198         "260"
39199       ],
39200       [
39201         "Zimbabwe",
39202         "zw",
39203         "263"
39204       ],
39205       [
39206         "Åland Islands",
39207         "ax",
39208         "358",
39209         1
39210       ]
39211   ];
39212   
39213   return d;
39214 }/**
39215 *    This script refer to:
39216 *    Title: International Telephone Input
39217 *    Author: Jack O'Connor
39218 *    Code version:  v12.1.12
39219 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39220 **/
39221
39222 /**
39223  * @class Roo.bootstrap.PhoneInput
39224  * @extends Roo.bootstrap.TriggerField
39225  * An input with International dial-code selection
39226  
39227  * @cfg {String} defaultDialCode default '+852'
39228  * @cfg {Array} preferedCountries default []
39229   
39230  * @constructor
39231  * Create a new PhoneInput.
39232  * @param {Object} config Configuration options
39233  */
39234
39235 Roo.bootstrap.PhoneInput = function(config) {
39236     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39237 };
39238
39239 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39240         
39241         listWidth: undefined,
39242         
39243         selectedClass: 'active',
39244         
39245         invalidClass : "has-warning",
39246         
39247         validClass: 'has-success',
39248         
39249         allowed: '0123456789',
39250         
39251         /**
39252          * @cfg {String} defaultDialCode The default dial code when initializing the input
39253          */
39254         defaultDialCode: '+852',
39255         
39256         /**
39257          * @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
39258          */
39259         preferedCountries: false,
39260         
39261         getAutoCreate : function()
39262         {
39263             var data = Roo.bootstrap.PhoneInputData();
39264             var align = this.labelAlign || this.parentLabelAlign();
39265             var id = Roo.id();
39266             
39267             this.allCountries = [];
39268             this.dialCodeMapping = [];
39269             
39270             for (var i = 0; i < data.length; i++) {
39271               var c = data[i];
39272               this.allCountries[i] = {
39273                 name: c[0],
39274                 iso2: c[1],
39275                 dialCode: c[2],
39276                 priority: c[3] || 0,
39277                 areaCodes: c[4] || null
39278               };
39279               this.dialCodeMapping[c[2]] = {
39280                   name: c[0],
39281                   iso2: c[1],
39282                   priority: c[3] || 0,
39283                   areaCodes: c[4] || null
39284               };
39285             }
39286             
39287             var cfg = {
39288                 cls: 'form-group',
39289                 cn: []
39290             };
39291             
39292             var input =  {
39293                 tag: 'input',
39294                 id : id,
39295                 cls : 'form-control tel-input',
39296                 autocomplete: 'new-password'
39297             };
39298             
39299             var hiddenInput = {
39300                 tag: 'input',
39301                 type: 'hidden',
39302                 cls: 'hidden-tel-input'
39303             };
39304             
39305             if (this.name) {
39306                 hiddenInput.name = this.name;
39307             }
39308             
39309             if (this.disabled) {
39310                 input.disabled = true;
39311             }
39312             
39313             var flag_container = {
39314                 tag: 'div',
39315                 cls: 'flag-box',
39316                 cn: [
39317                     {
39318                         tag: 'div',
39319                         cls: 'flag'
39320                     },
39321                     {
39322                         tag: 'div',
39323                         cls: 'caret'
39324                     }
39325                 ]
39326             };
39327             
39328             var box = {
39329                 tag: 'div',
39330                 cls: this.hasFeedback ? 'has-feedback' : '',
39331                 cn: [
39332                     hiddenInput,
39333                     input,
39334                     {
39335                         tag: 'input',
39336                         cls: 'dial-code-holder',
39337                         disabled: true
39338                     }
39339                 ]
39340             };
39341             
39342             var container = {
39343                 cls: 'roo-select2-container input-group',
39344                 cn: [
39345                     flag_container,
39346                     box
39347                 ]
39348             };
39349             
39350             if (this.fieldLabel.length) {
39351                 var indicator = {
39352                     tag: 'i',
39353                     tooltip: 'This field is required'
39354                 };
39355                 
39356                 var label = {
39357                     tag: 'label',
39358                     'for':  id,
39359                     cls: 'control-label',
39360                     cn: []
39361                 };
39362                 
39363                 var label_text = {
39364                     tag: 'span',
39365                     html: this.fieldLabel
39366                 };
39367                 
39368                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39369                 label.cn = [
39370                     indicator,
39371                     label_text
39372                 ];
39373                 
39374                 if(this.indicatorpos == 'right') {
39375                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39376                     label.cn = [
39377                         label_text,
39378                         indicator
39379                     ];
39380                 }
39381                 
39382                 if(align == 'left') {
39383                     container = {
39384                         tag: 'div',
39385                         cn: [
39386                             container
39387                         ]
39388                     };
39389                     
39390                     if(this.labelWidth > 12){
39391                         label.style = "width: " + this.labelWidth + 'px';
39392                     }
39393                     if(this.labelWidth < 13 && this.labelmd == 0){
39394                         this.labelmd = this.labelWidth;
39395                     }
39396                     if(this.labellg > 0){
39397                         label.cls += ' col-lg-' + this.labellg;
39398                         input.cls += ' col-lg-' + (12 - this.labellg);
39399                     }
39400                     if(this.labelmd > 0){
39401                         label.cls += ' col-md-' + this.labelmd;
39402                         container.cls += ' col-md-' + (12 - this.labelmd);
39403                     }
39404                     if(this.labelsm > 0){
39405                         label.cls += ' col-sm-' + this.labelsm;
39406                         container.cls += ' col-sm-' + (12 - this.labelsm);
39407                     }
39408                     if(this.labelxs > 0){
39409                         label.cls += ' col-xs-' + this.labelxs;
39410                         container.cls += ' col-xs-' + (12 - this.labelxs);
39411                     }
39412                 }
39413             }
39414             
39415             cfg.cn = [
39416                 label,
39417                 container
39418             ];
39419             
39420             var settings = this;
39421             
39422             ['xs','sm','md','lg'].map(function(size){
39423                 if (settings[size]) {
39424                     cfg.cls += ' col-' + size + '-' + settings[size];
39425                 }
39426             });
39427             
39428             this.store = new Roo.data.Store({
39429                 proxy : new Roo.data.MemoryProxy({}),
39430                 reader : new Roo.data.JsonReader({
39431                     fields : [
39432                         {
39433                             'name' : 'name',
39434                             'type' : 'string'
39435                         },
39436                         {
39437                             'name' : 'iso2',
39438                             'type' : 'string'
39439                         },
39440                         {
39441                             'name' : 'dialCode',
39442                             'type' : 'string'
39443                         },
39444                         {
39445                             'name' : 'priority',
39446                             'type' : 'string'
39447                         },
39448                         {
39449                             'name' : 'areaCodes',
39450                             'type' : 'string'
39451                         }
39452                     ]
39453                 })
39454             });
39455             
39456             if(!this.preferedCountries) {
39457                 this.preferedCountries = [
39458                     'hk',
39459                     'gb',
39460                     'us'
39461                 ];
39462             }
39463             
39464             var p = this.preferedCountries.reverse();
39465             
39466             if(p) {
39467                 for (var i = 0; i < p.length; i++) {
39468                     for (var j = 0; j < this.allCountries.length; j++) {
39469                         if(this.allCountries[j].iso2 == p[i]) {
39470                             var t = this.allCountries[j];
39471                             this.allCountries.splice(j,1);
39472                             this.allCountries.unshift(t);
39473                         }
39474                     } 
39475                 }
39476             }
39477             
39478             this.store.proxy.data = {
39479                 success: true,
39480                 data: this.allCountries
39481             };
39482             
39483             return cfg;
39484         },
39485         
39486         initEvents : function()
39487         {
39488             this.createList();
39489             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39490             
39491             this.indicator = this.indicatorEl();
39492             this.flag = this.flagEl();
39493             this.dialCodeHolder = this.dialCodeHolderEl();
39494             
39495             this.trigger = this.el.select('div.flag-box',true).first();
39496             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39497             
39498             var _this = this;
39499             
39500             (function(){
39501                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39502                 _this.list.setWidth(lw);
39503             }).defer(100);
39504             
39505             this.list.on('mouseover', this.onViewOver, this);
39506             this.list.on('mousemove', this.onViewMove, this);
39507             this.inputEl().on("keyup", this.onKeyUp, this);
39508             
39509             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39510
39511             this.view = new Roo.View(this.list, this.tpl, {
39512                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39513             });
39514             
39515             this.view.on('click', this.onViewClick, this);
39516             this.setValue(this.defaultDialCode);
39517         },
39518         
39519         onTriggerClick : function(e)
39520         {
39521             Roo.log('trigger click');
39522             if(this.disabled){
39523                 return;
39524             }
39525             
39526             if(this.isExpanded()){
39527                 this.collapse();
39528                 this.hasFocus = false;
39529             }else {
39530                 this.store.load({});
39531                 this.hasFocus = true;
39532                 this.expand();
39533             }
39534         },
39535         
39536         isExpanded : function()
39537         {
39538             return this.list.isVisible();
39539         },
39540         
39541         collapse : function()
39542         {
39543             if(!this.isExpanded()){
39544                 return;
39545             }
39546             this.list.hide();
39547             Roo.get(document).un('mousedown', this.collapseIf, this);
39548             Roo.get(document).un('mousewheel', this.collapseIf, this);
39549             this.fireEvent('collapse', this);
39550             this.validate();
39551         },
39552         
39553         expand : function()
39554         {
39555             Roo.log('expand');
39556
39557             if(this.isExpanded() || !this.hasFocus){
39558                 return;
39559             }
39560             
39561             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39562             this.list.setWidth(lw);
39563             
39564             this.list.show();
39565             this.restrictHeight();
39566             
39567             Roo.get(document).on('mousedown', this.collapseIf, this);
39568             Roo.get(document).on('mousewheel', this.collapseIf, this);
39569             
39570             this.fireEvent('expand', this);
39571         },
39572         
39573         restrictHeight : function()
39574         {
39575             this.list.alignTo(this.inputEl(), this.listAlign);
39576             this.list.alignTo(this.inputEl(), this.listAlign);
39577         },
39578         
39579         onViewOver : function(e, t)
39580         {
39581             if(this.inKeyMode){
39582                 return;
39583             }
39584             var item = this.view.findItemFromChild(t);
39585             
39586             if(item){
39587                 var index = this.view.indexOf(item);
39588                 this.select(index, false);
39589             }
39590         },
39591
39592         // private
39593         onViewClick : function(view, doFocus, el, e)
39594         {
39595             var index = this.view.getSelectedIndexes()[0];
39596             
39597             var r = this.store.getAt(index);
39598             
39599             if(r){
39600                 this.onSelect(r, index);
39601             }
39602             if(doFocus !== false && !this.blockFocus){
39603                 this.inputEl().focus();
39604             }
39605         },
39606         
39607         onViewMove : function(e, t)
39608         {
39609             this.inKeyMode = false;
39610         },
39611         
39612         select : function(index, scrollIntoView)
39613         {
39614             this.selectedIndex = index;
39615             this.view.select(index);
39616             if(scrollIntoView !== false){
39617                 var el = this.view.getNode(index);
39618                 if(el){
39619                     this.list.scrollChildIntoView(el, false);
39620                 }
39621             }
39622         },
39623         
39624         createList : function()
39625         {
39626             this.list = Roo.get(document.body).createChild({
39627                 tag: 'ul',
39628                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39629                 style: 'display:none'
39630             });
39631             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39632         },
39633         
39634         collapseIf : function(e)
39635         {
39636             var in_combo  = e.within(this.el);
39637             var in_list =  e.within(this.list);
39638             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39639             
39640             if (in_combo || in_list || is_list) {
39641                 return;
39642             }
39643             this.collapse();
39644         },
39645         
39646         onSelect : function(record, index)
39647         {
39648             if(this.fireEvent('beforeselect', this, record, index) !== false){
39649                 
39650                 this.setFlagClass(record.data.iso2);
39651                 this.setDialCode(record.data.dialCode);
39652                 this.hasFocus = false;
39653                 this.collapse();
39654                 this.fireEvent('select', this, record, index);
39655             }
39656         },
39657         
39658         flagEl : function()
39659         {
39660             var flag = this.el.select('div.flag',true).first();
39661             if(!flag){
39662                 return false;
39663             }
39664             return flag;
39665         },
39666         
39667         dialCodeHolderEl : function()
39668         {
39669             var d = this.el.select('input.dial-code-holder',true).first();
39670             if(!d){
39671                 return false;
39672             }
39673             return d;
39674         },
39675         
39676         setDialCode : function(v)
39677         {
39678             this.dialCodeHolder.dom.value = '+'+v;
39679         },
39680         
39681         setFlagClass : function(n)
39682         {
39683             this.flag.dom.className = 'flag '+n;
39684         },
39685         
39686         getValue : function()
39687         {
39688             var v = this.inputEl().getValue();
39689             if(this.dialCodeHolder) {
39690                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39691             }
39692             return v;
39693         },
39694         
39695         setValue : function(v)
39696         {
39697             var d = this.getDialCode(v);
39698             
39699             //invalid dial code
39700             if(v.length == 0 || !d || d.length == 0) {
39701                 if(this.rendered){
39702                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39703                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39704                 }
39705                 return;
39706             }
39707             
39708             //valid dial code
39709             this.setFlagClass(this.dialCodeMapping[d].iso2);
39710             this.setDialCode(d);
39711             this.inputEl().dom.value = v.replace('+'+d,'');
39712             this.hiddenEl().dom.value = this.getValue();
39713             
39714             this.validate();
39715         },
39716         
39717         getDialCode : function(v = '')
39718         {
39719             if (v.length == 0) {
39720                 return this.dialCodeHolder.dom.value;
39721             }
39722             
39723             var dialCode = "";
39724             if (v.charAt(0) != "+") {
39725                 return false;
39726             }
39727             var numericChars = "";
39728             for (var i = 1; i < v.length; i++) {
39729               var c = v.charAt(i);
39730               if (!isNaN(c)) {
39731                 numericChars += c;
39732                 if (this.dialCodeMapping[numericChars]) {
39733                   dialCode = v.substr(1, i);
39734                 }
39735                 if (numericChars.length == 4) {
39736                   break;
39737                 }
39738               }
39739             }
39740             return dialCode;
39741         },
39742         
39743         reset : function()
39744         {
39745             this.setValue(this.defaultDialCode);
39746             this.validate();
39747         },
39748         
39749         hiddenEl : function()
39750         {
39751             return this.el.select('input.hidden-tel-input',true).first();
39752         },
39753         
39754         onKeyUp : function(e){
39755             
39756             var k = e.getKey();
39757             var c = e.getCharCode();
39758             
39759             if(
39760                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39761                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39762             ){
39763                 e.stopEvent();
39764             }
39765             
39766             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39767             //     return;
39768             // }
39769             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39770                 e.stopEvent();
39771             }
39772             
39773             this.setValue(this.getValue());
39774         }
39775         
39776 });
39777 /**
39778  * @class Roo.bootstrap.MoneyField
39779  * @extends Roo.bootstrap.ComboBox
39780  * Bootstrap MoneyField class
39781  * 
39782  * @constructor
39783  * Create a new MoneyField.
39784  * @param {Object} config Configuration options
39785  */
39786
39787 Roo.bootstrap.MoneyField = function(config) {
39788     
39789     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39790     
39791 };
39792
39793 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39794     
39795     /**
39796      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39797      */
39798     allowDecimals : true,
39799     /**
39800      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39801      */
39802     decimalSeparator : ".",
39803     /**
39804      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39805      */
39806     decimalPrecision : 2,
39807     /**
39808      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39809      */
39810     allowNegative : true,
39811     /**
39812      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39813      */
39814     minValue : Number.NEGATIVE_INFINITY,
39815     /**
39816      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39817      */
39818     maxValue : Number.MAX_VALUE,
39819     /**
39820      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39821      */
39822     minText : "The minimum value for this field is {0}",
39823     /**
39824      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39825      */
39826     maxText : "The maximum value for this field is {0}",
39827     /**
39828      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39829      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39830      */
39831     nanText : "{0} is not a valid number",
39832     /**
39833      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39834      */
39835     castInt : true,
39836     
39837     inputlg : 9,
39838     inputmd : 9,
39839     inputsm : 9,
39840     inputxs : 6,
39841     
39842     store : false,
39843     
39844     getAutoCreate : function()
39845     {
39846         var align = this.labelAlign || this.parentLabelAlign();
39847         
39848         var id = Roo.id();
39849
39850         var cfg = {
39851             cls: 'form-group',
39852             cn: []
39853         };
39854
39855         var input =  {
39856             tag: 'input',
39857             id : id,
39858             cls : 'form-control roo-money-amount-input',
39859             autocomplete: 'new-password'
39860         };
39861         
39862         if (this.name) {
39863             input.name = this.name;
39864         }
39865
39866         if (this.disabled) {
39867             input.disabled = true;
39868         }
39869
39870         var clg = 12 - this.inputlg;
39871         var cmd = 12 - this.inputmd;
39872         var csm = 12 - this.inputsm;
39873         var cxs = 12 - this.inputxs;
39874         
39875         var container = {
39876             tag : 'div',
39877             cls : 'row roo-money-field',
39878             cn : [
39879                 {
39880                     tag : 'div',
39881                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
39882                     cn : [
39883                         {
39884                             tag : 'div',
39885                             cls: 'roo-select2-container input-group',
39886                             cn: [
39887                                 {
39888                                     tag : 'input',
39889                                     cls : 'form-control roo-money-currency-input',
39890                                     autocomplete: 'new-password'
39891                                 },
39892                                 {
39893                                     tag :'span',
39894                                     cls : 'input-group-addon',
39895                                     cn : [
39896                                         {
39897                                             tag: 'span',
39898                                             cls: 'caret'
39899                                         }
39900                                     ]
39901                                 }
39902                             ]
39903                         }
39904                     ]
39905                 },
39906                 {
39907                     tag : 'div',
39908                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
39909                     cn : [
39910                         {
39911                             tag: 'div',
39912                             cls: this.hasFeedback ? 'has-feedback' : '',
39913                             cn: [
39914                                 input
39915                             ]
39916                         }
39917                     ]
39918                 }
39919             ]
39920             
39921         };
39922         
39923         if (this.fieldLabel.length) {
39924             var indicator = {
39925                 tag: 'i',
39926                 tooltip: 'This field is required'
39927             };
39928
39929             var label = {
39930                 tag: 'label',
39931                 'for':  id,
39932                 cls: 'control-label',
39933                 cn: []
39934             };
39935
39936             var label_text = {
39937                 tag: 'span',
39938                 html: this.fieldLabel
39939             };
39940
39941             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39942             label.cn = [
39943                 indicator,
39944                 label_text
39945             ];
39946
39947             if(this.indicatorpos == 'right') {
39948                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39949                 label.cn = [
39950                     label_text,
39951                     indicator
39952                 ];
39953             }
39954
39955             if(align == 'left') {
39956                 container = {
39957                     tag: 'div',
39958                     cn: [
39959                         container
39960                     ]
39961                 };
39962
39963                 if(this.labelWidth > 12){
39964                     label.style = "width: " + this.labelWidth + 'px';
39965                 }
39966                 if(this.labelWidth < 13 && this.labelmd == 0){
39967                     this.labelmd = this.labelWidth;
39968                 }
39969                 if(this.labellg > 0){
39970                     label.cls += ' col-lg-' + this.labellg;
39971                     input.cls += ' col-lg-' + (12 - this.labellg);
39972                 }
39973                 if(this.labelmd > 0){
39974                     label.cls += ' col-md-' + this.labelmd;
39975                     container.cls += ' col-md-' + (12 - this.labelmd);
39976                 }
39977                 if(this.labelsm > 0){
39978                     label.cls += ' col-sm-' + this.labelsm;
39979                     container.cls += ' col-sm-' + (12 - this.labelsm);
39980                 }
39981                 if(this.labelxs > 0){
39982                     label.cls += ' col-xs-' + this.labelxs;
39983                     container.cls += ' col-xs-' + (12 - this.labelxs);
39984                 }
39985             }
39986         }
39987
39988         cfg.cn = [
39989             label,
39990             container
39991         ];
39992
39993         var settings = this;
39994
39995         ['xs','sm','md','lg'].map(function(size){
39996             if (settings[size]) {
39997                 cfg.cls += ' col-' + size + '-' + settings[size];
39998             }
39999         });
40000         
40001         return cfg;
40002         
40003     },
40004     
40005     initEvents : function()
40006     {
40007         this.indicator = this.indicatorEl();
40008         
40009         this.initCurrencyEvent();
40010         
40011         this.initNumberEvent();
40012         
40013     },
40014     
40015     initCurrencyEvent : function()
40016     {
40017         if (!this.store) {
40018             throw "can not find store for combo";
40019         }
40020         
40021         this.store = Roo.factory(this.store, Roo.data);
40022         this.store.parent = this;
40023         
40024         this.createList();
40025         
40026         this.triggerEl = this.el.select('.input-group-addon', true).first();
40027         
40028         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40029         
40030         var _this = this;
40031         
40032         (function(){
40033             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40034             _this.list.setWidth(lw);
40035         }).defer(100);
40036         
40037         this.list.on('mouseover', this.onViewOver, this);
40038         this.list.on('mousemove', this.onViewMove, this);
40039         this.list.on('scroll', this.onViewScroll, this);
40040         
40041         if(!this.tpl){
40042             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40043         }
40044         
40045         this.view = new Roo.View(this.list, this.tpl, {
40046             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40047         });
40048         
40049         this.view.on('click', this.onViewClick, this);
40050         
40051         this.store.on('beforeload', this.onBeforeLoad, this);
40052         this.store.on('load', this.onLoad, this);
40053         this.store.on('loadexception', this.onLoadException, this);
40054         
40055         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40056             "up" : function(e){
40057                 this.inKeyMode = true;
40058                 this.selectPrev();
40059             },
40060
40061             "down" : function(e){
40062                 if(!this.isExpanded()){
40063                     this.onTriggerClick();
40064                 }else{
40065                     this.inKeyMode = true;
40066                     this.selectNext();
40067                 }
40068             },
40069
40070             "enter" : function(e){
40071                 this.collapse();
40072                 
40073                 if(this.fireEvent("specialkey", this, e)){
40074                     this.onViewClick(false);
40075                 }
40076                 
40077                 return true;
40078             },
40079
40080             "esc" : function(e){
40081                 this.collapse();
40082             },
40083
40084             "tab" : function(e){
40085                 this.collapse();
40086                 
40087                 if(this.fireEvent("specialkey", this, e)){
40088                     this.onViewClick(false);
40089                 }
40090                 
40091                 return true;
40092             },
40093
40094             scope : this,
40095
40096             doRelay : function(foo, bar, hname){
40097                 if(hname == 'down' || this.scope.isExpanded()){
40098                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40099                 }
40100                 return true;
40101             },
40102
40103             forceKeyDown: true
40104         });
40105         
40106         this.queryDelay = Math.max(this.queryDelay || 10,
40107                 this.mode == 'local' ? 10 : 250);
40108         
40109         
40110         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
40111         
40112         if(this.typeAhead){
40113             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
40114         }
40115         
40116         this.currencyEl().on("keyup", this.onCurrencyKeyUp, this);
40117         
40118     },
40119     
40120     initNumberEvent : function(e)
40121     {
40122         this.inputEl().on("keydown" , this.fireKey,  this);
40123         this.inputEl().on("focus", this.onFocus,  this);
40124         this.inputEl().on("blur", this.onBlur,  this);
40125         
40126         this.inputEl().relayEvent('keyup', this);
40127         
40128         if(this.indicator){
40129             this.indicator.addClass('invisible');
40130         }
40131  
40132         this.originalValue = this.getValue();
40133         
40134         if(this.validationEvent == 'keyup'){
40135             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40136             this.inputEl().on('keyup', this.filterValidation, this);
40137         }
40138         else if(this.validationEvent !== false){
40139             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40140         }
40141         
40142         if(this.selectOnFocus){
40143             this.on("focus", this.preFocus, this);
40144             
40145         }
40146         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40147             this.inputEl().on("keypress", this.filterKeys, this);
40148         } else {
40149             this.inputEl().relayEvent('keypress', this);
40150         }
40151         
40152         var allowed = "0123456789";
40153         
40154         if(this.allowDecimals){
40155             allowed += this.decimalSeparator;
40156         }
40157         
40158         if(this.allowNegative){
40159             allowed += "-";
40160         }
40161         
40162         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40163         
40164         var keyPress = function(e){
40165             
40166             var k = e.getKey();
40167             
40168             var c = e.getCharCode();
40169             
40170             if(
40171                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40172                     allowed.indexOf(String.fromCharCode(c)) === -1
40173             ){
40174                 e.stopEvent();
40175                 return;
40176             }
40177             
40178             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40179                 return;
40180             }
40181             
40182             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40183                 e.stopEvent();
40184             }
40185         };
40186         
40187         this.inputEl().on("keypress", keyPress, this);
40188         
40189     },
40190     
40191     onTriggerClick : function(e)
40192     {   
40193         if(this.disabled){
40194             return;
40195         }
40196         
40197         this.page = 0;
40198         this.loadNext = false;
40199         
40200         if(this.isExpanded()){
40201             this.collapse();
40202             return;
40203         }
40204         
40205         this.hasFocus = true;
40206         
40207         if(this.triggerAction == 'all') {
40208             this.doQuery(this.allQuery, true);
40209             return;
40210         }
40211         
40212         this.doQuery(this.getRawValue());
40213     },
40214     
40215     getCurrency : function()
40216     {   
40217         var v = this.currencyEl().getValue();
40218         
40219         return v;
40220     },
40221     
40222     restrictHeight : function()
40223     {
40224         this.list.alignTo(this.currencyEl(), this.listAlign);
40225         this.list.alignTo(this.currencyEl(), this.listAlign);
40226     },
40227     
40228     onViewClick : function(view, doFocus, el, e)
40229     {
40230         var index = this.view.getSelectedIndexes()[0];
40231         
40232         var r = this.store.getAt(index);
40233         
40234         if(r){
40235             this.onSelect(r, index);
40236         }
40237     },
40238     
40239     onSelect : function(record, index){
40240         
40241         if(this.fireEvent('beforeselect', this, record, index) !== false){
40242         
40243             this.setFromCurrencyData(index > -1 ? record.data : false);
40244             
40245             this.collapse();
40246             
40247             this.fireEvent('select', this, record, index);
40248         }
40249     },
40250     
40251     setFromCurrencyData : function(o)
40252     {
40253         var currency = '';
40254         
40255         this.lastCurrency = o;
40256         
40257         if (this.currencyField) {
40258             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40259         } else {
40260             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40261         }
40262         
40263         this.lastSelectionText = currency;
40264         
40265         this.setCurrency(currency);
40266     },
40267     
40268     setFromData : function(o)
40269     {
40270         var c = {};
40271         
40272         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40273         
40274         this.setFromCurrencyData(c);
40275         
40276         var value = '';
40277         
40278         if (this.name) {
40279             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40280         } else {
40281             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40282         }
40283         
40284         this.setValue(value);
40285         
40286     },
40287     
40288     setCurrency : function(v)
40289     {   
40290         this.currencyValue = v;
40291         
40292         if(this.rendered){
40293             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40294             this.validate();
40295         }
40296     },
40297     
40298     setValue : function(v)
40299     {
40300         v = this.fixPrecision(v);
40301         
40302         v = String(v).replace(".", this.decimalSeparator);
40303         
40304         this.value = v;
40305         
40306         if(this.rendered){
40307             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40308             this.validate();
40309         }
40310     },
40311     
40312     getRawValue : function()
40313     {
40314         var v = this.inputEl().getValue();
40315         
40316         return v;
40317     },
40318     
40319     getValue : function()
40320     {
40321         return this.fixPrecision(this.parseValue(this.getRawValue()));
40322     },
40323     
40324     parseValue : function(value)
40325     {
40326         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40327         return isNaN(value) ? '' : value;
40328     },
40329     
40330     fixPrecision : function(value)
40331     {
40332         var nan = isNaN(value);
40333         
40334         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40335             return nan ? '' : value;
40336         }
40337         
40338         return parseFloat(value).toFixed(this.decimalPrecision);
40339     },
40340     
40341     decimalPrecisionFcn : function(v)
40342     {
40343         return Math.floor(v);
40344     },
40345     
40346     validateValue : function(value)
40347     {
40348         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40349             return false;
40350         }
40351         
40352         var num = this.parseValue(value);
40353         
40354         if(isNaN(num)){
40355             this.markInvalid(String.format(this.nanText, value));
40356             return false;
40357         }
40358         
40359         if(num < this.minValue){
40360             this.markInvalid(String.format(this.minText, this.minValue));
40361             return false;
40362         }
40363         
40364         if(num > this.maxValue){
40365             this.markInvalid(String.format(this.maxText, this.maxValue));
40366             return false;
40367         }
40368         
40369         return true;
40370     },
40371     
40372     validate : function()
40373     {
40374         if(this.disabled){
40375             this.markValid();
40376             return true;
40377         }
40378         
40379         var currency = this.getCurrency();
40380         
40381         if(this.validateValue(this.getRawValue()) && currency.length){
40382             this.markValid();
40383             return true;
40384         }
40385         
40386         this.markInvalid();
40387         return false;
40388     },
40389     
40390     getName: function()
40391     {
40392         return this.name;
40393     },
40394     
40395     beforeBlur : function()
40396     {
40397         if(!this.castInt){
40398             return;
40399         }
40400         
40401         var v = this.parseValue(this.getRawValue());
40402         
40403         if(v){
40404             this.setValue(v);
40405         }
40406     },
40407     
40408     onBlur : function()
40409     {
40410         this.beforeBlur();
40411         
40412         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40413             //this.el.removeClass(this.focusClass);
40414         }
40415         
40416         this.hasFocus = false;
40417         
40418         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40419             this.validate();
40420         }
40421         
40422         var v = this.getValue();
40423         
40424         if(String(v) !== String(this.startValue)){
40425             this.fireEvent('change', this, v, this.startValue);
40426         }
40427         
40428         this.fireEvent("blur", this);
40429     },
40430     
40431     onCurrencyKeyUp : function(e)
40432     {
40433         if(!e.isSpecialKey()){
40434             this.lastKey = e.getKey();
40435             this.dqTask.delay(this.queryDelay);
40436         }
40437     },
40438     
40439     inputEl : function()
40440     {
40441         return this.el.select('.roo-money-amount-input', true).first();
40442     },
40443     
40444     currencyEl : function()
40445     {
40446         return this.el.select('.roo-money-currency-input', true).first();
40447     },
40448     
40449     initQuery : function()
40450     {
40451         var v = this.getCurrency();
40452         
40453         this.doQuery(v);
40454     },
40455     
40456     onTypeAhead : function()
40457     {
40458         if(this.store.getCount() > 0){
40459             var r = this.store.getAt(0);
40460             var newValue = r.data[this.currencyField];
40461             var len = newValue.length;
40462             var selStart = this.getCurrency().length;
40463             
40464             if(selStart != len){
40465                 this.setCurrency(newValue);
40466                 this.selectText(selStart, newValue.length);
40467             }
40468         }
40469     },
40470     
40471     selectText : function(start, end)
40472     {
40473         var v = this.getCurrency();
40474         
40475         if(v.length > 0){
40476             start = start === undefined ? 0 : start;
40477             end = end === undefined ? v.length : end;
40478             var d = this.el.dom;
40479             if(d.setSelectionRange){
40480                 d.setSelectionRange(start, end);
40481             }else if(d.createTextRange){
40482                 var range = d.createTextRange();
40483                 range.moveStart("character", start);
40484                 range.moveEnd("character", v.length-end);
40485                 range.select();
40486             }
40487         }
40488     }
40489     
40490 });