roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  * 
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  * 
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394     Roo.bootstrap.Body.superclass.constructor.call(this, config);
395     this.el = Roo.get(document.body);
396     if (this.cls && this.cls.length) {
397         Roo.get(document.body).addClass(this.cls);
398     }
399 };
400
401 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
402     
403     is_body : true,// just to make sure it's constructed?
404     
405         autoCreate : {
406         cls: 'container'
407     },
408     onRender : function(ct, position)
409     {
410        /* Roo.log("Roo.bootstrap.Body - onRender");
411         if (this.cls && this.cls.length) {
412             Roo.get(document.body).addClass(this.cls);
413         }
414         // style??? xttr???
415         */
416     }
417     
418     
419  
420    
421 });
422
423  /*
424  * - LGPL
425  *
426  * button group
427  * 
428  */
429
430
431 /**
432  * @class Roo.bootstrap.ButtonGroup
433  * @extends Roo.bootstrap.Component
434  * Bootstrap ButtonGroup class
435  * @cfg {String} size lg | sm | xs (default empty normal)
436  * @cfg {String} align vertical | justified  (default none)
437  * @cfg {String} direction up | down (default down)
438  * @cfg {Boolean} toolbar false | true
439  * @cfg {Boolean} btn true | false
440  * 
441  * 
442  * @constructor
443  * Create a new Input
444  * @param {Object} config The config object
445  */
446
447 Roo.bootstrap.ButtonGroup = function(config){
448     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
449 };
450
451 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
452     
453     size: '',
454     align: '',
455     direction: '',
456     toolbar: false,
457     btn: true,
458
459     getAutoCreate : function(){
460         var cfg = {
461             cls: 'btn-group',
462             html : null
463         };
464         
465         cfg.html = this.html || cfg.html;
466         
467         if (this.toolbar) {
468             cfg = {
469                 cls: 'btn-toolbar',
470                 html: null
471             };
472             
473             return cfg;
474         }
475         
476         if (['vertical','justified'].indexOf(this.align)!==-1) {
477             cfg.cls = 'btn-group-' + this.align;
478             
479             if (this.align == 'justified') {
480                 console.log(this.items);
481             }
482         }
483         
484         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
485             cfg.cls += ' btn-group-' + this.size;
486         }
487         
488         if (this.direction == 'up') {
489             cfg.cls += ' dropup' ;
490         }
491         
492         return cfg;
493     }
494    
495 });
496
497  /*
498  * - LGPL
499  *
500  * button
501  * 
502  */
503
504 /**
505  * @class Roo.bootstrap.Button
506  * @extends Roo.bootstrap.Component
507  * Bootstrap Button class
508  * @cfg {String} html The button content
509  * @cfg {String} weight (  primary | success | info | warning | danger | link ) default 
510  * @cfg {String} size ( lg | sm | xs)
511  * @cfg {String} tag ( a | input | submit)
512  * @cfg {String} href empty or href
513  * @cfg {Boolean} disabled default false;
514  * @cfg {Boolean} isClose default false;
515  * @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)
516  * @cfg {String} badge text for badge
517  * @cfg {String} theme default 
518  * @cfg {Boolean} inverse 
519  * @cfg {Boolean} toggle 
520  * @cfg {String} ontext text for on toggle state
521  * @cfg {String} offtext text for off toggle state
522  * @cfg {Boolean} defaulton 
523  * @cfg {Boolean} preventDefault  default true
524  * @cfg {Boolean} removeClass remove the standard class..
525  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
526  * 
527  * @constructor
528  * Create a new button
529  * @param {Object} config The config object
530  */
531
532
533 Roo.bootstrap.Button = function(config){
534     Roo.bootstrap.Button.superclass.constructor.call(this, config);
535     this.addEvents({
536         // raw events
537         /**
538          * @event click
539          * When a butotn is pressed
540          * @param {Roo.bootstrap.Button} this
541          * @param {Roo.EventObject} e
542          */
543         "click" : true,
544          /**
545          * @event toggle
546          * After the button has been toggles
547          * @param {Roo.EventObject} e
548          * @param {boolean} pressed (also available as button.pressed)
549          */
550         "toggle" : true
551     });
552 };
553
554 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
555     html: false,
556     active: false,
557     weight: '',
558     size: '',
559     tag: 'button',
560     href: '',
561     disabled: false,
562     isClose: false,
563     glyphicon: '',
564     badge: '',
565     theme: 'default',
566     inverse: false,
567     
568     toggle: false,
569     ontext: 'ON',
570     offtext: 'OFF',
571     defaulton: true,
572     preventDefault: true,
573     removeClass: false,
574     name: false,
575     target: false,
576     
577     
578     pressed : null,
579      
580     
581     getAutoCreate : function(){
582         
583         var cfg = {
584             tag : 'button',
585             cls : 'roo-button',
586             html: ''
587         };
588         
589         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
590             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
591             this.tag = 'button';
592         } else {
593             cfg.tag = this.tag;
594         }
595         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
596         
597         if (this.toggle == true) {
598             cfg={
599                 tag: 'div',
600                 cls: 'slider-frame roo-button',
601                 cn: [
602                     {
603                         tag: 'span',
604                         'data-on-text':'ON',
605                         'data-off-text':'OFF',
606                         cls: 'slider-button',
607                         html: this.offtext
608                     }
609                 ]
610             };
611             
612             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
613                 cfg.cls += ' '+this.weight;
614             }
615             
616             return cfg;
617         }
618         
619         if (this.isClose) {
620             cfg.cls += ' close';
621             
622             cfg["aria-hidden"] = true;
623             
624             cfg.html = "&times;";
625             
626             return cfg;
627         }
628         
629          
630         if (this.theme==='default') {
631             cfg.cls = 'btn roo-button';
632             
633             //if (this.parentType != 'Navbar') {
634             this.weight = this.weight.length ?  this.weight : 'default';
635             //}
636             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
637                 
638                 cfg.cls += ' btn-' + this.weight;
639             }
640         } else if (this.theme==='glow') {
641             
642             cfg.tag = 'a';
643             cfg.cls = 'btn-glow roo-button';
644             
645             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
646                 
647                 cfg.cls += ' ' + this.weight;
648             }
649         }
650    
651         
652         if (this.inverse) {
653             this.cls += ' inverse';
654         }
655         
656         
657         if (this.active) {
658             cfg.cls += ' active';
659         }
660         
661         if (this.disabled) {
662             cfg.disabled = 'disabled';
663         }
664         
665         if (this.items) {
666             Roo.log('changing to ul' );
667             cfg.tag = 'ul';
668             this.glyphicon = 'caret';
669         }
670         
671         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
672          
673         //gsRoo.log(this.parentType);
674         if (this.parentType === 'Navbar' && !this.parent().bar) {
675             Roo.log('changing to li?');
676             
677             cfg.tag = 'li';
678             
679             cfg.cls = '';
680             cfg.cn =  [{
681                 tag : 'a',
682                 cls : 'roo-button',
683                 html : this.html,
684                 href : this.href || '#'
685             }];
686             if (this.menu) {
687                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
688                 cfg.cls += ' dropdown';
689             }   
690             
691             delete cfg.html;
692             
693         }
694         
695        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
696         
697         if (this.glyphicon) {
698             cfg.html = ' ' + cfg.html;
699             
700             cfg.cn = [
701                 {
702                     tag: 'span',
703                     cls: 'glyphicon glyphicon-' + this.glyphicon
704                 }
705             ];
706         }
707         
708         if (this.badge) {
709             cfg.html += ' ';
710             
711             cfg.tag = 'a';
712             
713 //            cfg.cls='btn roo-button';
714             
715             cfg.href=this.href;
716             
717             var value = cfg.html;
718             
719             if(this.glyphicon){
720                 value = {
721                             tag: 'span',
722                             cls: 'glyphicon glyphicon-' + this.glyphicon,
723                             html: this.html
724                         };
725                 
726             }
727             
728             cfg.cn = [
729                 value,
730                 {
731                     tag: 'span',
732                     cls: 'badge',
733                     html: this.badge
734                 }
735             ];
736             
737             cfg.html='';
738         }
739         
740         if (this.menu) {
741             cfg.cls += ' dropdown';
742             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
743         }
744         
745         if (cfg.tag !== 'a' && this.href !== '') {
746             throw "Tag must be a to set href.";
747         } else if (this.href.length > 0) {
748             cfg.href = this.href;
749         }
750         
751         if(this.removeClass){
752             cfg.cls = '';
753         }
754         
755         if(this.target){
756             cfg.target = this.target;
757         }
758         
759         return cfg;
760     },
761     initEvents: function() {
762        // Roo.log('init events?');
763 //        Roo.log(this.el.dom);
764         // add the menu...
765         
766         if (typeof (this.menu) != 'undefined') {
767             this.menu.parentType = this.xtype;
768             this.menu.triggerEl = this.el;
769             this.addxtype(Roo.apply({}, this.menu));
770         }
771
772
773        if (this.el.hasClass('roo-button')) {
774             this.el.on('click', this.onClick, this);
775        } else {
776             this.el.select('.roo-button').on('click', this.onClick, this);
777        }
778        
779        if(this.removeClass){
780            this.el.on('click', this.onClick, this);
781        }
782        
783        this.el.enableDisplayMode();
784         
785     },
786     onClick : function(e)
787     {
788         if (this.disabled) {
789             return;
790         }
791         
792         
793         Roo.log('button on click ');
794         if(this.preventDefault){
795             e.preventDefault();
796         }
797         if (this.pressed === true || this.pressed === false) {
798             this.pressed = !this.pressed;
799             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
800             this.fireEvent('toggle', this, e, this.pressed);
801         }
802         
803         
804         this.fireEvent('click', this, e);
805     },
806     
807     /**
808      * Enables this button
809      */
810     enable : function()
811     {
812         this.disabled = false;
813         this.el.removeClass('disabled');
814     },
815     
816     /**
817      * Disable this button
818      */
819     disable : function()
820     {
821         this.disabled = true;
822         this.el.addClass('disabled');
823     },
824      /**
825      * sets the active state on/off, 
826      * @param {Boolean} state (optional) Force a particular state
827      */
828     setActive : function(v) {
829         
830         this.el[v ? 'addClass' : 'removeClass']('active');
831     },
832      /**
833      * toggles the current active state 
834      */
835     toggleActive : function()
836     {
837        var active = this.el.hasClass('active');
838        this.setActive(!active);
839        
840         
841     },
842     setText : function(str)
843     {
844         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
845     },
846     getText : function()
847     {
848         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
849     },
850     hide: function() {
851        
852      
853         this.el.hide();   
854     },
855     show: function() {
856        
857         this.el.show();   
858     }
859     
860     
861 });
862
863  /*
864  * - LGPL
865  *
866  * column
867  * 
868  */
869
870 /**
871  * @class Roo.bootstrap.Column
872  * @extends Roo.bootstrap.Component
873  * Bootstrap Column class
874  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
875  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
876  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
877  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
878  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
879  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
880  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
881  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
882  *
883  * 
884  * @cfg {Boolean} hidden (true|false) hide the element
885  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
886  * @cfg {String} fa (ban|check|...) font awesome icon
887  * @cfg {Number} fasize (1|2|....) font awsome size
888
889  * @cfg {String} icon (info-sign|check|...) glyphicon name
890
891  * @cfg {String} html content of column.
892  * 
893  * @constructor
894  * Create a new Column
895  * @param {Object} config The config object
896  */
897
898 Roo.bootstrap.Column = function(config){
899     Roo.bootstrap.Column.superclass.constructor.call(this, config);
900 };
901
902 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
903     
904     xs: false,
905     sm: false,
906     md: false,
907     lg: false,
908     xsoff: false,
909     smoff: false,
910     mdoff: false,
911     lgoff: false,
912     html: '',
913     offset: 0,
914     alert: false,
915     fa: false,
916     icon : false,
917     hidden : false,
918     fasize : 1,
919     
920     getAutoCreate : function(){
921         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
922         
923         cfg = {
924             tag: 'div',
925             cls: 'column'
926         };
927         
928         var settings=this;
929         ['xs','sm','md','lg'].map(function(size){
930             //Roo.log( size + ':' + settings[size]);
931             
932             if (settings[size+'off'] !== false) {
933                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
934             }
935             
936             if (settings[size] === false) {
937                 return;
938             }
939             
940             if (!settings[size]) { // 0 = hidden
941                 cfg.cls += ' hidden-' + size;
942                 return;
943             }
944             cfg.cls += ' col-' + size + '-' + settings[size];
945             
946         });
947         
948         if (this.hidden) {
949             cfg.cls += ' hidden';
950         }
951         
952         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
953             cfg.cls +=' alert alert-' + this.alert;
954         }
955         
956         
957         if (this.html.length) {
958             cfg.html = this.html;
959         }
960         if (this.fa) {
961             var fasize = '';
962             if (this.fasize > 1) {
963                 fasize = ' fa-' + this.fasize + 'x';
964             }
965             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
966             
967             
968         }
969         if (this.icon) {
970             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
971         }
972         
973         return cfg;
974     }
975    
976 });
977
978  
979
980  /*
981  * - LGPL
982  *
983  * page container.
984  * 
985  */
986
987
988 /**
989  * @class Roo.bootstrap.Container
990  * @extends Roo.bootstrap.Component
991  * Bootstrap Container class
992  * @cfg {Boolean} jumbotron is it a jumbotron element
993  * @cfg {String} html content of element
994  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
995  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
996  * @cfg {String} header content of header (for panel)
997  * @cfg {String} footer content of footer (for panel)
998  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
999  * @cfg {String} tag (header|aside|section) type of HTML tag.
1000  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1001  * @cfg {String} fa font awesome icon
1002  * @cfg {String} icon (info-sign|check|...) glyphicon name
1003  * @cfg {Boolean} hidden (true|false) hide the element
1004  * @cfg {Boolean} expandable (true|false) default false
1005  * @cfg {Boolean} expanded (true|false) default true
1006  * @cfg {String} rheader contet on the right of header
1007  * @cfg {Boolean} clickable (true|false) default false
1008
1009  *     
1010  * @constructor
1011  * Create a new Container
1012  * @param {Object} config The config object
1013  */
1014
1015 Roo.bootstrap.Container = function(config){
1016     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1017     
1018     this.addEvents({
1019         // raw events
1020          /**
1021          * @event expand
1022          * After the panel has been expand
1023          * 
1024          * @param {Roo.bootstrap.Container} this
1025          */
1026         "expand" : true,
1027         /**
1028          * @event collapse
1029          * After the panel has been collapsed
1030          * 
1031          * @param {Roo.bootstrap.Container} this
1032          */
1033         "collapse" : true,
1034         /**
1035          * @event click
1036          * When a element is chick
1037          * @param {Roo.bootstrap.Container} this
1038          * @param {Roo.EventObject} e
1039          */
1040         "click" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1045     
1046     jumbotron : false,
1047     well: '',
1048     panel : '',
1049     header: '',
1050     footer : '',
1051     sticky: '',
1052     tag : false,
1053     alert : false,
1054     fa: false,
1055     icon : false,
1056     expandable : false,
1057     rheader : '',
1058     expanded : true,
1059     clickable: false,
1060   
1061      
1062     getChildContainer : function() {
1063         
1064         if(!this.el){
1065             return false;
1066         }
1067         
1068         if (this.panel.length) {
1069             return this.el.select('.panel-body',true).first();
1070         }
1071         
1072         return this.el;
1073     },
1074     
1075     
1076     getAutoCreate : function(){
1077         
1078         var cfg = {
1079             tag : this.tag || 'div',
1080             html : '',
1081             cls : ''
1082         };
1083         if (this.jumbotron) {
1084             cfg.cls = 'jumbotron';
1085         }
1086         
1087         
1088         
1089         // - this is applied by the parent..
1090         //if (this.cls) {
1091         //    cfg.cls = this.cls + '';
1092         //}
1093         
1094         if (this.sticky.length) {
1095             
1096             var bd = Roo.get(document.body);
1097             if (!bd.hasClass('bootstrap-sticky')) {
1098                 bd.addClass('bootstrap-sticky');
1099                 Roo.select('html',true).setStyle('height', '100%');
1100             }
1101              
1102             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1103         }
1104         
1105         
1106         if (this.well.length) {
1107             switch (this.well) {
1108                 case 'lg':
1109                 case 'sm':
1110                     cfg.cls +=' well well-' +this.well;
1111                     break;
1112                 default:
1113                     cfg.cls +=' well';
1114                     break;
1115             }
1116         }
1117         
1118         if (this.hidden) {
1119             cfg.cls += ' hidden';
1120         }
1121         
1122         
1123         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1124             cfg.cls +=' alert alert-' + this.alert;
1125         }
1126         
1127         var body = cfg;
1128         
1129         if (this.panel.length) {
1130             cfg.cls += ' panel panel-' + this.panel;
1131             cfg.cn = [];
1132             if (this.header.length) {
1133                 
1134                 var h = [];
1135                 
1136                 if(this.expandable){
1137                     
1138                     cfg.cls = cfg.cls + ' expandable';
1139                     
1140                     h.push({
1141                         tag: 'i',
1142                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1143                     });
1144                     
1145                 }
1146                 
1147                 h.push(
1148                     {
1149                         tag: 'span',
1150                         cls : 'panel-title',
1151                         html : (this.expandable ? '&nbsp;' : '') + this.header
1152                     },
1153                     {
1154                         tag: 'span',
1155                         cls: 'panel-header-right',
1156                         html: this.rheader
1157                     }
1158                 );
1159                 
1160                 cfg.cn.push({
1161                     cls : 'panel-heading',
1162                     style : this.expandable ? 'cursor: pointer' : '',
1163                     cn : h
1164                 });
1165                 
1166             }
1167             
1168             body = false;
1169             cfg.cn.push({
1170                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1171                 html : this.html
1172             });
1173             
1174             
1175             if (this.footer.length) {
1176                 cfg.cn.push({
1177                     cls : 'panel-footer',
1178                     html : this.footer
1179                     
1180                 });
1181             }
1182             
1183         }
1184         
1185         if (body) {
1186             body.html = this.html || cfg.html;
1187             // prefix with the icons..
1188             if (this.fa) {
1189                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1190             }
1191             if (this.icon) {
1192                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1193             }
1194             
1195             
1196         }
1197         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1198             cfg.cls =  'container';
1199         }
1200         
1201         return cfg;
1202     },
1203     
1204     initEvents: function() 
1205     {
1206         if(this.expandable){
1207             var headerEl = this.headerEl();
1208         
1209             if(headerEl){
1210                 headerEl.on('click', this.onToggleClick, this);
1211             }
1212         }
1213         
1214         if(this.clickable){
1215             this.el.on('click', this.onClick, this);
1216         }
1217         
1218     },
1219     
1220     onToggleClick : function()
1221     {
1222         var headerEl = this.headerEl();
1223         
1224         if(!headerEl){
1225             return;
1226         }
1227         
1228         if(this.expanded){
1229             this.collapse();
1230             return;
1231         }
1232         
1233         this.expand();
1234     },
1235     
1236     expand : function()
1237     {
1238         if(this.fireEvent('expand', this)) {
1239             
1240             this.expanded = true;
1241             
1242             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1243             
1244             this.el.select('.panel-body',true).first().removeClass('hide');
1245             
1246             var toggleEl = this.toggleEl();
1247
1248             if(!toggleEl){
1249                 return;
1250             }
1251
1252             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1253         }
1254         
1255     },
1256     
1257     collapse : function()
1258     {
1259         if(this.fireEvent('collapse', this)) {
1260             
1261             this.expanded = false;
1262             
1263             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1264             this.el.select('.panel-body',true).first().addClass('hide');
1265         
1266             var toggleEl = this.toggleEl();
1267
1268             if(!toggleEl){
1269                 return;
1270             }
1271
1272             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1273         }
1274     },
1275     
1276     toggleEl : function()
1277     {
1278         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1279             return;
1280         }
1281         
1282         return this.el.select('.panel-heading .fa',true).first();
1283     },
1284     
1285     headerEl : function()
1286     {
1287         if(!this.el || !this.panel.length || !this.header.length){
1288             return;
1289         }
1290         
1291         return this.el.select('.panel-heading',true).first()
1292     },
1293     
1294     titleEl : function()
1295     {
1296         if(!this.el || !this.panel.length || !this.header.length){
1297             return;
1298         }
1299         
1300         return this.el.select('.panel-title',true).first();
1301     },
1302     
1303     setTitle : function(v)
1304     {
1305         var titleEl = this.titleEl();
1306         
1307         if(!titleEl){
1308             return;
1309         }
1310         
1311         titleEl.dom.innerHTML = v;
1312     },
1313     
1314     getTitle : function()
1315     {
1316         
1317         var titleEl = this.titleEl();
1318         
1319         if(!titleEl){
1320             return '';
1321         }
1322         
1323         return titleEl.dom.innerHTML;
1324     },
1325     
1326     setRightTitle : function(v)
1327     {
1328         var t = this.el.select('.panel-header-right',true).first();
1329         
1330         if(!t){
1331             return;
1332         }
1333         
1334         t.dom.innerHTML = v;
1335     },
1336     
1337     onClick : function(e)
1338     {
1339         e.preventDefault();
1340         
1341         this.fireEvent('click', this, e);
1342     }
1343    
1344 });
1345
1346  /*
1347  * - LGPL
1348  *
1349  * image
1350  * 
1351  */
1352
1353
1354 /**
1355  * @class Roo.bootstrap.Img
1356  * @extends Roo.bootstrap.Component
1357  * Bootstrap Img class
1358  * @cfg {Boolean} imgResponsive false | true
1359  * @cfg {String} border rounded | circle | thumbnail
1360  * @cfg {String} src image source
1361  * @cfg {String} alt image alternative text
1362  * @cfg {String} href a tag href
1363  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1364  * @cfg {String} xsUrl xs image source
1365  * @cfg {String} smUrl sm image source
1366  * @cfg {String} mdUrl md image source
1367  * @cfg {String} lgUrl lg image source
1368  * 
1369  * @constructor
1370  * Create a new Input
1371  * @param {Object} config The config object
1372  */
1373
1374 Roo.bootstrap.Img = function(config){
1375     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1376     
1377     this.addEvents({
1378         // img events
1379         /**
1380          * @event click
1381          * The img click event for the img.
1382          * @param {Roo.EventObject} e
1383          */
1384         "click" : true
1385     });
1386 };
1387
1388 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1389     
1390     imgResponsive: true,
1391     border: '',
1392     src: 'about:blank',
1393     href: false,
1394     target: false,
1395     xsUrl: '',
1396     smUrl: '',
1397     mdUrl: '',
1398     lgUrl: '',
1399
1400     getAutoCreate : function()
1401     {   
1402         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1403             return this.createSingleImg();
1404         }
1405         
1406         var cfg = {
1407             tag: 'div',
1408             cls: 'roo-image-responsive-group',
1409             cn: []
1410         };
1411         var _this = this;
1412         
1413         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1414             
1415             if(!_this[size + 'Url']){
1416                 return;
1417             }
1418             
1419             var img = {
1420                 tag: 'img',
1421                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1422                 html: _this.html || cfg.html,
1423                 src: _this[size + 'Url']
1424             };
1425             
1426             img.cls += ' roo-image-responsive-' + size;
1427             
1428             var s = ['xs', 'sm', 'md', 'lg'];
1429             
1430             s.splice(s.indexOf(size), 1);
1431             
1432             Roo.each(s, function(ss){
1433                 img.cls += ' hidden-' + ss;
1434             });
1435             
1436             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1437                 cfg.cls += ' img-' + _this.border;
1438             }
1439             
1440             if(_this.alt){
1441                 cfg.alt = _this.alt;
1442             }
1443             
1444             if(_this.href){
1445                 var a = {
1446                     tag: 'a',
1447                     href: _this.href,
1448                     cn: [
1449                         img
1450                     ]
1451                 };
1452
1453                 if(this.target){
1454                     a.target = _this.target;
1455                 }
1456             }
1457             
1458             cfg.cn.push((_this.href) ? a : img);
1459             
1460         });
1461         
1462         return cfg;
1463     },
1464     
1465     createSingleImg : function()
1466     {
1467         var cfg = {
1468             tag: 'img',
1469             cls: (this.imgResponsive) ? 'img-responsive' : '',
1470             html : null,
1471             src : 'about:blank'  // just incase src get's set to undefined?!?
1472         };
1473         
1474         cfg.html = this.html || cfg.html;
1475         
1476         cfg.src = this.src || cfg.src;
1477         
1478         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1479             cfg.cls += ' img-' + this.border;
1480         }
1481         
1482         if(this.alt){
1483             cfg.alt = this.alt;
1484         }
1485         
1486         if(this.href){
1487             var a = {
1488                 tag: 'a',
1489                 href: this.href,
1490                 cn: [
1491                     cfg
1492                 ]
1493             };
1494             
1495             if(this.target){
1496                 a.target = this.target;
1497             }
1498             
1499         }
1500         
1501         return (this.href) ? a : cfg;
1502     },
1503     
1504     initEvents: function() 
1505     {
1506         if(!this.href){
1507             this.el.on('click', this.onClick, this);
1508         }
1509         
1510     },
1511     
1512     onClick : function(e)
1513     {
1514         Roo.log('img onclick');
1515         this.fireEvent('click', this, e);
1516     }
1517    
1518 });
1519
1520  /*
1521  * - LGPL
1522  *
1523  * image
1524  * 
1525  */
1526
1527
1528 /**
1529  * @class Roo.bootstrap.Link
1530  * @extends Roo.bootstrap.Component
1531  * Bootstrap Link Class
1532  * @cfg {String} alt image alternative text
1533  * @cfg {String} href a tag href
1534  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1535  * @cfg {String} html the content of the link.
1536  * @cfg {String} anchor name for the anchor link
1537  * @cfg {String} fa - favicon
1538
1539  * @cfg {Boolean} preventDefault (true | false) default false
1540
1541  * 
1542  * @constructor
1543  * Create a new Input
1544  * @param {Object} config The config object
1545  */
1546
1547 Roo.bootstrap.Link = function(config){
1548     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1549     
1550     this.addEvents({
1551         // img events
1552         /**
1553          * @event click
1554          * The img click event for the img.
1555          * @param {Roo.EventObject} e
1556          */
1557         "click" : true
1558     });
1559 };
1560
1561 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1562     
1563     href: false,
1564     target: false,
1565     preventDefault: false,
1566     anchor : false,
1567     alt : false,
1568     fa: false,
1569
1570
1571     getAutoCreate : function()
1572     {
1573         var html = this.html || '';
1574         
1575         if (this.fa !== false) {
1576             html = '<i class="fa fa-' + this.fa + '"></i>';
1577         }
1578         var cfg = {
1579             tag: 'a'
1580         };
1581         // anchor's do not require html/href...
1582         if (this.anchor === false) {
1583             cfg.html = html;
1584             cfg.href = this.href || '#';
1585         } else {
1586             cfg.name = this.anchor;
1587             if (this.html !== false || this.fa !== false) {
1588                 cfg.html = html;
1589             }
1590             if (this.href !== false) {
1591                 cfg.href = this.href;
1592             }
1593         }
1594         
1595         if(this.alt !== false){
1596             cfg.alt = this.alt;
1597         }
1598         
1599         
1600         if(this.target !== false) {
1601             cfg.target = this.target;
1602         }
1603         
1604         return cfg;
1605     },
1606     
1607     initEvents: function() {
1608         
1609         if(!this.href || this.preventDefault){
1610             this.el.on('click', this.onClick, this);
1611         }
1612     },
1613     
1614     onClick : function(e)
1615     {
1616         if(this.preventDefault){
1617             e.preventDefault();
1618         }
1619         //Roo.log('img onclick');
1620         this.fireEvent('click', this, e);
1621     }
1622    
1623 });
1624
1625  /*
1626  * - LGPL
1627  *
1628  * header
1629  * 
1630  */
1631
1632 /**
1633  * @class Roo.bootstrap.Header
1634  * @extends Roo.bootstrap.Component
1635  * Bootstrap Header class
1636  * @cfg {String} html content of header
1637  * @cfg {Number} level (1|2|3|4|5|6) default 1
1638  * 
1639  * @constructor
1640  * Create a new Header
1641  * @param {Object} config The config object
1642  */
1643
1644
1645 Roo.bootstrap.Header  = function(config){
1646     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1647 };
1648
1649 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1650     
1651     //href : false,
1652     html : false,
1653     level : 1,
1654     
1655     
1656     
1657     getAutoCreate : function(){
1658         
1659         
1660         
1661         var cfg = {
1662             tag: 'h' + (1 *this.level),
1663             html: this.html || ''
1664         } ;
1665         
1666         return cfg;
1667     }
1668    
1669 });
1670
1671  
1672
1673  /*
1674  * Based on:
1675  * Ext JS Library 1.1.1
1676  * Copyright(c) 2006-2007, Ext JS, LLC.
1677  *
1678  * Originally Released Under LGPL - original licence link has changed is not relivant.
1679  *
1680  * Fork - LGPL
1681  * <script type="text/javascript">
1682  */
1683  
1684 /**
1685  * @class Roo.bootstrap.MenuMgr
1686  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1687  * @singleton
1688  */
1689 Roo.bootstrap.MenuMgr = function(){
1690    var menus, active, groups = {}, attached = false, lastShow = new Date();
1691
1692    // private - called when first menu is created
1693    function init(){
1694        menus = {};
1695        active = new Roo.util.MixedCollection();
1696        Roo.get(document).addKeyListener(27, function(){
1697            if(active.length > 0){
1698                hideAll();
1699            }
1700        });
1701    }
1702
1703    // private
1704    function hideAll(){
1705        if(active && active.length > 0){
1706            var c = active.clone();
1707            c.each(function(m){
1708                m.hide();
1709            });
1710        }
1711    }
1712
1713    // private
1714    function onHide(m){
1715        active.remove(m);
1716        if(active.length < 1){
1717            Roo.get(document).un("mouseup", onMouseDown);
1718             
1719            attached = false;
1720        }
1721    }
1722
1723    // private
1724    function onShow(m){
1725        var last = active.last();
1726        lastShow = new Date();
1727        active.add(m);
1728        if(!attached){
1729           Roo.get(document).on("mouseup", onMouseDown);
1730            
1731            attached = true;
1732        }
1733        if(m.parentMenu){
1734           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1735           m.parentMenu.activeChild = m;
1736        }else if(last && last.isVisible()){
1737           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1738        }
1739    }
1740
1741    // private
1742    function onBeforeHide(m){
1743        if(m.activeChild){
1744            m.activeChild.hide();
1745        }
1746        if(m.autoHideTimer){
1747            clearTimeout(m.autoHideTimer);
1748            delete m.autoHideTimer;
1749        }
1750    }
1751
1752    // private
1753    function onBeforeShow(m){
1754        var pm = m.parentMenu;
1755        if(!pm && !m.allowOtherMenus){
1756            hideAll();
1757        }else if(pm && pm.activeChild && active != m){
1758            pm.activeChild.hide();
1759        }
1760    }
1761
1762    // private this should really trigger on mouseup..
1763    function onMouseDown(e){
1764         Roo.log("on Mouse Up");
1765         
1766         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1767             Roo.log("MenuManager hideAll");
1768             hideAll();
1769             e.stopEvent();
1770         }
1771         
1772         
1773    }
1774
1775    // private
1776    function onBeforeCheck(mi, state){
1777        if(state){
1778            var g = groups[mi.group];
1779            for(var i = 0, l = g.length; i < l; i++){
1780                if(g[i] != mi){
1781                    g[i].setChecked(false);
1782                }
1783            }
1784        }
1785    }
1786
1787    return {
1788
1789        /**
1790         * Hides all menus that are currently visible
1791         */
1792        hideAll : function(){
1793             hideAll();  
1794        },
1795
1796        // private
1797        register : function(menu){
1798            if(!menus){
1799                init();
1800            }
1801            menus[menu.id] = menu;
1802            menu.on("beforehide", onBeforeHide);
1803            menu.on("hide", onHide);
1804            menu.on("beforeshow", onBeforeShow);
1805            menu.on("show", onShow);
1806            var g = menu.group;
1807            if(g && menu.events["checkchange"]){
1808                if(!groups[g]){
1809                    groups[g] = [];
1810                }
1811                groups[g].push(menu);
1812                menu.on("checkchange", onCheck);
1813            }
1814        },
1815
1816         /**
1817          * Returns a {@link Roo.menu.Menu} object
1818          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1819          * be used to generate and return a new Menu instance.
1820          */
1821        get : function(menu){
1822            if(typeof menu == "string"){ // menu id
1823                return menus[menu];
1824            }else if(menu.events){  // menu instance
1825                return menu;
1826            }
1827            /*else if(typeof menu.length == 'number'){ // array of menu items?
1828                return new Roo.bootstrap.Menu({items:menu});
1829            }else{ // otherwise, must be a config
1830                return new Roo.bootstrap.Menu(menu);
1831            }
1832            */
1833            return false;
1834        },
1835
1836        // private
1837        unregister : function(menu){
1838            delete menus[menu.id];
1839            menu.un("beforehide", onBeforeHide);
1840            menu.un("hide", onHide);
1841            menu.un("beforeshow", onBeforeShow);
1842            menu.un("show", onShow);
1843            var g = menu.group;
1844            if(g && menu.events["checkchange"]){
1845                groups[g].remove(menu);
1846                menu.un("checkchange", onCheck);
1847            }
1848        },
1849
1850        // private
1851        registerCheckable : function(menuItem){
1852            var g = menuItem.group;
1853            if(g){
1854                if(!groups[g]){
1855                    groups[g] = [];
1856                }
1857                groups[g].push(menuItem);
1858                menuItem.on("beforecheckchange", onBeforeCheck);
1859            }
1860        },
1861
1862        // private
1863        unregisterCheckable : function(menuItem){
1864            var g = menuItem.group;
1865            if(g){
1866                groups[g].remove(menuItem);
1867                menuItem.un("beforecheckchange", onBeforeCheck);
1868            }
1869        }
1870    };
1871 }();/*
1872  * - LGPL
1873  *
1874  * menu
1875  * 
1876  */
1877
1878 /**
1879  * @class Roo.bootstrap.Menu
1880  * @extends Roo.bootstrap.Component
1881  * Bootstrap Menu class - container for MenuItems
1882  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1883  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1884  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1885  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1886  * 
1887  * @constructor
1888  * Create a new Menu
1889  * @param {Object} config The config object
1890  */
1891
1892
1893 Roo.bootstrap.Menu = function(config){
1894     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1895     if (this.registerMenu && this.type != 'treeview')  {
1896         Roo.bootstrap.MenuMgr.register(this);
1897     }
1898     this.addEvents({
1899         /**
1900          * @event beforeshow
1901          * Fires before this menu is displayed
1902          * @param {Roo.menu.Menu} this
1903          */
1904         beforeshow : true,
1905         /**
1906          * @event beforehide
1907          * Fires before this menu is hidden
1908          * @param {Roo.menu.Menu} this
1909          */
1910         beforehide : true,
1911         /**
1912          * @event show
1913          * Fires after this menu is displayed
1914          * @param {Roo.menu.Menu} this
1915          */
1916         show : true,
1917         /**
1918          * @event hide
1919          * Fires after this menu is hidden
1920          * @param {Roo.menu.Menu} this
1921          */
1922         hide : true,
1923         /**
1924          * @event click
1925          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1926          * @param {Roo.menu.Menu} this
1927          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1928          * @param {Roo.EventObject} e
1929          */
1930         click : true,
1931         /**
1932          * @event mouseover
1933          * Fires when the mouse is hovering over this menu
1934          * @param {Roo.menu.Menu} this
1935          * @param {Roo.EventObject} e
1936          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1937          */
1938         mouseover : true,
1939         /**
1940          * @event mouseout
1941          * Fires when the mouse exits this menu
1942          * @param {Roo.menu.Menu} this
1943          * @param {Roo.EventObject} e
1944          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1945          */
1946         mouseout : true,
1947         /**
1948          * @event itemclick
1949          * Fires when a menu item contained in this menu is clicked
1950          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1951          * @param {Roo.EventObject} e
1952          */
1953         itemclick: true
1954     });
1955     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1956 };
1957
1958 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
1959     
1960    /// html : false,
1961     //align : '',
1962     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
1963     type: false,
1964     /**
1965      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
1966      */
1967     registerMenu : true,
1968     
1969     menuItems :false, // stores the menu items..
1970     
1971     hidden:true,
1972         
1973     parentMenu : false,
1974     
1975     stopEvent : true,
1976     
1977     isLink : false,
1978     
1979     getChildContainer : function() {
1980         return this.el;  
1981     },
1982     
1983     getAutoCreate : function(){
1984          
1985         //if (['right'].indexOf(this.align)!==-1) {
1986         //    cfg.cn[1].cls += ' pull-right'
1987         //}
1988         
1989         
1990         var cfg = {
1991             tag : 'ul',
1992             cls : 'dropdown-menu' ,
1993             style : 'z-index:1000'
1994             
1995         };
1996         
1997         if (this.type === 'submenu') {
1998             cfg.cls = 'submenu active';
1999         }
2000         if (this.type === 'treeview') {
2001             cfg.cls = 'treeview-menu';
2002         }
2003         
2004         return cfg;
2005     },
2006     initEvents : function() {
2007         
2008        // Roo.log("ADD event");
2009        // Roo.log(this.triggerEl.dom);
2010         
2011         this.triggerEl.on('click', this.onTriggerClick, this);
2012         
2013         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2014         
2015         this.triggerEl.addClass('dropdown-toggle');
2016         
2017         if (Roo.isTouch) {
2018             this.el.on('touchstart'  , this.onTouch, this);
2019         }
2020         this.el.on('click' , this.onClick, this);
2021
2022         this.el.on("mouseover", this.onMouseOver, this);
2023         this.el.on("mouseout", this.onMouseOut, this);
2024         
2025     },
2026     
2027     findTargetItem : function(e)
2028     {
2029         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2030         if(!t){
2031             return false;
2032         }
2033         //Roo.log(t);         Roo.log(t.id);
2034         if(t && t.id){
2035             //Roo.log(this.menuitems);
2036             return this.menuitems.get(t.id);
2037             
2038             //return this.items.get(t.menuItemId);
2039         }
2040         
2041         return false;
2042     },
2043     
2044     onTouch : function(e) 
2045     {
2046         Roo.log("menu.onTouch");
2047         //e.stopEvent(); this make the user popdown broken
2048         this.onClick(e);
2049     },
2050     
2051     onClick : function(e)
2052     {
2053         Roo.log("menu.onClick");
2054         
2055         var t = this.findTargetItem(e);
2056         if(!t || t.isContainer){
2057             return;
2058         }
2059         Roo.log(e);
2060         /*
2061         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2062             if(t == this.activeItem && t.shouldDeactivate(e)){
2063                 this.activeItem.deactivate();
2064                 delete this.activeItem;
2065                 return;
2066             }
2067             if(t.canActivate){
2068                 this.setActiveItem(t, true);
2069             }
2070             return;
2071             
2072             
2073         }
2074         */
2075        
2076         Roo.log('pass click event');
2077         
2078         t.onClick(e);
2079         
2080         this.fireEvent("click", this, t, e);
2081         
2082         var _this = this;
2083         
2084         (function() { _this.hide(); }).defer(500);
2085     },
2086     
2087     onMouseOver : function(e){
2088         var t  = this.findTargetItem(e);
2089         //Roo.log(t);
2090         //if(t){
2091         //    if(t.canActivate && !t.disabled){
2092         //        this.setActiveItem(t, true);
2093         //    }
2094         //}
2095         
2096         this.fireEvent("mouseover", this, e, t);
2097     },
2098     isVisible : function(){
2099         return !this.hidden;
2100     },
2101      onMouseOut : function(e){
2102         var t  = this.findTargetItem(e);
2103         
2104         //if(t ){
2105         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2106         //        this.activeItem.deactivate();
2107         //        delete this.activeItem;
2108         //    }
2109         //}
2110         this.fireEvent("mouseout", this, e, t);
2111     },
2112     
2113     
2114     /**
2115      * Displays this menu relative to another element
2116      * @param {String/HTMLElement/Roo.Element} element The element to align to
2117      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2118      * the element (defaults to this.defaultAlign)
2119      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2120      */
2121     show : function(el, pos, parentMenu){
2122         this.parentMenu = parentMenu;
2123         if(!this.el){
2124             this.render();
2125         }
2126         this.fireEvent("beforeshow", this);
2127         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2128     },
2129      /**
2130      * Displays this menu at a specific xy position
2131      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2132      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2133      */
2134     showAt : function(xy, parentMenu, /* private: */_e){
2135         this.parentMenu = parentMenu;
2136         if(!this.el){
2137             this.render();
2138         }
2139         if(_e !== false){
2140             this.fireEvent("beforeshow", this);
2141             //xy = this.el.adjustForConstraints(xy);
2142         }
2143         
2144         //this.el.show();
2145         this.hideMenuItems();
2146         this.hidden = false;
2147         this.triggerEl.addClass('open');
2148         
2149         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2150             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2151         }
2152         
2153         /*
2154          * This not working on ios
2155          */
2156 //        if(this.el.getStyle('top').slice(-1) != "%"){
2157 //            this.el.setXY(xy);
2158 //        }
2159         
2160         if(!isNaN(this.el.getStyle('top') * 1)){
2161             this.el.setXY(xy);
2162         }
2163         
2164         this.focus();
2165         this.fireEvent("show", this);
2166     },
2167     
2168     focus : function(){
2169         return;
2170         if(!this.hidden){
2171             this.doFocus.defer(50, this);
2172         }
2173     },
2174
2175     doFocus : function(){
2176         if(!this.hidden){
2177             this.focusEl.focus();
2178         }
2179     },
2180
2181     /**
2182      * Hides this menu and optionally all parent menus
2183      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2184      */
2185     hide : function(deep)
2186     {
2187         
2188         this.hideMenuItems();
2189         if(this.el && this.isVisible()){
2190             this.fireEvent("beforehide", this);
2191             if(this.activeItem){
2192                 this.activeItem.deactivate();
2193                 this.activeItem = null;
2194             }
2195             this.triggerEl.removeClass('open');;
2196             this.hidden = true;
2197             this.fireEvent("hide", this);
2198         }
2199         if(deep === true && this.parentMenu){
2200             this.parentMenu.hide(true);
2201         }
2202     },
2203     
2204     onTriggerClick : function(e)
2205     {
2206         Roo.log('trigger click');
2207         
2208         var target = e.getTarget();
2209         
2210         Roo.log(target.nodeName.toLowerCase());
2211         
2212         if(target.nodeName.toLowerCase() === 'i'){
2213             e.preventDefault();
2214         }
2215         
2216     },
2217     
2218     onTriggerPress  : function(e)
2219     {
2220         Roo.log('trigger press');
2221         //Roo.log(e.getTarget());
2222        // Roo.log(this.triggerEl.dom);
2223        
2224         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2225         var pel = Roo.get(e.getTarget());
2226         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2227             Roo.log('is treeview or dropdown?');
2228             return;
2229         }
2230         
2231         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2232             return;
2233         }
2234         
2235         if (this.isVisible()) {
2236             Roo.log('hide');
2237             this.hide();
2238         } else {
2239             Roo.log('show');
2240             this.show(this.triggerEl, false, false);
2241         }
2242         
2243         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2244             e.stopEvent();
2245         }
2246         
2247     },
2248        
2249     
2250     hideMenuItems : function()
2251     {
2252         Roo.log("hide Menu Items");
2253         if (!this.el) { 
2254             return;
2255         }
2256         //$(backdrop).remove()
2257         this.el.select('.open',true).each(function(aa) {
2258             
2259             aa.removeClass('open');
2260           //var parent = getParent($(this))
2261           //var relatedTarget = { relatedTarget: this }
2262           
2263            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2264           //if (e.isDefaultPrevented()) return
2265            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2266         });
2267     },
2268     addxtypeChild : function (tree, cntr) {
2269         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2270           
2271         this.menuitems.add(comp);
2272         return comp;
2273
2274     },
2275     getEl : function()
2276     {
2277         Roo.log(this.el);
2278         return this.el;
2279     }
2280 });
2281
2282  
2283  /*
2284  * - LGPL
2285  *
2286  * menu item
2287  * 
2288  */
2289
2290
2291 /**
2292  * @class Roo.bootstrap.MenuItem
2293  * @extends Roo.bootstrap.Component
2294  * Bootstrap MenuItem class
2295  * @cfg {String} html the menu label
2296  * @cfg {String} href the link
2297  * @cfg {Boolean} preventDefault do not trigger A href on clicks.
2298  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2299  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2300  * @cfg {String} fa favicon to show on left of menu item.
2301  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2302  * 
2303  * 
2304  * @constructor
2305  * Create a new MenuItem
2306  * @param {Object} config The config object
2307  */
2308
2309
2310 Roo.bootstrap.MenuItem = function(config){
2311     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2312     this.addEvents({
2313         // raw events
2314         /**
2315          * @event click
2316          * The raw click event for the entire grid.
2317          * @param {Roo.bootstrap.MenuItem} this
2318          * @param {Roo.EventObject} e
2319          */
2320         "click" : true
2321     });
2322 };
2323
2324 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2325     
2326     href : false,
2327     html : false,
2328     preventDefault: true,
2329     isContainer : false,
2330     active : false,
2331     fa: false,
2332     
2333     getAutoCreate : function(){
2334         
2335         if(this.isContainer){
2336             return {
2337                 tag: 'li',
2338                 cls: 'dropdown-menu-item'
2339             };
2340         }
2341         var ctag = {
2342             tag: 'span',
2343             html: 'Link'
2344         };
2345         
2346         var anc = {
2347             tag : 'a',
2348             href : '#',
2349             cn : [  ]
2350         };
2351         
2352         if (this.fa !== false) {
2353             anc.cn.push({
2354                 tag : 'i',
2355                 cls : 'fa fa-' + this.fa
2356             });
2357         }
2358         
2359         anc.cn.push(ctag);
2360         
2361         
2362         var cfg= {
2363             tag: 'li',
2364             cls: 'dropdown-menu-item',
2365             cn: [ anc ]
2366         };
2367         if (this.parent().type == 'treeview') {
2368             cfg.cls = 'treeview-menu';
2369         }
2370         if (this.active) {
2371             cfg.cls += ' active';
2372         }
2373         
2374         
2375         
2376         anc.href = this.href || cfg.cn[0].href ;
2377         ctag.html = this.html || cfg.cn[0].html ;
2378         return cfg;
2379     },
2380     
2381     initEvents: function()
2382     {
2383         if (this.parent().type == 'treeview') {
2384             this.el.select('a').on('click', this.onClick, this);
2385         }
2386         if (this.menu) {
2387             this.menu.parentType = this.xtype;
2388             this.menu.triggerEl = this.el;
2389             this.menu = this.addxtype(Roo.apply({}, this.menu));
2390         }
2391         
2392     },
2393     onClick : function(e)
2394     {
2395         Roo.log('item on click ');
2396         //if(this.preventDefault){
2397         //    e.preventDefault();
2398         //}
2399         //this.parent().hideMenuItems();
2400         
2401         this.fireEvent('click', this, e);
2402     },
2403     getEl : function()
2404     {
2405         return this.el;
2406     } 
2407 });
2408
2409  
2410
2411  /*
2412  * - LGPL
2413  *
2414  * menu separator
2415  * 
2416  */
2417
2418
2419 /**
2420  * @class Roo.bootstrap.MenuSeparator
2421  * @extends Roo.bootstrap.Component
2422  * Bootstrap MenuSeparator class
2423  * 
2424  * @constructor
2425  * Create a new MenuItem
2426  * @param {Object} config The config object
2427  */
2428
2429
2430 Roo.bootstrap.MenuSeparator = function(config){
2431     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2432 };
2433
2434 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2435     
2436     getAutoCreate : function(){
2437         var cfg = {
2438             cls: 'divider',
2439             tag : 'li'
2440         };
2441         
2442         return cfg;
2443     }
2444    
2445 });
2446
2447  
2448
2449  
2450 /*
2451 * Licence: LGPL
2452 */
2453
2454 /**
2455  * @class Roo.bootstrap.Modal
2456  * @extends Roo.bootstrap.Component
2457  * Bootstrap Modal class
2458  * @cfg {String} title Title of dialog
2459  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2460  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn 
2461  * @cfg {Boolean} specificTitle default false
2462  * @cfg {Array} buttons Array of buttons or standard button set..
2463  * @cfg {String} buttonPosition (left|right|center) default right
2464  * @cfg {Boolean} animate default true
2465  * @cfg {Boolean} allow_close default true
2466  * @cfg {Boolean} fitwindow default true
2467  * 
2468  * 
2469  * @constructor
2470  * Create a new Modal Dialog
2471  * @param {Object} config The config object
2472  */
2473
2474 Roo.bootstrap.Modal = function(config){
2475     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2476     this.addEvents({
2477         // raw events
2478         /**
2479          * @event btnclick
2480          * The raw btnclick event for the button
2481          * @param {Roo.EventObject} e
2482          */
2483         "btnclick" : true
2484     });
2485     this.buttons = this.buttons || [];
2486      
2487     if (this.tmpl) {
2488         this.tmpl = Roo.factory(this.tmpl);
2489     }
2490     
2491 };
2492
2493 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2494     
2495     title : 'test dialog',
2496    
2497     buttons : false,
2498     
2499     // set on load...
2500      
2501     html: false,
2502     
2503     tmp: false,
2504     
2505     specificTitle: false,
2506     
2507     buttonPosition: 'right',
2508     
2509     allow_close : true,
2510     
2511     animate : true,
2512     
2513     fitwindow: false,
2514     
2515     
2516      // private
2517     dialogEl: false,
2518     bodyEl:  false,
2519     footerEl:  false,
2520     titleEl:  false,
2521     closeEl:  false,
2522     
2523     
2524     onRender : function(ct, position)
2525     {
2526         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2527      
2528         if(!this.el){
2529             var cfg = Roo.apply({},  this.getAutoCreate());
2530             cfg.id = Roo.id();
2531             //if(!cfg.name){
2532             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2533             //}
2534             //if (!cfg.name.length) {
2535             //    delete cfg.name;
2536            // }
2537             if (this.cls) {
2538                 cfg.cls += ' ' + this.cls;
2539             }
2540             if (this.style) {
2541                 cfg.style = this.style;
2542             }
2543             this.el = Roo.get(document.body).createChild(cfg, position);
2544         }
2545         //var type = this.el.dom.type;
2546         
2547         
2548         if(this.tabIndex !== undefined){
2549             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2550         }
2551         
2552         this.dialogEl = this.el.select('.modal-dialog',true).first();
2553         this.bodyEl = this.el.select('.modal-body',true).first();
2554         this.closeEl = this.el.select('.modal-header .close', true).first();
2555         this.footerEl = this.el.select('.modal-footer',true).first();
2556         this.titleEl = this.el.select('.modal-title',true).first();
2557         
2558         
2559          
2560         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2561         this.maskEl.enableDisplayMode("block");
2562         this.maskEl.hide();
2563         //this.el.addClass("x-dlg-modal");
2564     
2565         if (this.buttons.length) {
2566             Roo.each(this.buttons, function(bb) {
2567                 var b = Roo.apply({}, bb);
2568                 b.xns = b.xns || Roo.bootstrap;
2569                 b.xtype = b.xtype || 'Button';
2570                 if (typeof(b.listeners) == 'undefined') {
2571                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2572                 }
2573                 
2574                 var btn = Roo.factory(b);
2575                 
2576                 btn.render(this.el.select('.modal-footer div').first());
2577                 
2578             },this);
2579         }
2580         // render the children.
2581         var nitems = [];
2582         
2583         if(typeof(this.items) != 'undefined'){
2584             var items = this.items;
2585             delete this.items;
2586
2587             for(var i =0;i < items.length;i++) {
2588                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2589             }
2590         }
2591         
2592         this.items = nitems;
2593         
2594         // where are these used - they used to be body/close/footer
2595         
2596        
2597         this.initEvents();
2598         //this.el.addClass([this.fieldClass, this.cls]);
2599         
2600     },
2601     
2602     getAutoCreate : function(){
2603         
2604         
2605         var bdy = {
2606                 cls : 'modal-body',
2607                 html : this.html || ''
2608         };
2609         
2610         var title = {
2611             tag: 'h4',
2612             cls : 'modal-title',
2613             html : this.title
2614         };
2615         
2616         if(this.specificTitle){
2617             title = this.title;
2618             
2619         };
2620         
2621         var header = [];
2622         if (this.allow_close) {
2623             header.push({
2624                 tag: 'button',
2625                 cls : 'close',
2626                 html : '&times'
2627             });
2628         }
2629         header.push(title);
2630         
2631         var modal = {
2632             cls: "modal",
2633             style : 'display: none',
2634             cn : [
2635                 {
2636                     cls: "modal-dialog",
2637                     cn : [
2638                         {
2639                             cls : "modal-content",
2640                             cn : [
2641                                 {
2642                                     cls : 'modal-header',
2643                                     cn : header
2644                                 },
2645                                 bdy,
2646                                 {
2647                                     cls : 'modal-footer',
2648                                     cn : [
2649                                         {
2650                                             tag: 'div',
2651                                             cls: 'btn-' + this.buttonPosition
2652                                         }
2653                                     ]
2654                                     
2655                                 }
2656                                 
2657                                 
2658                             ]
2659                             
2660                         }
2661                     ]
2662                         
2663                 }
2664             ]
2665         };
2666         
2667         if(this.animate){
2668             modal.cls += ' fade';
2669         }
2670         
2671         return modal;
2672           
2673     },
2674     getChildContainer : function() {
2675          
2676          return this.bodyEl;
2677         
2678     },
2679     getButtonContainer : function() {
2680          return this.el.select('.modal-footer div',true).first();
2681         
2682     },
2683     initEvents : function()
2684     {
2685         if (this.allow_close) {
2686             this.closeEl.on('click', this.hide, this);
2687         }
2688         Roo.EventManager.onWindowResize(this.resize, this, true);
2689         
2690  
2691     },
2692     
2693     resize : function()
2694     {
2695         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2696         if (this.fitwindow) {
2697             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2698             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 30;
2699             this.setSize(w,h)
2700         }
2701     },
2702     
2703     setSize : function(w,h)
2704     {
2705         if (!w && !h) {
2706             return;
2707         }
2708         this.resizeTo(w,h);
2709     },
2710     
2711     show : function() {
2712         
2713         if (!this.rendered) {
2714             this.render();
2715         }
2716         
2717         this.el.setStyle('display', 'block');
2718         
2719         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2720             var _this = this;
2721             (function(){
2722                 this.el.addClass('in');
2723             }).defer(50, this);
2724         }else{
2725             this.el.addClass('in');
2726             
2727         }
2728         
2729         // not sure how we can show data in here.. 
2730         //if (this.tmpl) {
2731         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2732         //}
2733         
2734         Roo.get(document.body).addClass("x-body-masked");
2735         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2736         this.maskEl.show();
2737         this.el.setStyle('zIndex', '10001');
2738        
2739         this.fireEvent('show', this);
2740         this.items.forEach(function(e) {
2741             e.layout ? e.layout() : false;
2742                 
2743         });
2744         this.resize();
2745         
2746         
2747         
2748     },
2749     hide : function()
2750     {
2751         this.maskEl.hide();
2752         Roo.get(document.body).removeClass("x-body-masked");
2753         this.el.removeClass('in');
2754         this.el.select('.modal-dialog', true).first().setStyle('transform','');
2755         
2756         if(this.animate){ // why
2757             var _this = this;
2758             (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2759         }else{
2760             this.el.setStyle('display', 'none');
2761         }
2762         
2763         this.fireEvent('hide', this);
2764     },
2765     
2766     addButton : function(str, cb)
2767     {
2768          
2769         
2770         var b = Roo.apply({}, { html : str } );
2771         b.xns = b.xns || Roo.bootstrap;
2772         b.xtype = b.xtype || 'Button';
2773         if (typeof(b.listeners) == 'undefined') {
2774             b.listeners = { click : cb.createDelegate(this)  };
2775         }
2776         
2777         var btn = Roo.factory(b);
2778            
2779         btn.render(this.el.select('.modal-footer div').first());
2780         
2781         return btn;   
2782        
2783     },
2784     
2785     setDefaultButton : function(btn)
2786     {
2787         //this.el.select('.modal-footer').()
2788     },
2789     diff : false,
2790     
2791     resizeTo: function(w,h)
2792     {
2793         // skip.. ?? why??
2794         
2795         this.dialogEl.setWidth(w);
2796         if (this.diff === false) {
2797             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2798         }
2799         
2800         this.bodyEl.setHeight(h-this.diff);
2801         
2802         
2803     },
2804     setContentSize  : function(w, h)
2805     {
2806         
2807     },
2808     onButtonClick: function(btn,e)
2809     {
2810         //Roo.log([a,b,c]);
2811         this.fireEvent('btnclick', btn.name, e);
2812     },
2813      /**
2814      * Set the title of the Dialog
2815      * @param {String} str new Title
2816      */
2817     setTitle: function(str) {
2818         this.titleEl.dom.innerHTML = str;    
2819     },
2820     /**
2821      * Set the body of the Dialog
2822      * @param {String} str new Title
2823      */
2824     setBody: function(str) {
2825         this.bodyEl.dom.innerHTML = str;    
2826     },
2827     /**
2828      * Set the body of the Dialog using the template
2829      * @param {Obj} data - apply this data to the template and replace the body contents.
2830      */
2831     applyBody: function(obj)
2832     {
2833         if (!this.tmpl) {
2834             Roo.log("Error - using apply Body without a template");
2835             //code
2836         }
2837         this.tmpl.overwrite(this.bodyEl, obj);
2838     }
2839     
2840 });
2841
2842
2843 Roo.apply(Roo.bootstrap.Modal,  {
2844     /**
2845          * Button config that displays a single OK button
2846          * @type Object
2847          */
2848         OK :  [{
2849             name : 'ok',
2850             weight : 'primary',
2851             html : 'OK'
2852         }], 
2853         /**
2854          * Button config that displays Yes and No buttons
2855          * @type Object
2856          */
2857         YESNO : [
2858             {
2859                 name  : 'no',
2860                 html : 'No'
2861             },
2862             {
2863                 name  :'yes',
2864                 weight : 'primary',
2865                 html : 'Yes'
2866             }
2867         ],
2868         
2869         /**
2870          * Button config that displays OK and Cancel buttons
2871          * @type Object
2872          */
2873         OKCANCEL : [
2874             {
2875                name : 'cancel',
2876                 html : 'Cancel'
2877             },
2878             {
2879                 name : 'ok',
2880                 weight : 'primary',
2881                 html : 'OK'
2882             }
2883         ],
2884         /**
2885          * Button config that displays Yes, No and Cancel buttons
2886          * @type Object
2887          */
2888         YESNOCANCEL : [
2889             {
2890                 name : 'yes',
2891                 weight : 'primary',
2892                 html : 'Yes'
2893             },
2894             {
2895                 name : 'no',
2896                 html : 'No'
2897             },
2898             {
2899                 name : 'cancel',
2900                 html : 'Cancel'
2901             }
2902         ]
2903 });
2904  
2905  /*
2906  * - LGPL
2907  *
2908  * messagebox - can be used as a replace
2909  * 
2910  */
2911 /**
2912  * @class Roo.MessageBox
2913  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2914  * Example usage:
2915  *<pre><code>
2916 // Basic alert:
2917 Roo.Msg.alert('Status', 'Changes saved successfully.');
2918
2919 // Prompt for user data:
2920 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2921     if (btn == 'ok'){
2922         // process text value...
2923     }
2924 });
2925
2926 // Show a dialog using config options:
2927 Roo.Msg.show({
2928    title:'Save Changes?',
2929    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2930    buttons: Roo.Msg.YESNOCANCEL,
2931    fn: processResult,
2932    animEl: 'elId'
2933 });
2934 </code></pre>
2935  * @singleton
2936  */
2937 Roo.bootstrap.MessageBox = function(){
2938     var dlg, opt, mask, waitTimer;
2939     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
2940     var buttons, activeTextEl, bwidth;
2941
2942     
2943     // private
2944     var handleButton = function(button){
2945         dlg.hide();
2946         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
2947     };
2948
2949     // private
2950     var handleHide = function(){
2951         if(opt && opt.cls){
2952             dlg.el.removeClass(opt.cls);
2953         }
2954         //if(waitTimer){
2955         //    Roo.TaskMgr.stop(waitTimer);
2956         //    waitTimer = null;
2957         //}
2958     };
2959
2960     // private
2961     var updateButtons = function(b){
2962         var width = 0;
2963         if(!b){
2964             buttons["ok"].hide();
2965             buttons["cancel"].hide();
2966             buttons["yes"].hide();
2967             buttons["no"].hide();
2968             //dlg.footer.dom.style.display = 'none';
2969             return width;
2970         }
2971         dlg.footerEl.dom.style.display = '';
2972         for(var k in buttons){
2973             if(typeof buttons[k] != "function"){
2974                 if(b[k]){
2975                     buttons[k].show();
2976                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
2977                     width += buttons[k].el.getWidth()+15;
2978                 }else{
2979                     buttons[k].hide();
2980                 }
2981             }
2982         }
2983         return width;
2984     };
2985
2986     // private
2987     var handleEsc = function(d, k, e){
2988         if(opt && opt.closable !== false){
2989             dlg.hide();
2990         }
2991         if(e){
2992             e.stopEvent();
2993         }
2994     };
2995
2996     return {
2997         /**
2998          * Returns a reference to the underlying {@link Roo.BasicDialog} element
2999          * @return {Roo.BasicDialog} The BasicDialog element
3000          */
3001         getDialog : function(){
3002            if(!dlg){
3003                 dlg = new Roo.bootstrap.Modal( {
3004                     //draggable: true,
3005                     //resizable:false,
3006                     //constraintoviewport:false,
3007                     //fixedcenter:true,
3008                     //collapsible : false,
3009                     //shim:true,
3010                     //modal: true,
3011                   //  width:400,
3012                   //  height:100,
3013                     //buttonAlign:"center",
3014                     closeClick : function(){
3015                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3016                             handleButton("no");
3017                         }else{
3018                             handleButton("cancel");
3019                         }
3020                     }
3021                 });
3022                 dlg.render();
3023                 dlg.on("hide", handleHide);
3024                 mask = dlg.mask;
3025                 //dlg.addKeyListener(27, handleEsc);
3026                 buttons = {};
3027                 this.buttons = buttons;
3028                 var bt = this.buttonText;
3029                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3030                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3031                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3032                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3033                 //Roo.log(buttons);
3034                 bodyEl = dlg.bodyEl.createChild({
3035
3036                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3037                         '<textarea class="roo-mb-textarea"></textarea>' +
3038                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3039                 });
3040                 msgEl = bodyEl.dom.firstChild;
3041                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3042                 textboxEl.enableDisplayMode();
3043                 textboxEl.addKeyListener([10,13], function(){
3044                     if(dlg.isVisible() && opt && opt.buttons){
3045                         if(opt.buttons.ok){
3046                             handleButton("ok");
3047                         }else if(opt.buttons.yes){
3048                             handleButton("yes");
3049                         }
3050                     }
3051                 });
3052                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3053                 textareaEl.enableDisplayMode();
3054                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3055                 progressEl.enableDisplayMode();
3056                 var pf = progressEl.dom.firstChild;
3057                 if (pf) {
3058                     pp = Roo.get(pf.firstChild);
3059                     pp.setHeight(pf.offsetHeight);
3060                 }
3061                 
3062             }
3063             return dlg;
3064         },
3065
3066         /**
3067          * Updates the message box body text
3068          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3069          * the XHTML-compliant non-breaking space character '&amp;#160;')
3070          * @return {Roo.MessageBox} This message box
3071          */
3072         updateText : function(text){
3073             if(!dlg.isVisible() && !opt.width){
3074                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
3075             }
3076             msgEl.innerHTML = text || '&#160;';
3077       
3078             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3079             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3080             var w = Math.max(
3081                     Math.min(opt.width || cw , this.maxWidth), 
3082                     Math.max(opt.minWidth || this.minWidth, bwidth)
3083             );
3084             if(opt.prompt){
3085                 activeTextEl.setWidth(w);
3086             }
3087             if(dlg.isVisible()){
3088                 dlg.fixedcenter = false;
3089             }
3090             // to big, make it scroll. = But as usual stupid IE does not support
3091             // !important..
3092             
3093             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3094                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3095                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3096             } else {
3097                 bodyEl.dom.style.height = '';
3098                 bodyEl.dom.style.overflowY = '';
3099             }
3100             if (cw > w) {
3101                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3102             } else {
3103                 bodyEl.dom.style.overflowX = '';
3104             }
3105             
3106             dlg.setContentSize(w, bodyEl.getHeight());
3107             if(dlg.isVisible()){
3108                 dlg.fixedcenter = true;
3109             }
3110             return this;
3111         },
3112
3113         /**
3114          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3115          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3116          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3117          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3118          * @return {Roo.MessageBox} This message box
3119          */
3120         updateProgress : function(value, text){
3121             if(text){
3122                 this.updateText(text);
3123             }
3124             if (pp) { // weird bug on my firefox - for some reason this is not defined
3125                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3126             }
3127             return this;
3128         },        
3129
3130         /**
3131          * Returns true if the message box is currently displayed
3132          * @return {Boolean} True if the message box is visible, else false
3133          */
3134         isVisible : function(){
3135             return dlg && dlg.isVisible();  
3136         },
3137
3138         /**
3139          * Hides the message box if it is displayed
3140          */
3141         hide : function(){
3142             if(this.isVisible()){
3143                 dlg.hide();
3144             }  
3145         },
3146
3147         /**
3148          * Displays a new message box, or reinitializes an existing message box, based on the config options
3149          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3150          * The following config object properties are supported:
3151          * <pre>
3152 Property    Type             Description
3153 ----------  ---------------  ------------------------------------------------------------------------------------
3154 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3155                                    closes (defaults to undefined)
3156 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3157                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3158 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3159                                    progress and wait dialogs will ignore this property and always hide the
3160                                    close button as they can only be closed programmatically.
3161 cls               String           A custom CSS class to apply to the message box element
3162 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3163                                    displayed (defaults to 75)
3164 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3165                                    function will be btn (the name of the button that was clicked, if applicable,
3166                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3167                                    Progress and wait dialogs will ignore this option since they do not respond to
3168                                    user actions and can only be closed programmatically, so any required function
3169                                    should be called by the same code after it closes the dialog.
3170 icon              String           A CSS class that provides a background image to be used as an icon for
3171                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3172 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3173 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3174 modal             Boolean          False to allow user interaction with the page while the message box is
3175                                    displayed (defaults to true)
3176 msg               String           A string that will replace the existing message box body text (defaults
3177                                    to the XHTML-compliant non-breaking space character '&#160;')
3178 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3179 progress          Boolean          True to display a progress bar (defaults to false)
3180 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3181 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3182 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3183 title             String           The title text
3184 value             String           The string value to set into the active textbox element if displayed
3185 wait              Boolean          True to display a progress bar (defaults to false)
3186 width             Number           The width of the dialog in pixels
3187 </pre>
3188          *
3189          * Example usage:
3190          * <pre><code>
3191 Roo.Msg.show({
3192    title: 'Address',
3193    msg: 'Please enter your address:',
3194    width: 300,
3195    buttons: Roo.MessageBox.OKCANCEL,
3196    multiline: true,
3197    fn: saveAddress,
3198    animEl: 'addAddressBtn'
3199 });
3200 </code></pre>
3201          * @param {Object} config Configuration options
3202          * @return {Roo.MessageBox} This message box
3203          */
3204         show : function(options)
3205         {
3206             
3207             // this causes nightmares if you show one dialog after another
3208             // especially on callbacks..
3209              
3210             if(this.isVisible()){
3211                 
3212                 this.hide();
3213                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3214                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3215                 Roo.log("New Dialog Message:" +  options.msg )
3216                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3217                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3218                 
3219             }
3220             var d = this.getDialog();
3221             opt = options;
3222             d.setTitle(opt.title || "&#160;");
3223             d.closeEl.setDisplayed(opt.closable !== false);
3224             activeTextEl = textboxEl;
3225             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3226             if(opt.prompt){
3227                 if(opt.multiline){
3228                     textboxEl.hide();
3229                     textareaEl.show();
3230                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3231                         opt.multiline : this.defaultTextHeight);
3232                     activeTextEl = textareaEl;
3233                 }else{
3234                     textboxEl.show();
3235                     textareaEl.hide();
3236                 }
3237             }else{
3238                 textboxEl.hide();
3239                 textareaEl.hide();
3240             }
3241             progressEl.setDisplayed(opt.progress === true);
3242             this.updateProgress(0);
3243             activeTextEl.dom.value = opt.value || "";
3244             if(opt.prompt){
3245                 dlg.setDefaultButton(activeTextEl);
3246             }else{
3247                 var bs = opt.buttons;
3248                 var db = null;
3249                 if(bs && bs.ok){
3250                     db = buttons["ok"];
3251                 }else if(bs && bs.yes){
3252                     db = buttons["yes"];
3253                 }
3254                 dlg.setDefaultButton(db);
3255             }
3256             bwidth = updateButtons(opt.buttons);
3257             this.updateText(opt.msg);
3258             if(opt.cls){
3259                 d.el.addClass(opt.cls);
3260             }
3261             d.proxyDrag = opt.proxyDrag === true;
3262             d.modal = opt.modal !== false;
3263             d.mask = opt.modal !== false ? mask : false;
3264             if(!d.isVisible()){
3265                 // force it to the end of the z-index stack so it gets a cursor in FF
3266                 document.body.appendChild(dlg.el.dom);
3267                 d.animateTarget = null;
3268                 d.show(options.animEl);
3269             }
3270             return this;
3271         },
3272
3273         /**
3274          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3275          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3276          * and closing the message box when the process is complete.
3277          * @param {String} title The title bar text
3278          * @param {String} msg The message box body text
3279          * @return {Roo.MessageBox} This message box
3280          */
3281         progress : function(title, msg){
3282             this.show({
3283                 title : title,
3284                 msg : msg,
3285                 buttons: false,
3286                 progress:true,
3287                 closable:false,
3288                 minWidth: this.minProgressWidth,
3289                 modal : true
3290             });
3291             return this;
3292         },
3293
3294         /**
3295          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3296          * If a callback function is passed it will be called after the user clicks the button, and the
3297          * id of the button that was clicked will be passed as the only parameter to the callback
3298          * (could also be the top-right close button).
3299          * @param {String} title The title bar text
3300          * @param {String} msg The message box body text
3301          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3302          * @param {Object} scope (optional) The scope of the callback function
3303          * @return {Roo.MessageBox} This message box
3304          */
3305         alert : function(title, msg, fn, scope){
3306             this.show({
3307                 title : title,
3308                 msg : msg,
3309                 buttons: this.OK,
3310                 fn: fn,
3311                 scope : scope,
3312                 modal : true
3313             });
3314             return this;
3315         },
3316
3317         /**
3318          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3319          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3320          * You are responsible for closing the message box when the process is complete.
3321          * @param {String} msg The message box body text
3322          * @param {String} title (optional) The title bar text
3323          * @return {Roo.MessageBox} This message box
3324          */
3325         wait : function(msg, title){
3326             this.show({
3327                 title : title,
3328                 msg : msg,
3329                 buttons: false,
3330                 closable:false,
3331                 progress:true,
3332                 modal:true,
3333                 width:300,
3334                 wait:true
3335             });
3336             waitTimer = Roo.TaskMgr.start({
3337                 run: function(i){
3338                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3339                 },
3340                 interval: 1000
3341             });
3342             return this;
3343         },
3344
3345         /**
3346          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3347          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3348          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3349          * @param {String} title The title bar text
3350          * @param {String} msg The message box body text
3351          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3352          * @param {Object} scope (optional) The scope of the callback function
3353          * @return {Roo.MessageBox} This message box
3354          */
3355         confirm : function(title, msg, fn, scope){
3356             this.show({
3357                 title : title,
3358                 msg : msg,
3359                 buttons: this.YESNO,
3360                 fn: fn,
3361                 scope : scope,
3362                 modal : true
3363             });
3364             return this;
3365         },
3366
3367         /**
3368          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3369          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3370          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3371          * (could also be the top-right close button) and the text that was entered will be passed as the two
3372          * parameters to the callback.
3373          * @param {String} title The title bar text
3374          * @param {String} msg The message box body text
3375          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3376          * @param {Object} scope (optional) The scope of the callback function
3377          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3378          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3379          * @return {Roo.MessageBox} This message box
3380          */
3381         prompt : function(title, msg, fn, scope, multiline){
3382             this.show({
3383                 title : title,
3384                 msg : msg,
3385                 buttons: this.OKCANCEL,
3386                 fn: fn,
3387                 minWidth:250,
3388                 scope : scope,
3389                 prompt:true,
3390                 multiline: multiline,
3391                 modal : true
3392             });
3393             return this;
3394         },
3395
3396         /**
3397          * Button config that displays a single OK button
3398          * @type Object
3399          */
3400         OK : {ok:true},
3401         /**
3402          * Button config that displays Yes and No buttons
3403          * @type Object
3404          */
3405         YESNO : {yes:true, no:true},
3406         /**
3407          * Button config that displays OK and Cancel buttons
3408          * @type Object
3409          */
3410         OKCANCEL : {ok:true, cancel:true},
3411         /**
3412          * Button config that displays Yes, No and Cancel buttons
3413          * @type Object
3414          */
3415         YESNOCANCEL : {yes:true, no:true, cancel:true},
3416
3417         /**
3418          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3419          * @type Number
3420          */
3421         defaultTextHeight : 75,
3422         /**
3423          * The maximum width in pixels of the message box (defaults to 600)
3424          * @type Number
3425          */
3426         maxWidth : 600,
3427         /**
3428          * The minimum width in pixels of the message box (defaults to 100)
3429          * @type Number
3430          */
3431         minWidth : 100,
3432         /**
3433          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3434          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3435          * @type Number
3436          */
3437         minProgressWidth : 250,
3438         /**
3439          * An object containing the default button text strings that can be overriden for localized language support.
3440          * Supported properties are: ok, cancel, yes and no.
3441          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3442          * @type Object
3443          */
3444         buttonText : {
3445             ok : "OK",
3446             cancel : "Cancel",
3447             yes : "Yes",
3448             no : "No"
3449         }
3450     };
3451 }();
3452
3453 /**
3454  * Shorthand for {@link Roo.MessageBox}
3455  */
3456 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3457 Roo.Msg = Roo.Msg || Roo.MessageBox;
3458 /*
3459  * - LGPL
3460  *
3461  * navbar
3462  * 
3463  */
3464
3465 /**
3466  * @class Roo.bootstrap.Navbar
3467  * @extends Roo.bootstrap.Component
3468  * Bootstrap Navbar class
3469
3470  * @constructor
3471  * Create a new Navbar
3472  * @param {Object} config The config object
3473  */
3474
3475
3476 Roo.bootstrap.Navbar = function(config){
3477     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3478     this.addEvents({
3479         // raw events
3480         /**
3481          * @event beforetoggle
3482          * Fire before toggle the menu
3483          * @param {Roo.EventObject} e
3484          */
3485         "beforetoggle" : true
3486     });
3487 };
3488
3489 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3490     
3491     
3492    
3493     // private
3494     navItems : false,
3495     loadMask : false,
3496     
3497     
3498     getAutoCreate : function(){
3499         
3500         
3501         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3502         
3503     },
3504     
3505     initEvents :function ()
3506     {
3507         //Roo.log(this.el.select('.navbar-toggle',true));
3508         this.el.select('.navbar-toggle',true).on('click', function() {
3509             if(this.fireEvent('beforetoggle', this) !== false){
3510                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3511             }
3512             
3513         }, this);
3514         
3515         var mark = {
3516             tag: "div",
3517             cls:"x-dlg-mask"
3518         };
3519         
3520         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3521         
3522         var size = this.el.getSize();
3523         this.maskEl.setSize(size.width, size.height);
3524         this.maskEl.enableDisplayMode("block");
3525         this.maskEl.hide();
3526         
3527         if(this.loadMask){
3528             this.maskEl.show();
3529         }
3530     },
3531     
3532     
3533     getChildContainer : function()
3534     {
3535         if (this.el.select('.collapse').getCount()) {
3536             return this.el.select('.collapse',true).first();
3537         }
3538         
3539         return this.el;
3540     },
3541     
3542     mask : function()
3543     {
3544         this.maskEl.show();
3545     },
3546     
3547     unmask : function()
3548     {
3549         this.maskEl.hide();
3550     } 
3551     
3552     
3553     
3554     
3555 });
3556
3557
3558
3559  
3560
3561  /*
3562  * - LGPL
3563  *
3564  * navbar
3565  * 
3566  */
3567
3568 /**
3569  * @class Roo.bootstrap.NavSimplebar
3570  * @extends Roo.bootstrap.Navbar
3571  * Bootstrap Sidebar class
3572  *
3573  * @cfg {Boolean} inverse is inverted color
3574  * 
3575  * @cfg {String} type (nav | pills | tabs)
3576  * @cfg {Boolean} arrangement stacked | justified
3577  * @cfg {String} align (left | right) alignment
3578  * 
3579  * @cfg {Boolean} main (true|false) main nav bar? default false
3580  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3581  * 
3582  * @cfg {String} tag (header|footer|nav|div) default is nav 
3583
3584  * 
3585  * 
3586  * 
3587  * @constructor
3588  * Create a new Sidebar
3589  * @param {Object} config The config object
3590  */
3591
3592
3593 Roo.bootstrap.NavSimplebar = function(config){
3594     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3595 };
3596
3597 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3598     
3599     inverse: false,
3600     
3601     type: false,
3602     arrangement: '',
3603     align : false,
3604     
3605     
3606     
3607     main : false,
3608     
3609     
3610     tag : false,
3611     
3612     
3613     getAutoCreate : function(){
3614         
3615         
3616         var cfg = {
3617             tag : this.tag || 'div',
3618             cls : 'navbar'
3619         };
3620           
3621         
3622         cfg.cn = [
3623             {
3624                 cls: 'nav',
3625                 tag : 'ul'
3626             }
3627         ];
3628         
3629          
3630         this.type = this.type || 'nav';
3631         if (['tabs','pills'].indexOf(this.type)!==-1) {
3632             cfg.cn[0].cls += ' nav-' + this.type
3633         
3634         
3635         } else {
3636             if (this.type!=='nav') {
3637                 Roo.log('nav type must be nav/tabs/pills')
3638             }
3639             cfg.cn[0].cls += ' navbar-nav'
3640         }
3641         
3642         
3643         
3644         
3645         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3646             cfg.cn[0].cls += ' nav-' + this.arrangement;
3647         }
3648         
3649         
3650         if (this.align === 'right') {
3651             cfg.cn[0].cls += ' navbar-right';
3652         }
3653         
3654         if (this.inverse) {
3655             cfg.cls += ' navbar-inverse';
3656             
3657         }
3658         
3659         
3660         return cfg;
3661     
3662         
3663     }
3664     
3665     
3666     
3667 });
3668
3669
3670
3671  
3672
3673  
3674        /*
3675  * - LGPL
3676  *
3677  * navbar
3678  * 
3679  */
3680
3681 /**
3682  * @class Roo.bootstrap.NavHeaderbar
3683  * @extends Roo.bootstrap.NavSimplebar
3684  * Bootstrap Sidebar class
3685  *
3686  * @cfg {String} brand what is brand
3687  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3688  * @cfg {String} brand_href href of the brand
3689  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3690  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3691  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3692  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3693  * 
3694  * @constructor
3695  * Create a new Sidebar
3696  * @param {Object} config The config object
3697  */
3698
3699
3700 Roo.bootstrap.NavHeaderbar = function(config){
3701     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3702       
3703 };
3704
3705 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3706     
3707     position: '',
3708     brand: '',
3709     brand_href: false,
3710     srButton : true,
3711     autohide : false,
3712     desktopCenter : false,
3713    
3714     
3715     getAutoCreate : function(){
3716         
3717         var   cfg = {
3718             tag: this.nav || 'nav',
3719             cls: 'navbar',
3720             role: 'navigation',
3721             cn: []
3722         };
3723         
3724         var cn = cfg.cn;
3725         if (this.desktopCenter) {
3726             cn.push({cls : 'container', cn : []});
3727             cn = cn[0].cn;
3728         }
3729         
3730         if(this.srButton){
3731             cn.push({
3732                 tag: 'div',
3733                 cls: 'navbar-header',
3734                 cn: [
3735                     {
3736                         tag: 'button',
3737                         type: 'button',
3738                         cls: 'navbar-toggle',
3739                         'data-toggle': 'collapse',
3740                         cn: [
3741                             {
3742                                 tag: 'span',
3743                                 cls: 'sr-only',
3744                                 html: 'Toggle navigation'
3745                             },
3746                             {
3747                                 tag: 'span',
3748                                 cls: 'icon-bar'
3749                             },
3750                             {
3751                                 tag: 'span',
3752                                 cls: 'icon-bar'
3753                             },
3754                             {
3755                                 tag: 'span',
3756                                 cls: 'icon-bar'
3757                             }
3758                         ]
3759                     }
3760                 ]
3761             });
3762         }
3763         
3764         cn.push({
3765             tag: 'div',
3766             cls: 'collapse navbar-collapse',
3767             cn : []
3768         });
3769         
3770         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3771         
3772         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3773             cfg.cls += ' navbar-' + this.position;
3774             
3775             // tag can override this..
3776             
3777             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3778         }
3779         
3780         if (this.brand !== '') {
3781             cn[0].cn.push({
3782                 tag: 'a',
3783                 href: this.brand_href ? this.brand_href : '#',
3784                 cls: 'navbar-brand',
3785                 cn: [
3786                 this.brand
3787                 ]
3788             });
3789         }
3790         
3791         if(this.main){
3792             cfg.cls += ' main-nav';
3793         }
3794         
3795         
3796         return cfg;
3797
3798         
3799     },
3800     getHeaderChildContainer : function()
3801     {
3802         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3803             return this.el.select('.navbar-header',true).first();
3804         }
3805         
3806         return this.getChildContainer();
3807     },
3808     
3809     
3810     initEvents : function()
3811     {
3812         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3813         
3814         if (this.autohide) {
3815             
3816             var prevScroll = 0;
3817             var ft = this.el;
3818             
3819             Roo.get(document).on('scroll',function(e) {
3820                 var ns = Roo.get(document).getScroll().top;
3821                 var os = prevScroll;
3822                 prevScroll = ns;
3823                 
3824                 if(ns > os){
3825                     ft.removeClass('slideDown');
3826                     ft.addClass('slideUp');
3827                     return;
3828                 }
3829                 ft.removeClass('slideUp');
3830                 ft.addClass('slideDown');
3831                  
3832               
3833           },this);
3834         }
3835     }    
3836     
3837 });
3838
3839
3840
3841  
3842
3843  /*
3844  * - LGPL
3845  *
3846  * navbar
3847  * 
3848  */
3849
3850 /**
3851  * @class Roo.bootstrap.NavSidebar
3852  * @extends Roo.bootstrap.Navbar
3853  * Bootstrap Sidebar class
3854  * 
3855  * @constructor
3856  * Create a new Sidebar
3857  * @param {Object} config The config object
3858  */
3859
3860
3861 Roo.bootstrap.NavSidebar = function(config){
3862     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3863 };
3864
3865 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3866     
3867     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3868     
3869     getAutoCreate : function(){
3870         
3871         
3872         return  {
3873             tag: 'div',
3874             cls: 'sidebar sidebar-nav'
3875         };
3876     
3877         
3878     }
3879     
3880     
3881     
3882 });
3883
3884
3885
3886  
3887
3888  /*
3889  * - LGPL
3890  *
3891  * nav group
3892  * 
3893  */
3894
3895 /**
3896  * @class Roo.bootstrap.NavGroup
3897  * @extends Roo.bootstrap.Component
3898  * Bootstrap NavGroup class
3899  * @cfg {String} align (left|right)
3900  * @cfg {Boolean} inverse
3901  * @cfg {String} type (nav|pills|tab) default nav
3902  * @cfg {String} navId - reference Id for navbar.
3903
3904  * 
3905  * @constructor
3906  * Create a new nav group
3907  * @param {Object} config The config object
3908  */
3909
3910 Roo.bootstrap.NavGroup = function(config){
3911     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3912     this.navItems = [];
3913    
3914     Roo.bootstrap.NavGroup.register(this);
3915      this.addEvents({
3916         /**
3917              * @event changed
3918              * Fires when the active item changes
3919              * @param {Roo.bootstrap.NavGroup} this
3920              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3921              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3922          */
3923         'changed': true
3924      });
3925     
3926 };
3927
3928 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
3929     
3930     align: '',
3931     inverse: false,
3932     form: false,
3933     type: 'nav',
3934     navId : '',
3935     // private
3936     
3937     navItems : false, 
3938     
3939     getAutoCreate : function()
3940     {
3941         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
3942         
3943         cfg = {
3944             tag : 'ul',
3945             cls: 'nav' 
3946         };
3947         
3948         if (['tabs','pills'].indexOf(this.type)!==-1) {
3949             cfg.cls += ' nav-' + this.type
3950         } else {
3951             if (this.type!=='nav') {
3952                 Roo.log('nav type must be nav/tabs/pills')
3953             }
3954             cfg.cls += ' navbar-nav'
3955         }
3956         
3957         if (this.parent().sidebar) {
3958             cfg = {
3959                 tag: 'ul',
3960                 cls: 'dashboard-menu sidebar-menu'
3961             };
3962             
3963             return cfg;
3964         }
3965         
3966         if (this.form === true) {
3967             cfg = {
3968                 tag: 'form',
3969                 cls: 'navbar-form'
3970             };
3971             
3972             if (this.align === 'right') {
3973                 cfg.cls += ' navbar-right';
3974             } else {
3975                 cfg.cls += ' navbar-left';
3976             }
3977         }
3978         
3979         if (this.align === 'right') {
3980             cfg.cls += ' navbar-right';
3981         }
3982         
3983         if (this.inverse) {
3984             cfg.cls += ' navbar-inverse';
3985             
3986         }
3987         
3988         
3989         return cfg;
3990     },
3991     /**
3992     * sets the active Navigation item
3993     * @param {Roo.bootstrap.NavItem} the new current navitem
3994     */
3995     setActiveItem : function(item)
3996     {
3997         var prev = false;
3998         Roo.each(this.navItems, function(v){
3999             if (v == item) {
4000                 return ;
4001             }
4002             if (v.isActive()) {
4003                 v.setActive(false, true);
4004                 prev = v;
4005                 
4006             }
4007             
4008         });
4009
4010         item.setActive(true, true);
4011         this.fireEvent('changed', this, item, prev);
4012         
4013         
4014     },
4015     /**
4016     * gets the active Navigation item
4017     * @return {Roo.bootstrap.NavItem} the current navitem
4018     */
4019     getActive : function()
4020     {
4021         
4022         var prev = false;
4023         Roo.each(this.navItems, function(v){
4024             
4025             if (v.isActive()) {
4026                 prev = v;
4027                 
4028             }
4029             
4030         });
4031         return prev;
4032     },
4033     
4034     indexOfNav : function()
4035     {
4036         
4037         var prev = false;
4038         Roo.each(this.navItems, function(v,i){
4039             
4040             if (v.isActive()) {
4041                 prev = i;
4042                 
4043             }
4044             
4045         });
4046         return prev;
4047     },
4048     /**
4049     * adds a Navigation item
4050     * @param {Roo.bootstrap.NavItem} the navitem to add
4051     */
4052     addItem : function(cfg)
4053     {
4054         var cn = new Roo.bootstrap.NavItem(cfg);
4055         this.register(cn);
4056         cn.parentId = this.id;
4057         cn.onRender(this.el, null);
4058         return cn;
4059     },
4060     /**
4061     * register a Navigation item
4062     * @param {Roo.bootstrap.NavItem} the navitem to add
4063     */
4064     register : function(item)
4065     {
4066         this.navItems.push( item);
4067         item.navId = this.navId;
4068     
4069     },
4070     
4071     /**
4072     * clear all the Navigation item
4073     */
4074    
4075     clearAll : function()
4076     {
4077         this.navItems = [];
4078         this.el.dom.innerHTML = '';
4079     },
4080     
4081     getNavItem: function(tabId)
4082     {
4083         var ret = false;
4084         Roo.each(this.navItems, function(e) {
4085             if (e.tabId == tabId) {
4086                ret =  e;
4087                return false;
4088             }
4089             return true;
4090             
4091         });
4092         return ret;
4093     },
4094     
4095     setActiveNext : function()
4096     {
4097         var i = this.indexOfNav(this.getActive());
4098         if (i > this.navItems.length) {
4099             return;
4100         }
4101         this.setActiveItem(this.navItems[i+1]);
4102     },
4103     setActivePrev : function()
4104     {
4105         var i = this.indexOfNav(this.getActive());
4106         if (i  < 1) {
4107             return;
4108         }
4109         this.setActiveItem(this.navItems[i-1]);
4110     },
4111     clearWasActive : function(except) {
4112         Roo.each(this.navItems, function(e) {
4113             if (e.tabId != except.tabId && e.was_active) {
4114                e.was_active = false;
4115                return false;
4116             }
4117             return true;
4118             
4119         });
4120     },
4121     getWasActive : function ()
4122     {
4123         var r = false;
4124         Roo.each(this.navItems, function(e) {
4125             if (e.was_active) {
4126                r = e;
4127                return false;
4128             }
4129             return true;
4130             
4131         });
4132         return r;
4133     }
4134     
4135     
4136 });
4137
4138  
4139 Roo.apply(Roo.bootstrap.NavGroup, {
4140     
4141     groups: {},
4142      /**
4143     * register a Navigation Group
4144     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4145     */
4146     register : function(navgrp)
4147     {
4148         this.groups[navgrp.navId] = navgrp;
4149         
4150     },
4151     /**
4152     * fetch a Navigation Group based on the navigation ID
4153     * @param {string} the navgroup to add
4154     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4155     */
4156     get: function(navId) {
4157         if (typeof(this.groups[navId]) == 'undefined') {
4158             return false;
4159             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4160         }
4161         return this.groups[navId] ;
4162     }
4163     
4164     
4165     
4166 });
4167
4168  /*
4169  * - LGPL
4170  *
4171  * row
4172  * 
4173  */
4174
4175 /**
4176  * @class Roo.bootstrap.NavItem
4177  * @extends Roo.bootstrap.Component
4178  * Bootstrap Navbar.NavItem class
4179  * @cfg {String} href  link to
4180  * @cfg {String} html content of button
4181  * @cfg {String} badge text inside badge
4182  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4183  * @cfg {String} glyphicon name of glyphicon
4184  * @cfg {String} icon name of font awesome icon
4185  * @cfg {Boolean} active Is item active
4186  * @cfg {Boolean} disabled Is item disabled
4187  
4188  * @cfg {Boolean} preventDefault (true | false) default false
4189  * @cfg {String} tabId the tab that this item activates.
4190  * @cfg {String} tagtype (a|span) render as a href or span?
4191  * @cfg {Boolean} animateRef (true|false) link to element default false  
4192   
4193  * @constructor
4194  * Create a new Navbar Item
4195  * @param {Object} config The config object
4196  */
4197 Roo.bootstrap.NavItem = function(config){
4198     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4199     this.addEvents({
4200         // raw events
4201         /**
4202          * @event click
4203          * The raw click event for the entire grid.
4204          * @param {Roo.EventObject} e
4205          */
4206         "click" : true,
4207          /**
4208             * @event changed
4209             * Fires when the active item active state changes
4210             * @param {Roo.bootstrap.NavItem} this
4211             * @param {boolean} state the new state
4212              
4213          */
4214         'changed': true,
4215         /**
4216             * @event scrollto
4217             * Fires when scroll to element
4218             * @param {Roo.bootstrap.NavItem} this
4219             * @param {Object} options
4220             * @param {Roo.EventObject} e
4221              
4222          */
4223         'scrollto': true
4224     });
4225    
4226 };
4227
4228 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4229     
4230     href: false,
4231     html: '',
4232     badge: '',
4233     icon: false,
4234     glyphicon: false,
4235     active: false,
4236     preventDefault : false,
4237     tabId : false,
4238     tagtype : 'a',
4239     disabled : false,
4240     animateRef : false,
4241     was_active : false,
4242     
4243     getAutoCreate : function(){
4244          
4245         var cfg = {
4246             tag: 'li',
4247             cls: 'nav-item'
4248             
4249         };
4250         
4251         if (this.active) {
4252             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4253         }
4254         if (this.disabled) {
4255             cfg.cls += ' disabled';
4256         }
4257         
4258         if (this.href || this.html || this.glyphicon || this.icon) {
4259             cfg.cn = [
4260                 {
4261                     tag: this.tagtype,
4262                     href : this.href || "#",
4263                     html: this.html || ''
4264                 }
4265             ];
4266             
4267             if (this.icon) {
4268                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4269             }
4270
4271             if(this.glyphicon) {
4272                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4273             }
4274             
4275             if (this.menu) {
4276                 
4277                 cfg.cn[0].html += " <span class='caret'></span>";
4278              
4279             }
4280             
4281             if (this.badge !== '') {
4282                  
4283                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4284             }
4285         }
4286         
4287         
4288         
4289         return cfg;
4290     },
4291     initEvents: function() 
4292     {
4293         if (typeof (this.menu) != 'undefined') {
4294             this.menu.parentType = this.xtype;
4295             this.menu.triggerEl = this.el;
4296             this.menu = this.addxtype(Roo.apply({}, this.menu));
4297         }
4298         
4299         this.el.select('a',true).on('click', this.onClick, this);
4300         
4301         if(this.tagtype == 'span'){
4302             this.el.select('span',true).on('click', this.onClick, this);
4303         }
4304        
4305         // at this point parent should be available..
4306         this.parent().register(this);
4307     },
4308     
4309     onClick : function(e)
4310     {
4311         if (e.getTarget('.dropdown-menu-item')) {
4312             // did you click on a menu itemm.... - then don't trigger onclick..
4313             return;
4314         }
4315         
4316         if(
4317                 this.preventDefault || 
4318                 this.href == '#' 
4319         ){
4320             Roo.log("NavItem - prevent Default?");
4321             e.preventDefault();
4322         }
4323         
4324         if (this.disabled) {
4325             return;
4326         }
4327         
4328         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4329         if (tg && tg.transition) {
4330             Roo.log("waiting for the transitionend");
4331             return;
4332         }
4333         
4334         
4335         
4336         //Roo.log("fire event clicked");
4337         if(this.fireEvent('click', this, e) === false){
4338             return;
4339         };
4340         
4341         if(this.tagtype == 'span'){
4342             return;
4343         }
4344         
4345         //Roo.log(this.href);
4346         var ael = this.el.select('a',true).first();
4347         //Roo.log(ael);
4348         
4349         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4350             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4351             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4352                 return; // ignore... - it's a 'hash' to another page.
4353             }
4354             Roo.log("NavItem - prevent Default?");
4355             e.preventDefault();
4356             this.scrollToElement(e);
4357         }
4358         
4359         
4360         var p =  this.parent();
4361    
4362         if (['tabs','pills'].indexOf(p.type)!==-1) {
4363             if (typeof(p.setActiveItem) !== 'undefined') {
4364                 p.setActiveItem(this);
4365             }
4366         }
4367         
4368         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4369         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4370             // remove the collapsed menu expand...
4371             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4372         }
4373     },
4374     
4375     isActive: function () {
4376         return this.active
4377     },
4378     setActive : function(state, fire, is_was_active)
4379     {
4380         if (this.active && !state && this.navId) {
4381             this.was_active = true;
4382             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4383             if (nv) {
4384                 nv.clearWasActive(this);
4385             }
4386             
4387         }
4388         this.active = state;
4389         
4390         if (!state ) {
4391             this.el.removeClass('active');
4392         } else if (!this.el.hasClass('active')) {
4393             this.el.addClass('active');
4394         }
4395         if (fire) {
4396             this.fireEvent('changed', this, state);
4397         }
4398         
4399         // show a panel if it's registered and related..
4400         
4401         if (!this.navId || !this.tabId || !state || is_was_active) {
4402             return;
4403         }
4404         
4405         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4406         if (!tg) {
4407             return;
4408         }
4409         var pan = tg.getPanelByName(this.tabId);
4410         if (!pan) {
4411             return;
4412         }
4413         // if we can not flip to new panel - go back to old nav highlight..
4414         if (false == tg.showPanel(pan)) {
4415             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4416             if (nv) {
4417                 var onav = nv.getWasActive();
4418                 if (onav) {
4419                     onav.setActive(true, false, true);
4420                 }
4421             }
4422             
4423         }
4424         
4425         
4426         
4427     },
4428      // this should not be here...
4429     setDisabled : function(state)
4430     {
4431         this.disabled = state;
4432         if (!state ) {
4433             this.el.removeClass('disabled');
4434         } else if (!this.el.hasClass('disabled')) {
4435             this.el.addClass('disabled');
4436         }
4437         
4438     },
4439     
4440     /**
4441      * Fetch the element to display the tooltip on.
4442      * @return {Roo.Element} defaults to this.el
4443      */
4444     tooltipEl : function()
4445     {
4446         return this.el.select('' + this.tagtype + '', true).first();
4447     },
4448     
4449     scrollToElement : function(e)
4450     {
4451         var c = document.body;
4452         
4453         /*
4454          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4455          */
4456         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4457             c = document.documentElement;
4458         }
4459         
4460         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4461         
4462         if(!target){
4463             return;
4464         }
4465
4466         var o = target.calcOffsetsTo(c);
4467         
4468         var options = {
4469             target : target,
4470             value : o[1]
4471         };
4472         
4473         this.fireEvent('scrollto', this, options, e);
4474         
4475         Roo.get(c).scrollTo('top', options.value, true);
4476         
4477         return;
4478     }
4479 });
4480  
4481
4482  /*
4483  * - LGPL
4484  *
4485  * sidebar item
4486  *
4487  *  li
4488  *    <span> icon </span>
4489  *    <span> text </span>
4490  *    <span>badge </span>
4491  */
4492
4493 /**
4494  * @class Roo.bootstrap.NavSidebarItem
4495  * @extends Roo.bootstrap.NavItem
4496  * Bootstrap Navbar.NavSidebarItem class
4497  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4498  * {bool} open is the menu open
4499  * @constructor
4500  * Create a new Navbar Button
4501  * @param {Object} config The config object
4502  */
4503 Roo.bootstrap.NavSidebarItem = function(config){
4504     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4505     this.addEvents({
4506         // raw events
4507         /**
4508          * @event click
4509          * The raw click event for the entire grid.
4510          * @param {Roo.EventObject} e
4511          */
4512         "click" : true,
4513          /**
4514             * @event changed
4515             * Fires when the active item active state changes
4516             * @param {Roo.bootstrap.NavSidebarItem} this
4517             * @param {boolean} state the new state
4518              
4519          */
4520         'changed': true
4521     });
4522    
4523 };
4524
4525 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4526     
4527     badgeWeight : 'default',
4528     
4529     open: false,
4530     
4531     getAutoCreate : function(){
4532         
4533         
4534         var a = {
4535                 tag: 'a',
4536                 href : this.href || '#',
4537                 cls: '',
4538                 html : '',
4539                 cn : []
4540         };
4541         var cfg = {
4542             tag: 'li',
4543             cls: '',
4544             cn: [ a ]
4545         };
4546         var span = {
4547             tag: 'span',
4548             html : this.html || ''
4549         };
4550         
4551         
4552         if (this.active) {
4553             cfg.cls += ' active';
4554         }
4555         
4556         if (this.disabled) {
4557             cfg.cls += ' disabled';
4558         }
4559         if (this.open) {
4560             cfg.cls += ' open x-open';
4561         }
4562         // left icon..
4563         if (this.glyphicon || this.icon) {
4564             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4565             a.cn.push({ tag : 'i', cls : c }) ;
4566         }
4567         // html..
4568         a.cn.push(span);
4569         // then badge..
4570         if (this.badge !== '') {
4571             
4572             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4573         }
4574         // fi
4575         if (this.menu) {
4576             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4577             a.cls += 'dropdown-toggle treeview' ;
4578         }
4579         
4580         return cfg;
4581          
4582            
4583     },
4584     
4585     initEvents : function()
4586     { 
4587         if (typeof (this.menu) != 'undefined') {
4588             this.menu.parentType = this.xtype;
4589             this.menu.triggerEl = this.el;
4590             this.menu = this.addxtype(Roo.apply({}, this.menu));
4591         }
4592         
4593         this.el.on('click', this.onClick, this);
4594        
4595     
4596         if(this.badge !== ''){
4597  
4598             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4599         }
4600         
4601     },
4602     
4603     onClick : function(e)
4604     {
4605         if(this.disabled){
4606             e.preventDefault();
4607             return;
4608         }
4609         
4610         if(this.preventDefault){
4611             e.preventDefault();
4612         }
4613         
4614         this.fireEvent('click', this);
4615     },
4616     
4617     disable : function()
4618     {
4619         this.setDisabled(true);
4620     },
4621     
4622     enable : function()
4623     {
4624         this.setDisabled(false);
4625     },
4626     
4627     setDisabled : function(state)
4628     {
4629         if(this.disabled == state){
4630             return;
4631         }
4632         
4633         this.disabled = state;
4634         
4635         if (state) {
4636             this.el.addClass('disabled');
4637             return;
4638         }
4639         
4640         this.el.removeClass('disabled');
4641         
4642         return;
4643     },
4644     
4645     setActive : function(state)
4646     {
4647         if(this.active == state){
4648             return;
4649         }
4650         
4651         this.active = state;
4652         
4653         if (state) {
4654             this.el.addClass('active');
4655             return;
4656         }
4657         
4658         this.el.removeClass('active');
4659         
4660         return;
4661     },
4662     
4663     isActive: function () 
4664     {
4665         return this.active;
4666     },
4667     
4668     setBadge : function(str)
4669     {
4670         if(!this.badgeEl){
4671             return;
4672         }
4673         
4674         this.badgeEl.dom.innerHTML = str;
4675     }
4676     
4677    
4678      
4679  
4680 });
4681  
4682
4683  /*
4684  * - LGPL
4685  *
4686  * row
4687  * 
4688  */
4689
4690 /**
4691  * @class Roo.bootstrap.Row
4692  * @extends Roo.bootstrap.Component
4693  * Bootstrap Row class (contains columns...)
4694  * 
4695  * @constructor
4696  * Create a new Row
4697  * @param {Object} config The config object
4698  */
4699
4700 Roo.bootstrap.Row = function(config){
4701     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4702 };
4703
4704 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4705     
4706     getAutoCreate : function(){
4707        return {
4708             cls: 'row clearfix'
4709        };
4710     }
4711     
4712     
4713 });
4714
4715  
4716
4717  /*
4718  * - LGPL
4719  *
4720  * element
4721  * 
4722  */
4723
4724 /**
4725  * @class Roo.bootstrap.Element
4726  * @extends Roo.bootstrap.Component
4727  * Bootstrap Element class
4728  * @cfg {String} html contents of the element
4729  * @cfg {String} tag tag of the element
4730  * @cfg {String} cls class of the element
4731  * @cfg {Boolean} preventDefault (true|false) default false
4732  * @cfg {Boolean} clickable (true|false) default false
4733  * 
4734  * @constructor
4735  * Create a new Element
4736  * @param {Object} config The config object
4737  */
4738
4739 Roo.bootstrap.Element = function(config){
4740     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4741     
4742     this.addEvents({
4743         // raw events
4744         /**
4745          * @event click
4746          * When a element is chick
4747          * @param {Roo.bootstrap.Element} this
4748          * @param {Roo.EventObject} e
4749          */
4750         "click" : true
4751     });
4752 };
4753
4754 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4755     
4756     tag: 'div',
4757     cls: '',
4758     html: '',
4759     preventDefault: false, 
4760     clickable: false,
4761     
4762     getAutoCreate : function(){
4763         
4764         var cfg = {
4765             tag: this.tag,
4766             cls: this.cls,
4767             html: this.html
4768         };
4769         
4770         return cfg;
4771     },
4772     
4773     initEvents: function() 
4774     {
4775         Roo.bootstrap.Element.superclass.initEvents.call(this);
4776         
4777         if(this.clickable){
4778             this.el.on('click', this.onClick, this);
4779         }
4780         
4781     },
4782     
4783     onClick : function(e)
4784     {
4785         if(this.preventDefault){
4786             e.preventDefault();
4787         }
4788         
4789         this.fireEvent('click', this, e);
4790     },
4791     
4792     getValue : function()
4793     {
4794         return this.el.dom.innerHTML;
4795     },
4796     
4797     setValue : function(value)
4798     {
4799         this.el.dom.innerHTML = value;
4800     }
4801    
4802 });
4803
4804  
4805
4806  /*
4807  * - LGPL
4808  *
4809  * pagination
4810  * 
4811  */
4812
4813 /**
4814  * @class Roo.bootstrap.Pagination
4815  * @extends Roo.bootstrap.Component
4816  * Bootstrap Pagination class
4817  * @cfg {String} size xs | sm | md | lg
4818  * @cfg {Boolean} inverse false | true
4819  * 
4820  * @constructor
4821  * Create a new Pagination
4822  * @param {Object} config The config object
4823  */
4824
4825 Roo.bootstrap.Pagination = function(config){
4826     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4827 };
4828
4829 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4830     
4831     cls: false,
4832     size: false,
4833     inverse: false,
4834     
4835     getAutoCreate : function(){
4836         var cfg = {
4837             tag: 'ul',
4838                 cls: 'pagination'
4839         };
4840         if (this.inverse) {
4841             cfg.cls += ' inverse';
4842         }
4843         if (this.html) {
4844             cfg.html=this.html;
4845         }
4846         if (this.cls) {
4847             cfg.cls += " " + this.cls;
4848         }
4849         return cfg;
4850     }
4851    
4852 });
4853
4854  
4855
4856  /*
4857  * - LGPL
4858  *
4859  * Pagination item
4860  * 
4861  */
4862
4863
4864 /**
4865  * @class Roo.bootstrap.PaginationItem
4866  * @extends Roo.bootstrap.Component
4867  * Bootstrap PaginationItem class
4868  * @cfg {String} html text
4869  * @cfg {String} href the link
4870  * @cfg {Boolean} preventDefault (true | false) default true
4871  * @cfg {Boolean} active (true | false) default false
4872  * @cfg {Boolean} disabled default false
4873  * 
4874  * 
4875  * @constructor
4876  * Create a new PaginationItem
4877  * @param {Object} config The config object
4878  */
4879
4880
4881 Roo.bootstrap.PaginationItem = function(config){
4882     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4883     this.addEvents({
4884         // raw events
4885         /**
4886          * @event click
4887          * The raw click event for the entire grid.
4888          * @param {Roo.EventObject} e
4889          */
4890         "click" : true
4891     });
4892 };
4893
4894 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4895     
4896     href : false,
4897     html : false,
4898     preventDefault: true,
4899     active : false,
4900     cls : false,
4901     disabled: false,
4902     
4903     getAutoCreate : function(){
4904         var cfg= {
4905             tag: 'li',
4906             cn: [
4907                 {
4908                     tag : 'a',
4909                     href : this.href ? this.href : '#',
4910                     html : this.html ? this.html : ''
4911                 }
4912             ]
4913         };
4914         
4915         if(this.cls){
4916             cfg.cls = this.cls;
4917         }
4918         
4919         if(this.disabled){
4920             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4921         }
4922         
4923         if(this.active){
4924             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4925         }
4926         
4927         return cfg;
4928     },
4929     
4930     initEvents: function() {
4931         
4932         this.el.on('click', this.onClick, this);
4933         
4934     },
4935     onClick : function(e)
4936     {
4937         Roo.log('PaginationItem on click ');
4938         if(this.preventDefault){
4939             e.preventDefault();
4940         }
4941         
4942         if(this.disabled){
4943             return;
4944         }
4945         
4946         this.fireEvent('click', this, e);
4947     }
4948    
4949 });
4950
4951  
4952
4953  /*
4954  * - LGPL
4955  *
4956  * slider
4957  * 
4958  */
4959
4960
4961 /**
4962  * @class Roo.bootstrap.Slider
4963  * @extends Roo.bootstrap.Component
4964  * Bootstrap Slider class
4965  *    
4966  * @constructor
4967  * Create a new Slider
4968  * @param {Object} config The config object
4969  */
4970
4971 Roo.bootstrap.Slider = function(config){
4972     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
4973 };
4974
4975 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
4976     
4977     getAutoCreate : function(){
4978         
4979         var cfg = {
4980             tag: 'div',
4981             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
4982             cn: [
4983                 {
4984                     tag: 'a',
4985                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
4986                 }
4987             ]
4988         };
4989         
4990         return cfg;
4991     }
4992    
4993 });
4994
4995  /*
4996  * Based on:
4997  * Ext JS Library 1.1.1
4998  * Copyright(c) 2006-2007, Ext JS, LLC.
4999  *
5000  * Originally Released Under LGPL - original licence link has changed is not relivant.
5001  *
5002  * Fork - LGPL
5003  * <script type="text/javascript">
5004  */
5005  
5006
5007 /**
5008  * @class Roo.grid.ColumnModel
5009  * @extends Roo.util.Observable
5010  * This is the default implementation of a ColumnModel used by the Grid. It defines
5011  * the columns in the grid.
5012  * <br>Usage:<br>
5013  <pre><code>
5014  var colModel = new Roo.grid.ColumnModel([
5015         {header: "Ticker", width: 60, sortable: true, locked: true},
5016         {header: "Company Name", width: 150, sortable: true},
5017         {header: "Market Cap.", width: 100, sortable: true},
5018         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5019         {header: "Employees", width: 100, sortable: true, resizable: false}
5020  ]);
5021  </code></pre>
5022  * <p>
5023  
5024  * The config options listed for this class are options which may appear in each
5025  * individual column definition.
5026  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5027  * @constructor
5028  * @param {Object} config An Array of column config objects. See this class's
5029  * config objects for details.
5030 */
5031 Roo.grid.ColumnModel = function(config){
5032         /**
5033      * The config passed into the constructor
5034      */
5035     this.config = config;
5036     this.lookup = {};
5037
5038     // if no id, create one
5039     // if the column does not have a dataIndex mapping,
5040     // map it to the order it is in the config
5041     for(var i = 0, len = config.length; i < len; i++){
5042         var c = config[i];
5043         if(typeof c.dataIndex == "undefined"){
5044             c.dataIndex = i;
5045         }
5046         if(typeof c.renderer == "string"){
5047             c.renderer = Roo.util.Format[c.renderer];
5048         }
5049         if(typeof c.id == "undefined"){
5050             c.id = Roo.id();
5051         }
5052         if(c.editor && c.editor.xtype){
5053             c.editor  = Roo.factory(c.editor, Roo.grid);
5054         }
5055         if(c.editor && c.editor.isFormField){
5056             c.editor = new Roo.grid.GridEditor(c.editor);
5057         }
5058         this.lookup[c.id] = c;
5059     }
5060
5061     /**
5062      * The width of columns which have no width specified (defaults to 100)
5063      * @type Number
5064      */
5065     this.defaultWidth = 100;
5066
5067     /**
5068      * Default sortable of columns which have no sortable specified (defaults to false)
5069      * @type Boolean
5070      */
5071     this.defaultSortable = false;
5072
5073     this.addEvents({
5074         /**
5075              * @event widthchange
5076              * Fires when the width of a column changes.
5077              * @param {ColumnModel} this
5078              * @param {Number} columnIndex The column index
5079              * @param {Number} newWidth The new width
5080              */
5081             "widthchange": true,
5082         /**
5083              * @event headerchange
5084              * Fires when the text of a header changes.
5085              * @param {ColumnModel} this
5086              * @param {Number} columnIndex The column index
5087              * @param {Number} newText The new header text
5088              */
5089             "headerchange": true,
5090         /**
5091              * @event hiddenchange
5092              * Fires when a column is hidden or "unhidden".
5093              * @param {ColumnModel} this
5094              * @param {Number} columnIndex The column index
5095              * @param {Boolean} hidden true if hidden, false otherwise
5096              */
5097             "hiddenchange": true,
5098             /**
5099          * @event columnmoved
5100          * Fires when a column is moved.
5101          * @param {ColumnModel} this
5102          * @param {Number} oldIndex
5103          * @param {Number} newIndex
5104          */
5105         "columnmoved" : true,
5106         /**
5107          * @event columlockchange
5108          * Fires when a column's locked state is changed
5109          * @param {ColumnModel} this
5110          * @param {Number} colIndex
5111          * @param {Boolean} locked true if locked
5112          */
5113         "columnlockchange" : true
5114     });
5115     Roo.grid.ColumnModel.superclass.constructor.call(this);
5116 };
5117 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5118     /**
5119      * @cfg {String} header The header text to display in the Grid view.
5120      */
5121     /**
5122      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5123      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5124      * specified, the column's index is used as an index into the Record's data Array.
5125      */
5126     /**
5127      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5128      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5129      */
5130     /**
5131      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5132      * Defaults to the value of the {@link #defaultSortable} property.
5133      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5134      */
5135     /**
5136      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5137      */
5138     /**
5139      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5140      */
5141     /**
5142      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5143      */
5144     /**
5145      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5146      */
5147     /**
5148      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5149      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5150      * default renderer uses the raw data value. If an object is returned (bootstrap only)
5151      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5152      */
5153        /**
5154      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5155      */
5156     /**
5157      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5158      */
5159     /**
5160      * @cfg {String} cursor (Optional)
5161      */
5162     /**
5163      * @cfg {String} tooltip (Optional)
5164      */
5165     /**
5166      * @cfg {Number} xs (Optional)
5167      */
5168     /**
5169      * @cfg {Number} sm (Optional)
5170      */
5171     /**
5172      * @cfg {Number} md (Optional)
5173      */
5174     /**
5175      * @cfg {Number} lg (Optional)
5176      */
5177     /**
5178      * Returns the id of the column at the specified index.
5179      * @param {Number} index The column index
5180      * @return {String} the id
5181      */
5182     getColumnId : function(index){
5183         return this.config[index].id;
5184     },
5185
5186     /**
5187      * Returns the column for a specified id.
5188      * @param {String} id The column id
5189      * @return {Object} the column
5190      */
5191     getColumnById : function(id){
5192         return this.lookup[id];
5193     },
5194
5195     
5196     /**
5197      * Returns the column for a specified dataIndex.
5198      * @param {String} dataIndex The column dataIndex
5199      * @return {Object|Boolean} the column or false if not found
5200      */
5201     getColumnByDataIndex: function(dataIndex){
5202         var index = this.findColumnIndex(dataIndex);
5203         return index > -1 ? this.config[index] : false;
5204     },
5205     
5206     /**
5207      * Returns the index for a specified column id.
5208      * @param {String} id The column id
5209      * @return {Number} the index, or -1 if not found
5210      */
5211     getIndexById : function(id){
5212         for(var i = 0, len = this.config.length; i < len; i++){
5213             if(this.config[i].id == id){
5214                 return i;
5215             }
5216         }
5217         return -1;
5218     },
5219     
5220     /**
5221      * Returns the index for a specified column dataIndex.
5222      * @param {String} dataIndex The column dataIndex
5223      * @return {Number} the index, or -1 if not found
5224      */
5225     
5226     findColumnIndex : function(dataIndex){
5227         for(var i = 0, len = this.config.length; i < len; i++){
5228             if(this.config[i].dataIndex == dataIndex){
5229                 return i;
5230             }
5231         }
5232         return -1;
5233     },
5234     
5235     
5236     moveColumn : function(oldIndex, newIndex){
5237         var c = this.config[oldIndex];
5238         this.config.splice(oldIndex, 1);
5239         this.config.splice(newIndex, 0, c);
5240         this.dataMap = null;
5241         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5242     },
5243
5244     isLocked : function(colIndex){
5245         return this.config[colIndex].locked === true;
5246     },
5247
5248     setLocked : function(colIndex, value, suppressEvent){
5249         if(this.isLocked(colIndex) == value){
5250             return;
5251         }
5252         this.config[colIndex].locked = value;
5253         if(!suppressEvent){
5254             this.fireEvent("columnlockchange", this, colIndex, value);
5255         }
5256     },
5257
5258     getTotalLockedWidth : function(){
5259         var totalWidth = 0;
5260         for(var i = 0; i < this.config.length; i++){
5261             if(this.isLocked(i) && !this.isHidden(i)){
5262                 this.totalWidth += this.getColumnWidth(i);
5263             }
5264         }
5265         return totalWidth;
5266     },
5267
5268     getLockedCount : function(){
5269         for(var i = 0, len = this.config.length; i < len; i++){
5270             if(!this.isLocked(i)){
5271                 return i;
5272             }
5273         }
5274         
5275         return this.config.length;
5276     },
5277
5278     /**
5279      * Returns the number of columns.
5280      * @return {Number}
5281      */
5282     getColumnCount : function(visibleOnly){
5283         if(visibleOnly === true){
5284             var c = 0;
5285             for(var i = 0, len = this.config.length; i < len; i++){
5286                 if(!this.isHidden(i)){
5287                     c++;
5288                 }
5289             }
5290             return c;
5291         }
5292         return this.config.length;
5293     },
5294
5295     /**
5296      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5297      * @param {Function} fn
5298      * @param {Object} scope (optional)
5299      * @return {Array} result
5300      */
5301     getColumnsBy : function(fn, scope){
5302         var r = [];
5303         for(var i = 0, len = this.config.length; i < len; i++){
5304             var c = this.config[i];
5305             if(fn.call(scope||this, c, i) === true){
5306                 r[r.length] = c;
5307             }
5308         }
5309         return r;
5310     },
5311
5312     /**
5313      * Returns true if the specified column is sortable.
5314      * @param {Number} col The column index
5315      * @return {Boolean}
5316      */
5317     isSortable : function(col){
5318         if(typeof this.config[col].sortable == "undefined"){
5319             return this.defaultSortable;
5320         }
5321         return this.config[col].sortable;
5322     },
5323
5324     /**
5325      * Returns the rendering (formatting) function defined for the column.
5326      * @param {Number} col The column index.
5327      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5328      */
5329     getRenderer : function(col){
5330         if(!this.config[col].renderer){
5331             return Roo.grid.ColumnModel.defaultRenderer;
5332         }
5333         return this.config[col].renderer;
5334     },
5335
5336     /**
5337      * Sets the rendering (formatting) function for a column.
5338      * @param {Number} col The column index
5339      * @param {Function} fn The function to use to process the cell's raw data
5340      * to return HTML markup for the grid view. The render function is called with
5341      * the following parameters:<ul>
5342      * <li>Data value.</li>
5343      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5344      * <li>css A CSS style string to apply to the table cell.</li>
5345      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5346      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5347      * <li>Row index</li>
5348      * <li>Column index</li>
5349      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5350      */
5351     setRenderer : function(col, fn){
5352         this.config[col].renderer = fn;
5353     },
5354
5355     /**
5356      * Returns the width for the specified column.
5357      * @param {Number} col The column index
5358      * @return {Number}
5359      */
5360     getColumnWidth : function(col){
5361         return this.config[col].width * 1 || this.defaultWidth;
5362     },
5363
5364     /**
5365      * Sets the width for a column.
5366      * @param {Number} col The column index
5367      * @param {Number} width The new width
5368      */
5369     setColumnWidth : function(col, width, suppressEvent){
5370         this.config[col].width = width;
5371         this.totalWidth = null;
5372         if(!suppressEvent){
5373              this.fireEvent("widthchange", this, col, width);
5374         }
5375     },
5376
5377     /**
5378      * Returns the total width of all columns.
5379      * @param {Boolean} includeHidden True to include hidden column widths
5380      * @return {Number}
5381      */
5382     getTotalWidth : function(includeHidden){
5383         if(!this.totalWidth){
5384             this.totalWidth = 0;
5385             for(var i = 0, len = this.config.length; i < len; i++){
5386                 if(includeHidden || !this.isHidden(i)){
5387                     this.totalWidth += this.getColumnWidth(i);
5388                 }
5389             }
5390         }
5391         return this.totalWidth;
5392     },
5393
5394     /**
5395      * Returns the header for the specified column.
5396      * @param {Number} col The column index
5397      * @return {String}
5398      */
5399     getColumnHeader : function(col){
5400         return this.config[col].header;
5401     },
5402
5403     /**
5404      * Sets the header for a column.
5405      * @param {Number} col The column index
5406      * @param {String} header The new header
5407      */
5408     setColumnHeader : function(col, header){
5409         this.config[col].header = header;
5410         this.fireEvent("headerchange", this, col, header);
5411     },
5412
5413     /**
5414      * Returns the tooltip for the specified column.
5415      * @param {Number} col The column index
5416      * @return {String}
5417      */
5418     getColumnTooltip : function(col){
5419             return this.config[col].tooltip;
5420     },
5421     /**
5422      * Sets the tooltip for a column.
5423      * @param {Number} col The column index
5424      * @param {String} tooltip The new tooltip
5425      */
5426     setColumnTooltip : function(col, tooltip){
5427             this.config[col].tooltip = tooltip;
5428     },
5429
5430     /**
5431      * Returns the dataIndex for the specified column.
5432      * @param {Number} col The column index
5433      * @return {Number}
5434      */
5435     getDataIndex : function(col){
5436         return this.config[col].dataIndex;
5437     },
5438
5439     /**
5440      * Sets the dataIndex for a column.
5441      * @param {Number} col The column index
5442      * @param {Number} dataIndex The new dataIndex
5443      */
5444     setDataIndex : function(col, dataIndex){
5445         this.config[col].dataIndex = dataIndex;
5446     },
5447
5448     
5449     
5450     /**
5451      * Returns true if the cell is editable.
5452      * @param {Number} colIndex The column index
5453      * @param {Number} rowIndex The row index - this is nto actually used..?
5454      * @return {Boolean}
5455      */
5456     isCellEditable : function(colIndex, rowIndex){
5457         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5458     },
5459
5460     /**
5461      * Returns the editor defined for the cell/column.
5462      * return false or null to disable editing.
5463      * @param {Number} colIndex The column index
5464      * @param {Number} rowIndex The row index
5465      * @return {Object}
5466      */
5467     getCellEditor : function(colIndex, rowIndex){
5468         return this.config[colIndex].editor;
5469     },
5470
5471     /**
5472      * Sets if a column is editable.
5473      * @param {Number} col The column index
5474      * @param {Boolean} editable True if the column is editable
5475      */
5476     setEditable : function(col, editable){
5477         this.config[col].editable = editable;
5478     },
5479
5480
5481     /**
5482      * Returns true if the column is hidden.
5483      * @param {Number} colIndex The column index
5484      * @return {Boolean}
5485      */
5486     isHidden : function(colIndex){
5487         return this.config[colIndex].hidden;
5488     },
5489
5490
5491     /**
5492      * Returns true if the column width cannot be changed
5493      */
5494     isFixed : function(colIndex){
5495         return this.config[colIndex].fixed;
5496     },
5497
5498     /**
5499      * Returns true if the column can be resized
5500      * @return {Boolean}
5501      */
5502     isResizable : function(colIndex){
5503         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5504     },
5505     /**
5506      * Sets if a column is hidden.
5507      * @param {Number} colIndex The column index
5508      * @param {Boolean} hidden True if the column is hidden
5509      */
5510     setHidden : function(colIndex, hidden){
5511         this.config[colIndex].hidden = hidden;
5512         this.totalWidth = null;
5513         this.fireEvent("hiddenchange", this, colIndex, hidden);
5514     },
5515
5516     /**
5517      * Sets the editor for a column.
5518      * @param {Number} col The column index
5519      * @param {Object} editor The editor object
5520      */
5521     setEditor : function(col, editor){
5522         this.config[col].editor = editor;
5523     }
5524 });
5525
5526 Roo.grid.ColumnModel.defaultRenderer = function(value){
5527         if(typeof value == "string" && value.length < 1){
5528             return "&#160;";
5529         }
5530         return value;
5531 };
5532
5533 // Alias for backwards compatibility
5534 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5535 /*
5536  * Based on:
5537  * Ext JS Library 1.1.1
5538  * Copyright(c) 2006-2007, Ext JS, LLC.
5539  *
5540  * Originally Released Under LGPL - original licence link has changed is not relivant.
5541  *
5542  * Fork - LGPL
5543  * <script type="text/javascript">
5544  */
5545  
5546 /**
5547  * @class Roo.LoadMask
5548  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5549  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5550  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5551  * element's UpdateManager load indicator and will be destroyed after the initial load.
5552  * @constructor
5553  * Create a new LoadMask
5554  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5555  * @param {Object} config The config object
5556  */
5557 Roo.LoadMask = function(el, config){
5558     this.el = Roo.get(el);
5559     Roo.apply(this, config);
5560     if(this.store){
5561         this.store.on('beforeload', this.onBeforeLoad, this);
5562         this.store.on('load', this.onLoad, this);
5563         this.store.on('loadexception', this.onLoadException, this);
5564         this.removeMask = false;
5565     }else{
5566         var um = this.el.getUpdateManager();
5567         um.showLoadIndicator = false; // disable the default indicator
5568         um.on('beforeupdate', this.onBeforeLoad, this);
5569         um.on('update', this.onLoad, this);
5570         um.on('failure', this.onLoad, this);
5571         this.removeMask = true;
5572     }
5573 };
5574
5575 Roo.LoadMask.prototype = {
5576     /**
5577      * @cfg {Boolean} removeMask
5578      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5579      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5580      */
5581     /**
5582      * @cfg {String} msg
5583      * The text to display in a centered loading message box (defaults to 'Loading...')
5584      */
5585     msg : 'Loading...',
5586     /**
5587      * @cfg {String} msgCls
5588      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5589      */
5590     msgCls : 'x-mask-loading',
5591
5592     /**
5593      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5594      * @type Boolean
5595      */
5596     disabled: false,
5597
5598     /**
5599      * Disables the mask to prevent it from being displayed
5600      */
5601     disable : function(){
5602        this.disabled = true;
5603     },
5604
5605     /**
5606      * Enables the mask so that it can be displayed
5607      */
5608     enable : function(){
5609         this.disabled = false;
5610     },
5611     
5612     onLoadException : function()
5613     {
5614         Roo.log(arguments);
5615         
5616         if (typeof(arguments[3]) != 'undefined') {
5617             Roo.MessageBox.alert("Error loading",arguments[3]);
5618         } 
5619         /*
5620         try {
5621             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5622                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5623             }   
5624         } catch(e) {
5625             
5626         }
5627         */
5628     
5629         
5630         
5631         this.el.unmask(this.removeMask);
5632     },
5633     // private
5634     onLoad : function()
5635     {
5636         this.el.unmask(this.removeMask);
5637     },
5638
5639     // private
5640     onBeforeLoad : function(){
5641         if(!this.disabled){
5642             this.el.mask(this.msg, this.msgCls);
5643         }
5644     },
5645
5646     // private
5647     destroy : function(){
5648         if(this.store){
5649             this.store.un('beforeload', this.onBeforeLoad, this);
5650             this.store.un('load', this.onLoad, this);
5651             this.store.un('loadexception', this.onLoadException, this);
5652         }else{
5653             var um = this.el.getUpdateManager();
5654             um.un('beforeupdate', this.onBeforeLoad, this);
5655             um.un('update', this.onLoad, this);
5656             um.un('failure', this.onLoad, this);
5657         }
5658     }
5659 };/*
5660  * - LGPL
5661  *
5662  * table
5663  * 
5664  */
5665
5666 /**
5667  * @class Roo.bootstrap.Table
5668  * @extends Roo.bootstrap.Component
5669  * Bootstrap Table class
5670  * @cfg {String} cls table class
5671  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5672  * @cfg {String} bgcolor Specifies the background color for a table
5673  * @cfg {Number} border Specifies whether the table cells should have borders or not
5674  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5675  * @cfg {Number} cellspacing Specifies the space between cells
5676  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5677  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5678  * @cfg {String} sortable Specifies that the table should be sortable
5679  * @cfg {String} summary Specifies a summary of the content of a table
5680  * @cfg {Number} width Specifies the width of a table
5681  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5682  * 
5683  * @cfg {boolean} striped Should the rows be alternative striped
5684  * @cfg {boolean} bordered Add borders to the table
5685  * @cfg {boolean} hover Add hover highlighting
5686  * @cfg {boolean} condensed Format condensed
5687  * @cfg {boolean} responsive Format condensed
5688  * @cfg {Boolean} loadMask (true|false) default false
5689  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5690  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5691  * @cfg {Boolean} rowSelection (true|false) default false
5692  * @cfg {Boolean} cellSelection (true|false) default false
5693  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5694  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5695  
5696  * 
5697  * @constructor
5698  * Create a new Table
5699  * @param {Object} config The config object
5700  */
5701
5702 Roo.bootstrap.Table = function(config){
5703     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5704     
5705   
5706     
5707     // BC...
5708     this.rowSelection = (typeof(config.RowSelection) != 'undefined') ? config.RowSelection : this.rowSelection;
5709     this.cellSelection = (typeof(config.CellSelection) != 'undefined') ? config.CellSelection : this.cellSelection;
5710     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5711     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5712     
5713     
5714     if (this.sm) {
5715         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5716         this.sm = this.selModel;
5717         this.sm.xmodule = this.xmodule || false;
5718     }
5719     if (this.cm && typeof(this.cm.config) == 'undefined') {
5720         this.colModel = new Roo.grid.ColumnModel(this.cm);
5721         this.cm = this.colModel;
5722         this.cm.xmodule = this.xmodule || false;
5723     }
5724     if (this.store) {
5725         this.store= Roo.factory(this.store, Roo.data);
5726         this.ds = this.store;
5727         this.ds.xmodule = this.xmodule || false;
5728          
5729     }
5730     if (this.footer && this.store) {
5731         this.footer.dataSource = this.ds;
5732         this.footer = Roo.factory(this.footer);
5733     }
5734     
5735     /** @private */
5736     this.addEvents({
5737         /**
5738          * @event cellclick
5739          * Fires when a cell is clicked
5740          * @param {Roo.bootstrap.Table} this
5741          * @param {Roo.Element} el
5742          * @param {Number} rowIndex
5743          * @param {Number} columnIndex
5744          * @param {Roo.EventObject} e
5745          */
5746         "cellclick" : true,
5747         /**
5748          * @event celldblclick
5749          * Fires when a cell is double clicked
5750          * @param {Roo.bootstrap.Table} this
5751          * @param {Roo.Element} el
5752          * @param {Number} rowIndex
5753          * @param {Number} columnIndex
5754          * @param {Roo.EventObject} e
5755          */
5756         "celldblclick" : true,
5757         /**
5758          * @event rowclick
5759          * Fires when a row is clicked
5760          * @param {Roo.bootstrap.Table} this
5761          * @param {Roo.Element} el
5762          * @param {Number} rowIndex
5763          * @param {Roo.EventObject} e
5764          */
5765         "rowclick" : true,
5766         /**
5767          * @event rowdblclick
5768          * Fires when a row is double clicked
5769          * @param {Roo.bootstrap.Table} this
5770          * @param {Roo.Element} el
5771          * @param {Number} rowIndex
5772          * @param {Roo.EventObject} e
5773          */
5774         "rowdblclick" : true,
5775         /**
5776          * @event mouseover
5777          * Fires when a mouseover occur
5778          * @param {Roo.bootstrap.Table} this
5779          * @param {Roo.Element} el
5780          * @param {Number} rowIndex
5781          * @param {Number} columnIndex
5782          * @param {Roo.EventObject} e
5783          */
5784         "mouseover" : true,
5785         /**
5786          * @event mouseout
5787          * Fires when a mouseout occur
5788          * @param {Roo.bootstrap.Table} this
5789          * @param {Roo.Element} el
5790          * @param {Number} rowIndex
5791          * @param {Number} columnIndex
5792          * @param {Roo.EventObject} e
5793          */
5794         "mouseout" : true,
5795         /**
5796          * @event rowclass
5797          * Fires when a row is rendered, so you can change add a style to it.
5798          * @param {Roo.bootstrap.Table} this
5799          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5800          */
5801         'rowclass' : true,
5802           /**
5803          * @event rowsrendered
5804          * Fires when all the  rows have been rendered
5805          * @param {Roo.bootstrap.Table} this
5806          */
5807         'rowsrendered' : true,
5808         /**
5809          * @event contextmenu
5810          * The raw contextmenu event for the entire grid.
5811          * @param {Roo.EventObject} e
5812          */
5813         "contextmenu" : true,
5814         /**
5815          * @event rowcontextmenu
5816          * Fires when a row is right clicked
5817          * @param {Roo.bootstrap.Table} this
5818          * @param {Number} rowIndex
5819          * @param {Roo.EventObject} e
5820          */
5821         "rowcontextmenu" : true,
5822         /**
5823          * @event cellcontextmenu
5824          * Fires when a cell is right clicked
5825          * @param {Roo.bootstrap.Table} this
5826          * @param {Number} rowIndex
5827          * @param {Number} cellIndex
5828          * @param {Roo.EventObject} e
5829          */
5830          "cellcontextmenu" : true,
5831          /**
5832          * @event headercontextmenu
5833          * Fires when a header is right clicked
5834          * @param {Roo.bootstrap.Table} this
5835          * @param {Number} columnIndex
5836          * @param {Roo.EventObject} e
5837          */
5838         "headercontextmenu" : true
5839     });
5840 };
5841
5842 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5843     
5844     cls: false,
5845     align: false,
5846     bgcolor: false,
5847     border: false,
5848     cellpadding: false,
5849     cellspacing: false,
5850     frame: false,
5851     rules: false,
5852     sortable: false,
5853     summary: false,
5854     width: false,
5855     striped : false,
5856     scrollBody : false,
5857     bordered: false,
5858     hover:  false,
5859     condensed : false,
5860     responsive : false,
5861     sm : false,
5862     cm : false,
5863     store : false,
5864     loadMask : false,
5865     footerShow : true,
5866     headerShow : true,
5867   
5868     rowSelection : false,
5869     cellSelection : false,
5870     layout : false,
5871     
5872     // Roo.Element - the tbody
5873     mainBody: false,
5874     // Roo.Element - thead element
5875     mainHead: false,
5876     
5877     container: false, // used by gridpanel...
5878     
5879     getAutoCreate : function()
5880     {
5881         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5882         
5883         cfg = {
5884             tag: 'table',
5885             cls : 'table',
5886             cn : []
5887         };
5888         if (this.scrollBody) {
5889             cfg.cls += ' table-body-fixed';
5890         }    
5891         if (this.striped) {
5892             cfg.cls += ' table-striped';
5893         }
5894         
5895         if (this.hover) {
5896             cfg.cls += ' table-hover';
5897         }
5898         if (this.bordered) {
5899             cfg.cls += ' table-bordered';
5900         }
5901         if (this.condensed) {
5902             cfg.cls += ' table-condensed';
5903         }
5904         if (this.responsive) {
5905             cfg.cls += ' table-responsive';
5906         }
5907         
5908         if (this.cls) {
5909             cfg.cls+=  ' ' +this.cls;
5910         }
5911         
5912         // this lot should be simplifed...
5913         
5914         if (this.align) {
5915             cfg.align=this.align;
5916         }
5917         if (this.bgcolor) {
5918             cfg.bgcolor=this.bgcolor;
5919         }
5920         if (this.border) {
5921             cfg.border=this.border;
5922         }
5923         if (this.cellpadding) {
5924             cfg.cellpadding=this.cellpadding;
5925         }
5926         if (this.cellspacing) {
5927             cfg.cellspacing=this.cellspacing;
5928         }
5929         if (this.frame) {
5930             cfg.frame=this.frame;
5931         }
5932         if (this.rules) {
5933             cfg.rules=this.rules;
5934         }
5935         if (this.sortable) {
5936             cfg.sortable=this.sortable;
5937         }
5938         if (this.summary) {
5939             cfg.summary=this.summary;
5940         }
5941         if (this.width) {
5942             cfg.width=this.width;
5943         }
5944         if (this.layout) {
5945             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
5946         }
5947         
5948         if(this.store || this.cm){
5949             if(this.headerShow){
5950                 cfg.cn.push(this.renderHeader());
5951             }
5952             
5953             cfg.cn.push(this.renderBody());
5954             
5955             if(this.footerShow){
5956                 cfg.cn.push(this.renderFooter());
5957             }
5958             // where does this come from?
5959             //cfg.cls+=  ' TableGrid';
5960         }
5961         
5962         return { cn : [ cfg ] };
5963     },
5964     
5965     initEvents : function()
5966     {   
5967         if(!this.store || !this.cm){
5968             return;
5969         }
5970         
5971         //Roo.log('initEvents with ds!!!!');
5972         
5973         this.mainBody = this.el.select('tbody', true).first();
5974         this.mainHead = this.el.select('thead', true).first();
5975         
5976         
5977         
5978         var _this = this;
5979         
5980         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
5981             e.on('click', _this.sort, _this);
5982         });
5983         
5984         this.el.on("click", this.onClick, this);
5985         this.el.on("dblclick", this.onDblClick, this);
5986         
5987         // why is this done????? = it breaks dialogs??
5988         //this.parent().el.setStyle('position', 'relative');
5989         
5990         
5991         if (this.footer) {
5992             this.footer.parentId = this.id;
5993             this.footer.onRender(this.el.select('tfoot tr td').first(), null);        
5994         }
5995         
5996         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
5997         
5998         this.store.on('load', this.onLoad, this);
5999         this.store.on('beforeload', this.onBeforeLoad, this);
6000         this.store.on('update', this.onUpdate, this);
6001         this.store.on('add', this.onAdd, this);
6002         
6003         this.el.on("contextmenu", this.onContextMenu, this);
6004         
6005         this.mainBody.on('scroll', this.onBodyScroll, this);
6006         
6007         
6008     },
6009     
6010     onContextMenu : function(e, t)
6011     {
6012         this.processEvent("contextmenu", e);
6013     },
6014     
6015     processEvent : function(name, e)
6016     {
6017         if (name != 'touchstart' ) {
6018             this.fireEvent(name, e);    
6019         }
6020         
6021         var t = e.getTarget();
6022         
6023         var cell = Roo.get(t);
6024         
6025         if(!cell){
6026             return;
6027         }
6028         
6029         if(cell.findParent('tfoot', false, true)){
6030             return;
6031         }
6032         
6033         if(cell.findParent('thead', false, true)){
6034             
6035             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6036                 cell = Roo.get(t).findParent('th', false, true);
6037                 if (!cell) {
6038                     Roo.log("failed to find th in thead?");
6039                     Roo.log(e.getTarget());
6040                     return;
6041                 }
6042             }
6043             
6044             var cellIndex = cell.dom.cellIndex;
6045             
6046             var ename = name == 'touchstart' ? 'click' : name;
6047             this.fireEvent("header" + ename, this, cellIndex, e);
6048             
6049             return;
6050         }
6051         
6052         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6053             cell = Roo.get(t).findParent('td', false, true);
6054             if (!cell) {
6055                 Roo.log("failed to find th in tbody?");
6056                 Roo.log(e.getTarget());
6057                 return;
6058             }
6059         }
6060         
6061         var row = cell.findParent('tr', false, true);
6062         var cellIndex = cell.dom.cellIndex;
6063         var rowIndex = row.dom.rowIndex - 1;
6064         
6065         if(row !== false){
6066             
6067             this.fireEvent("row" + name, this, rowIndex, e);
6068             
6069             if(cell !== false){
6070             
6071                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6072             }
6073         }
6074         
6075     },
6076     
6077     onMouseover : function(e, el)
6078     {
6079         var cell = Roo.get(el);
6080         
6081         if(!cell){
6082             return;
6083         }
6084         
6085         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6086             cell = cell.findParent('td', false, true);
6087         }
6088         
6089         var row = cell.findParent('tr', false, true);
6090         var cellIndex = cell.dom.cellIndex;
6091         var rowIndex = row.dom.rowIndex - 1; // start from 0
6092         
6093         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6094         
6095     },
6096     
6097     onMouseout : function(e, el)
6098     {
6099         var cell = Roo.get(el);
6100         
6101         if(!cell){
6102             return;
6103         }
6104         
6105         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6106             cell = cell.findParent('td', false, true);
6107         }
6108         
6109         var row = cell.findParent('tr', false, true);
6110         var cellIndex = cell.dom.cellIndex;
6111         var rowIndex = row.dom.rowIndex - 1; // start from 0
6112         
6113         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6114         
6115     },
6116     
6117     onClick : function(e, el)
6118     {
6119         var cell = Roo.get(el);
6120         
6121         if(!cell || (!this.cellSelection && !this.rowSelection)){
6122             return;
6123         }
6124         
6125         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6126             cell = cell.findParent('td', false, true);
6127         }
6128         
6129         if(!cell || typeof(cell) == 'undefined'){
6130             return;
6131         }
6132         
6133         var row = cell.findParent('tr', false, true);
6134         
6135         if(!row || typeof(row) == 'undefined'){
6136             return;
6137         }
6138         
6139         var cellIndex = cell.dom.cellIndex;
6140         var rowIndex = this.getRowIndex(row);
6141         
6142         // why??? - should these not be based on SelectionModel?
6143         if(this.cellSelection){
6144             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6145         }
6146         
6147         if(this.rowSelection){
6148             this.fireEvent('rowclick', this, row, rowIndex, e);
6149         }
6150         
6151         
6152     },
6153     
6154     onDblClick : function(e,el)
6155     {
6156         var cell = Roo.get(el);
6157         
6158         if(!cell || (!this.CellSelection && !this.RowSelection)){
6159             return;
6160         }
6161         
6162         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6163             cell = cell.findParent('td', false, true);
6164         }
6165         
6166         if(!cell || typeof(cell) == 'undefined'){
6167             return;
6168         }
6169         
6170         var row = cell.findParent('tr', false, true);
6171         
6172         if(!row || typeof(row) == 'undefined'){
6173             return;
6174         }
6175         
6176         var cellIndex = cell.dom.cellIndex;
6177         var rowIndex = this.getRowIndex(row);
6178         
6179         if(this.CellSelection){
6180             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6181         }
6182         
6183         if(this.RowSelection){
6184             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6185         }
6186     },
6187     
6188     sort : function(e,el)
6189     {
6190         var col = Roo.get(el);
6191         
6192         if(!col.hasClass('sortable')){
6193             return;
6194         }
6195         
6196         var sort = col.attr('sort');
6197         var dir = 'ASC';
6198         
6199         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6200             dir = 'DESC';
6201         }
6202         
6203         this.store.sortInfo = {field : sort, direction : dir};
6204         
6205         if (this.footer) {
6206             Roo.log("calling footer first");
6207             this.footer.onClick('first');
6208         } else {
6209         
6210             this.store.load({ params : { start : 0 } });
6211         }
6212     },
6213     
6214     renderHeader : function()
6215     {
6216         var header = {
6217             tag: 'thead',
6218             cn : []
6219         };
6220         
6221         var cm = this.cm;
6222         this.totalWidth = 0;
6223         
6224         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6225             
6226             var config = cm.config[i];
6227             
6228             var c = {
6229                 tag: 'th',
6230                 style : '',
6231                 html: cm.getColumnHeader(i)
6232             };
6233             
6234             var hh = '';
6235             
6236             if(typeof(config.sortable) != 'undefined' && config.sortable){
6237                 c.cls = 'sortable';
6238                 c.html = '<i class="glyphicon"></i>' + c.html;
6239             }
6240             
6241             if(typeof(config.lgHeader) != 'undefined'){
6242                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6243             }
6244             
6245             if(typeof(config.mdHeader) != 'undefined'){
6246                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6247             }
6248             
6249             if(typeof(config.smHeader) != 'undefined'){
6250                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6251             }
6252             
6253             if(typeof(config.xsHeader) != 'undefined'){
6254                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6255             }
6256             
6257             if(hh.length){
6258                 c.html = hh;
6259             }
6260             
6261             if(typeof(config.tooltip) != 'undefined'){
6262                 c.tooltip = config.tooltip;
6263             }
6264             
6265             if(typeof(config.colspan) != 'undefined'){
6266                 c.colspan = config.colspan;
6267             }
6268             
6269             if(typeof(config.hidden) != 'undefined' && config.hidden){
6270                 c.style += ' display:none;';
6271             }
6272             
6273             if(typeof(config.dataIndex) != 'undefined'){
6274                 c.sort = config.dataIndex;
6275             }
6276             
6277            
6278             
6279             if(typeof(config.align) != 'undefined' && config.align.length){
6280                 c.style += ' text-align:' + config.align + ';';
6281             }
6282             
6283             if(typeof(config.width) != 'undefined'){
6284                 c.style += ' width:' + config.width + 'px;';
6285                 this.totalWidth += config.width;
6286             } else {
6287                 this.totalWidth += 100; // assume minimum of 100 per column?
6288             }
6289             
6290             if(typeof(config.cls) != 'undefined'){
6291                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6292             }
6293             
6294             ['xs','sm','md','lg'].map(function(size){
6295                 
6296                 if(typeof(config[size]) == 'undefined'){
6297                     return;
6298                 }
6299                 
6300                 if (!config[size]) { // 0 = hidden
6301                     c.cls += ' hidden-' + size;
6302                     return;
6303                 }
6304                 
6305                 c.cls += ' col-' + size + '-' + config[size];
6306
6307             });
6308             
6309             header.cn.push(c)
6310         }
6311         
6312         return header;
6313     },
6314     
6315     renderBody : function()
6316     {
6317         var body = {
6318             tag: 'tbody',
6319             cn : [
6320                 {
6321                     tag: 'tr',
6322                     cn : [
6323                         {
6324                             tag : 'td',
6325                             colspan :  this.cm.getColumnCount()
6326                         }
6327                     ]
6328                 }
6329             ]
6330         };
6331         
6332         return body;
6333     },
6334     
6335     renderFooter : function()
6336     {
6337         var footer = {
6338             tag: 'tfoot',
6339             cn : [
6340                 {
6341                     tag: 'tr',
6342                     cn : [
6343                         {
6344                             tag : 'td',
6345                             colspan :  this.cm.getColumnCount()
6346                         }
6347                     ]
6348                 }
6349             ]
6350         };
6351         
6352         return footer;
6353     },
6354     
6355     
6356     
6357     onLoad : function()
6358     {
6359 //        Roo.log('ds onload');
6360         this.clear();
6361         
6362         var _this = this;
6363         var cm = this.cm;
6364         var ds = this.store;
6365         
6366         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6367             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6368             if (_this.store.sortInfo) {
6369                     
6370                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6371                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6372                 }
6373                 
6374                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6375                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6376                 }
6377             }
6378         });
6379         
6380         var tbody =  this.mainBody;
6381               
6382         if(ds.getCount() > 0){
6383             ds.data.each(function(d,rowIndex){
6384                 var row =  this.renderRow(cm, ds, rowIndex);
6385                 
6386                 tbody.createChild(row);
6387                 
6388                 var _this = this;
6389                 
6390                 if(row.cellObjects.length){
6391                     Roo.each(row.cellObjects, function(r){
6392                         _this.renderCellObject(r);
6393                     })
6394                 }
6395                 
6396             }, this);
6397         }
6398         
6399         Roo.each(this.el.select('tbody td', true).elements, function(e){
6400             e.on('mouseover', _this.onMouseover, _this);
6401         });
6402         
6403         Roo.each(this.el.select('tbody td', true).elements, function(e){
6404             e.on('mouseout', _this.onMouseout, _this);
6405         });
6406         this.fireEvent('rowsrendered', this);
6407         //if(this.loadMask){
6408         //    this.maskEl.hide();
6409         //}
6410         
6411         this.autoSize();
6412     },
6413     
6414     
6415     onUpdate : function(ds,record)
6416     {
6417         this.refreshRow(record);
6418     },
6419     
6420     onRemove : function(ds, record, index, isUpdate){
6421         if(isUpdate !== true){
6422             this.fireEvent("beforerowremoved", this, index, record);
6423         }
6424         var bt = this.mainBody.dom;
6425         
6426         var rows = this.el.select('tbody > tr', true).elements;
6427         
6428         if(typeof(rows[index]) != 'undefined'){
6429             bt.removeChild(rows[index].dom);
6430         }
6431         
6432 //        if(bt.rows[index]){
6433 //            bt.removeChild(bt.rows[index]);
6434 //        }
6435         
6436         if(isUpdate !== true){
6437             //this.stripeRows(index);
6438             //this.syncRowHeights(index, index);
6439             //this.layout();
6440             this.fireEvent("rowremoved", this, index, record);
6441         }
6442     },
6443     
6444     onAdd : function(ds, records, rowIndex)
6445     {
6446         //Roo.log('on Add called');
6447         // - note this does not handle multiple adding very well..
6448         var bt = this.mainBody.dom;
6449         for (var i =0 ; i < records.length;i++) {
6450             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6451             //Roo.log(records[i]);
6452             //Roo.log(this.store.getAt(rowIndex+i));
6453             this.insertRow(this.store, rowIndex + i, false);
6454             return;
6455         }
6456         
6457     },
6458     
6459     
6460     refreshRow : function(record){
6461         var ds = this.store, index;
6462         if(typeof record == 'number'){
6463             index = record;
6464             record = ds.getAt(index);
6465         }else{
6466             index = ds.indexOf(record);
6467         }
6468         this.insertRow(ds, index, true);
6469         this.onRemove(ds, record, index+1, true);
6470         //this.syncRowHeights(index, index);
6471         //this.layout();
6472         this.fireEvent("rowupdated", this, index, record);
6473     },
6474     
6475     insertRow : function(dm, rowIndex, isUpdate){
6476         
6477         if(!isUpdate){
6478             this.fireEvent("beforerowsinserted", this, rowIndex);
6479         }
6480             //var s = this.getScrollState();
6481         var row = this.renderRow(this.cm, this.store, rowIndex);
6482         // insert before rowIndex..
6483         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6484         
6485         var _this = this;
6486                 
6487         if(row.cellObjects.length){
6488             Roo.each(row.cellObjects, function(r){
6489                 _this.renderCellObject(r);
6490             })
6491         }
6492             
6493         if(!isUpdate){
6494             this.fireEvent("rowsinserted", this, rowIndex);
6495             //this.syncRowHeights(firstRow, lastRow);
6496             //this.stripeRows(firstRow);
6497             //this.layout();
6498         }
6499         
6500     },
6501     
6502     
6503     getRowDom : function(rowIndex)
6504     {
6505         var rows = this.el.select('tbody > tr', true).elements;
6506         
6507         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6508         
6509     },
6510     // returns the object tree for a tr..
6511   
6512     
6513     renderRow : function(cm, ds, rowIndex) 
6514     {
6515         
6516         var d = ds.getAt(rowIndex);
6517         
6518         var row = {
6519             tag : 'tr',
6520             cn : []
6521         };
6522             
6523         var cellObjects = [];
6524         
6525         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6526             var config = cm.config[i];
6527             
6528             var renderer = cm.getRenderer(i);
6529             var value = '';
6530             var id = false;
6531             
6532             if(typeof(renderer) !== 'undefined'){
6533                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6534             }
6535             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6536             // and are rendered into the cells after the row is rendered - using the id for the element.
6537             
6538             if(typeof(value) === 'object'){
6539                 id = Roo.id();
6540                 cellObjects.push({
6541                     container : id,
6542                     cfg : value 
6543                 })
6544             }
6545             
6546             var rowcfg = {
6547                 record: d,
6548                 rowIndex : rowIndex,
6549                 colIndex : i,
6550                 rowClass : ''
6551             };
6552
6553             this.fireEvent('rowclass', this, rowcfg);
6554             
6555             var td = {
6556                 tag: 'td',
6557                 cls : rowcfg.rowClass,
6558                 style: '',
6559                 html: (typeof(value) === 'object') ? '' : value
6560             };
6561             
6562             if (id) {
6563                 td.id = id;
6564             }
6565             
6566             if(typeof(config.colspan) != 'undefined'){
6567                 td.colspan = config.colspan;
6568             }
6569             
6570             if(typeof(config.hidden) != 'undefined' && config.hidden){
6571                 td.style += ' display:none;';
6572             }
6573             
6574             if(typeof(config.align) != 'undefined' && config.align.length){
6575                 td.style += ' text-align:' + config.align + ';';
6576             }
6577             
6578             if(typeof(config.width) != 'undefined'){
6579                 td.style += ' width:' +  config.width + 'px;';
6580             }
6581             
6582             if(typeof(config.cursor) != 'undefined'){
6583                 td.style += ' cursor:' +  config.cursor + ';';
6584             }
6585             
6586             if(typeof(config.cls) != 'undefined'){
6587                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6588             }
6589             
6590             ['xs','sm','md','lg'].map(function(size){
6591                 
6592                 if(typeof(config[size]) == 'undefined'){
6593                     return;
6594                 }
6595                 
6596                 if (!config[size]) { // 0 = hidden
6597                     td.cls += ' hidden-' + size;
6598                     return;
6599                 }
6600                 
6601                 td.cls += ' col-' + size + '-' + config[size];
6602
6603             });
6604              
6605             row.cn.push(td);
6606            
6607         }
6608         
6609         row.cellObjects = cellObjects;
6610         
6611         return row;
6612           
6613     },
6614     
6615     
6616     
6617     onBeforeLoad : function()
6618     {
6619         //Roo.log('ds onBeforeLoad');
6620         
6621         //this.clear();
6622         
6623         //if(this.loadMask){
6624         //    this.maskEl.show();
6625         //}
6626     },
6627      /**
6628      * Remove all rows
6629      */
6630     clear : function()
6631     {
6632         this.el.select('tbody', true).first().dom.innerHTML = '';
6633     },
6634     /**
6635      * Show or hide a row.
6636      * @param {Number} rowIndex to show or hide
6637      * @param {Boolean} state hide
6638      */
6639     setRowVisibility : function(rowIndex, state)
6640     {
6641         var bt = this.mainBody.dom;
6642         
6643         var rows = this.el.select('tbody > tr', true).elements;
6644         
6645         if(typeof(rows[rowIndex]) == 'undefined'){
6646             return;
6647         }
6648         rows[rowIndex].dom.style.display = state ? '' : 'none';
6649     },
6650     
6651     
6652     getSelectionModel : function(){
6653         if(!this.selModel){
6654             this.selModel = new Roo.bootstrap.Table.RowSelectionModel();
6655         }
6656         return this.selModel;
6657     },
6658     /*
6659      * Render the Roo.bootstrap object from renderder
6660      */
6661     renderCellObject : function(r)
6662     {
6663         var _this = this;
6664         
6665         var t = r.cfg.render(r.container);
6666         
6667         if(r.cfg.cn){
6668             Roo.each(r.cfg.cn, function(c){
6669                 var child = {
6670                     container: t.getChildContainer(),
6671                     cfg: c
6672                 };
6673                 _this.renderCellObject(child);
6674             })
6675         }
6676     },
6677     
6678     getRowIndex : function(row)
6679     {
6680         var rowIndex = -1;
6681         
6682         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6683             if(el != row){
6684                 return;
6685             }
6686             
6687             rowIndex = index;
6688         });
6689         
6690         return rowIndex;
6691     },
6692      /**
6693      * Returns the grid's underlying element = used by panel.Grid
6694      * @return {Element} The element
6695      */
6696     getGridEl : function(){
6697         return this.el;
6698     },
6699      /**
6700      * Forces a resize - used by panel.Grid
6701      * @return {Element} The element
6702      */
6703     autoSize : function(){
6704         //var ctr = Roo.get(this.container.dom.parentElement);
6705         var ctr = Roo.get(this.el.dom);
6706         
6707         var thd = this.getGridEl().select('thead',true).first();
6708         var tbd = this.getGridEl().select('tbody', true).first();
6709         
6710         
6711         var cw = ctr.getWidth();
6712         
6713         if (tbd) {
6714             
6715             tbd.setSize(ctr.getWidth(), ctr.getHeight() - thd.getHeight());
6716             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6717             cw -= barsize;
6718         }
6719         cw = Math.max(cw, this.totalWidth);
6720         this.getGridEl().select('tr',true).setWidth(cw);
6721         
6722         return; // we doe not have a view in this design..
6723         
6724     },
6725     onBodyScroll: function()
6726     {
6727         
6728         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6729         this.mainHead.setStyle({
6730                     'position' : 'relative',
6731                     'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6732         });
6733         
6734         
6735     }
6736 });
6737
6738  
6739
6740  /*
6741  * - LGPL
6742  *
6743  * table cell
6744  * 
6745  */
6746
6747 /**
6748  * @class Roo.bootstrap.TableCell
6749  * @extends Roo.bootstrap.Component
6750  * Bootstrap TableCell class
6751  * @cfg {String} html cell contain text
6752  * @cfg {String} cls cell class
6753  * @cfg {String} tag cell tag (td|th) default td
6754  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6755  * @cfg {String} align Aligns the content in a cell
6756  * @cfg {String} axis Categorizes cells
6757  * @cfg {String} bgcolor Specifies the background color of a cell
6758  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6759  * @cfg {Number} colspan Specifies the number of columns a cell should span
6760  * @cfg {String} headers Specifies one or more header cells a cell is related to
6761  * @cfg {Number} height Sets the height of a cell
6762  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6763  * @cfg {Number} rowspan Sets the number of rows a cell should span
6764  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6765  * @cfg {String} valign Vertical aligns the content in a cell
6766  * @cfg {Number} width Specifies the width of a cell
6767  * 
6768  * @constructor
6769  * Create a new TableCell
6770  * @param {Object} config The config object
6771  */
6772
6773 Roo.bootstrap.TableCell = function(config){
6774     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6775 };
6776
6777 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6778     
6779     html: false,
6780     cls: false,
6781     tag: false,
6782     abbr: false,
6783     align: false,
6784     axis: false,
6785     bgcolor: false,
6786     charoff: false,
6787     colspan: false,
6788     headers: false,
6789     height: false,
6790     nowrap: false,
6791     rowspan: false,
6792     scope: false,
6793     valign: false,
6794     width: false,
6795     
6796     
6797     getAutoCreate : function(){
6798         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6799         
6800         cfg = {
6801             tag: 'td'
6802         };
6803         
6804         if(this.tag){
6805             cfg.tag = this.tag;
6806         }
6807         
6808         if (this.html) {
6809             cfg.html=this.html
6810         }
6811         if (this.cls) {
6812             cfg.cls=this.cls
6813         }
6814         if (this.abbr) {
6815             cfg.abbr=this.abbr
6816         }
6817         if (this.align) {
6818             cfg.align=this.align
6819         }
6820         if (this.axis) {
6821             cfg.axis=this.axis
6822         }
6823         if (this.bgcolor) {
6824             cfg.bgcolor=this.bgcolor
6825         }
6826         if (this.charoff) {
6827             cfg.charoff=this.charoff
6828         }
6829         if (this.colspan) {
6830             cfg.colspan=this.colspan
6831         }
6832         if (this.headers) {
6833             cfg.headers=this.headers
6834         }
6835         if (this.height) {
6836             cfg.height=this.height
6837         }
6838         if (this.nowrap) {
6839             cfg.nowrap=this.nowrap
6840         }
6841         if (this.rowspan) {
6842             cfg.rowspan=this.rowspan
6843         }
6844         if (this.scope) {
6845             cfg.scope=this.scope
6846         }
6847         if (this.valign) {
6848             cfg.valign=this.valign
6849         }
6850         if (this.width) {
6851             cfg.width=this.width
6852         }
6853         
6854         
6855         return cfg;
6856     }
6857    
6858 });
6859
6860  
6861
6862  /*
6863  * - LGPL
6864  *
6865  * table row
6866  * 
6867  */
6868
6869 /**
6870  * @class Roo.bootstrap.TableRow
6871  * @extends Roo.bootstrap.Component
6872  * Bootstrap TableRow class
6873  * @cfg {String} cls row class
6874  * @cfg {String} align Aligns the content in a table row
6875  * @cfg {String} bgcolor Specifies a background color for a table row
6876  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6877  * @cfg {String} valign Vertical aligns the content in a table row
6878  * 
6879  * @constructor
6880  * Create a new TableRow
6881  * @param {Object} config The config object
6882  */
6883
6884 Roo.bootstrap.TableRow = function(config){
6885     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
6886 };
6887
6888 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
6889     
6890     cls: false,
6891     align: false,
6892     bgcolor: false,
6893     charoff: false,
6894     valign: false,
6895     
6896     getAutoCreate : function(){
6897         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
6898         
6899         cfg = {
6900             tag: 'tr'
6901         };
6902             
6903         if(this.cls){
6904             cfg.cls = this.cls;
6905         }
6906         if(this.align){
6907             cfg.align = this.align;
6908         }
6909         if(this.bgcolor){
6910             cfg.bgcolor = this.bgcolor;
6911         }
6912         if(this.charoff){
6913             cfg.charoff = this.charoff;
6914         }
6915         if(this.valign){
6916             cfg.valign = this.valign;
6917         }
6918         
6919         return cfg;
6920     }
6921    
6922 });
6923
6924  
6925
6926  /*
6927  * - LGPL
6928  *
6929  * table body
6930  * 
6931  */
6932
6933 /**
6934  * @class Roo.bootstrap.TableBody
6935  * @extends Roo.bootstrap.Component
6936  * Bootstrap TableBody class
6937  * @cfg {String} cls element class
6938  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
6939  * @cfg {String} align Aligns the content inside the element
6940  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
6941  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
6942  * 
6943  * @constructor
6944  * Create a new TableBody
6945  * @param {Object} config The config object
6946  */
6947
6948 Roo.bootstrap.TableBody = function(config){
6949     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
6950 };
6951
6952 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
6953     
6954     cls: false,
6955     tag: false,
6956     align: false,
6957     charoff: false,
6958     valign: false,
6959     
6960     getAutoCreate : function(){
6961         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
6962         
6963         cfg = {
6964             tag: 'tbody'
6965         };
6966             
6967         if (this.cls) {
6968             cfg.cls=this.cls
6969         }
6970         if(this.tag){
6971             cfg.tag = this.tag;
6972         }
6973         
6974         if(this.align){
6975             cfg.align = this.align;
6976         }
6977         if(this.charoff){
6978             cfg.charoff = this.charoff;
6979         }
6980         if(this.valign){
6981             cfg.valign = this.valign;
6982         }
6983         
6984         return cfg;
6985     }
6986     
6987     
6988 //    initEvents : function()
6989 //    {
6990 //        
6991 //        if(!this.store){
6992 //            return;
6993 //        }
6994 //        
6995 //        this.store = Roo.factory(this.store, Roo.data);
6996 //        this.store.on('load', this.onLoad, this);
6997 //        
6998 //        this.store.load();
6999 //        
7000 //    },
7001 //    
7002 //    onLoad: function () 
7003 //    {   
7004 //        this.fireEvent('load', this);
7005 //    }
7006 //    
7007 //   
7008 });
7009
7010  
7011
7012  /*
7013  * Based on:
7014  * Ext JS Library 1.1.1
7015  * Copyright(c) 2006-2007, Ext JS, LLC.
7016  *
7017  * Originally Released Under LGPL - original licence link has changed is not relivant.
7018  *
7019  * Fork - LGPL
7020  * <script type="text/javascript">
7021  */
7022
7023 // as we use this in bootstrap.
7024 Roo.namespace('Roo.form');
7025  /**
7026  * @class Roo.form.Action
7027  * Internal Class used to handle form actions
7028  * @constructor
7029  * @param {Roo.form.BasicForm} el The form element or its id
7030  * @param {Object} config Configuration options
7031  */
7032
7033  
7034  
7035 // define the action interface
7036 Roo.form.Action = function(form, options){
7037     this.form = form;
7038     this.options = options || {};
7039 };
7040 /**
7041  * Client Validation Failed
7042  * @const 
7043  */
7044 Roo.form.Action.CLIENT_INVALID = 'client';
7045 /**
7046  * Server Validation Failed
7047  * @const 
7048  */
7049 Roo.form.Action.SERVER_INVALID = 'server';
7050  /**
7051  * Connect to Server Failed
7052  * @const 
7053  */
7054 Roo.form.Action.CONNECT_FAILURE = 'connect';
7055 /**
7056  * Reading Data from Server Failed
7057  * @const 
7058  */
7059 Roo.form.Action.LOAD_FAILURE = 'load';
7060
7061 Roo.form.Action.prototype = {
7062     type : 'default',
7063     failureType : undefined,
7064     response : undefined,
7065     result : undefined,
7066
7067     // interface method
7068     run : function(options){
7069
7070     },
7071
7072     // interface method
7073     success : function(response){
7074
7075     },
7076
7077     // interface method
7078     handleResponse : function(response){
7079
7080     },
7081
7082     // default connection failure
7083     failure : function(response){
7084         
7085         this.response = response;
7086         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7087         this.form.afterAction(this, false);
7088     },
7089
7090     processResponse : function(response){
7091         this.response = response;
7092         if(!response.responseText){
7093             return true;
7094         }
7095         this.result = this.handleResponse(response);
7096         return this.result;
7097     },
7098
7099     // utility functions used internally
7100     getUrl : function(appendParams){
7101         var url = this.options.url || this.form.url || this.form.el.dom.action;
7102         if(appendParams){
7103             var p = this.getParams();
7104             if(p){
7105                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7106             }
7107         }
7108         return url;
7109     },
7110
7111     getMethod : function(){
7112         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7113     },
7114
7115     getParams : function(){
7116         var bp = this.form.baseParams;
7117         var p = this.options.params;
7118         if(p){
7119             if(typeof p == "object"){
7120                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7121             }else if(typeof p == 'string' && bp){
7122                 p += '&' + Roo.urlEncode(bp);
7123             }
7124         }else if(bp){
7125             p = Roo.urlEncode(bp);
7126         }
7127         return p;
7128     },
7129
7130     createCallback : function(){
7131         return {
7132             success: this.success,
7133             failure: this.failure,
7134             scope: this,
7135             timeout: (this.form.timeout*1000),
7136             upload: this.form.fileUpload ? this.success : undefined
7137         };
7138     }
7139 };
7140
7141 Roo.form.Action.Submit = function(form, options){
7142     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7143 };
7144
7145 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7146     type : 'submit',
7147
7148     haveProgress : false,
7149     uploadComplete : false,
7150     
7151     // uploadProgress indicator.
7152     uploadProgress : function()
7153     {
7154         if (!this.form.progressUrl) {
7155             return;
7156         }
7157         
7158         if (!this.haveProgress) {
7159             Roo.MessageBox.progress("Uploading", "Uploading");
7160         }
7161         if (this.uploadComplete) {
7162            Roo.MessageBox.hide();
7163            return;
7164         }
7165         
7166         this.haveProgress = true;
7167    
7168         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7169         
7170         var c = new Roo.data.Connection();
7171         c.request({
7172             url : this.form.progressUrl,
7173             params: {
7174                 id : uid
7175             },
7176             method: 'GET',
7177             success : function(req){
7178                //console.log(data);
7179                 var rdata = false;
7180                 var edata;
7181                 try  {
7182                    rdata = Roo.decode(req.responseText)
7183                 } catch (e) {
7184                     Roo.log("Invalid data from server..");
7185                     Roo.log(edata);
7186                     return;
7187                 }
7188                 if (!rdata || !rdata.success) {
7189                     Roo.log(rdata);
7190                     Roo.MessageBox.alert(Roo.encode(rdata));
7191                     return;
7192                 }
7193                 var data = rdata.data;
7194                 
7195                 if (this.uploadComplete) {
7196                    Roo.MessageBox.hide();
7197                    return;
7198                 }
7199                    
7200                 if (data){
7201                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7202                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7203                     );
7204                 }
7205                 this.uploadProgress.defer(2000,this);
7206             },
7207        
7208             failure: function(data) {
7209                 Roo.log('progress url failed ');
7210                 Roo.log(data);
7211             },
7212             scope : this
7213         });
7214            
7215     },
7216     
7217     
7218     run : function()
7219     {
7220         // run get Values on the form, so it syncs any secondary forms.
7221         this.form.getValues();
7222         
7223         var o = this.options;
7224         var method = this.getMethod();
7225         var isPost = method == 'POST';
7226         if(o.clientValidation === false || this.form.isValid()){
7227             
7228             if (this.form.progressUrl) {
7229                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7230                     (new Date() * 1) + '' + Math.random());
7231                     
7232             } 
7233             
7234             
7235             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7236                 form:this.form.el.dom,
7237                 url:this.getUrl(!isPost),
7238                 method: method,
7239                 params:isPost ? this.getParams() : null,
7240                 isUpload: this.form.fileUpload
7241             }));
7242             
7243             this.uploadProgress();
7244
7245         }else if (o.clientValidation !== false){ // client validation failed
7246             this.failureType = Roo.form.Action.CLIENT_INVALID;
7247             this.form.afterAction(this, false);
7248         }
7249     },
7250
7251     success : function(response)
7252     {
7253         this.uploadComplete= true;
7254         if (this.haveProgress) {
7255             Roo.MessageBox.hide();
7256         }
7257         
7258         
7259         var result = this.processResponse(response);
7260         if(result === true || result.success){
7261             this.form.afterAction(this, true);
7262             return;
7263         }
7264         if(result.errors){
7265             this.form.markInvalid(result.errors);
7266             this.failureType = Roo.form.Action.SERVER_INVALID;
7267         }
7268         this.form.afterAction(this, false);
7269     },
7270     failure : function(response)
7271     {
7272         this.uploadComplete= true;
7273         if (this.haveProgress) {
7274             Roo.MessageBox.hide();
7275         }
7276         
7277         this.response = response;
7278         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7279         this.form.afterAction(this, false);
7280     },
7281     
7282     handleResponse : function(response){
7283         if(this.form.errorReader){
7284             var rs = this.form.errorReader.read(response);
7285             var errors = [];
7286             if(rs.records){
7287                 for(var i = 0, len = rs.records.length; i < len; i++) {
7288                     var r = rs.records[i];
7289                     errors[i] = r.data;
7290                 }
7291             }
7292             if(errors.length < 1){
7293                 errors = null;
7294             }
7295             return {
7296                 success : rs.success,
7297                 errors : errors
7298             };
7299         }
7300         var ret = false;
7301         try {
7302             ret = Roo.decode(response.responseText);
7303         } catch (e) {
7304             ret = {
7305                 success: false,
7306                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7307                 errors : []
7308             };
7309         }
7310         return ret;
7311         
7312     }
7313 });
7314
7315
7316 Roo.form.Action.Load = function(form, options){
7317     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7318     this.reader = this.form.reader;
7319 };
7320
7321 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7322     type : 'load',
7323
7324     run : function(){
7325         
7326         Roo.Ajax.request(Roo.apply(
7327                 this.createCallback(), {
7328                     method:this.getMethod(),
7329                     url:this.getUrl(false),
7330                     params:this.getParams()
7331         }));
7332     },
7333
7334     success : function(response){
7335         
7336         var result = this.processResponse(response);
7337         if(result === true || !result.success || !result.data){
7338             this.failureType = Roo.form.Action.LOAD_FAILURE;
7339             this.form.afterAction(this, false);
7340             return;
7341         }
7342         this.form.clearInvalid();
7343         this.form.setValues(result.data);
7344         this.form.afterAction(this, true);
7345     },
7346
7347     handleResponse : function(response){
7348         if(this.form.reader){
7349             var rs = this.form.reader.read(response);
7350             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7351             return {
7352                 success : rs.success,
7353                 data : data
7354             };
7355         }
7356         return Roo.decode(response.responseText);
7357     }
7358 });
7359
7360 Roo.form.Action.ACTION_TYPES = {
7361     'load' : Roo.form.Action.Load,
7362     'submit' : Roo.form.Action.Submit
7363 };/*
7364  * - LGPL
7365  *
7366  * form
7367  * 
7368  */
7369
7370 /**
7371  * @class Roo.bootstrap.Form
7372  * @extends Roo.bootstrap.Component
7373  * Bootstrap Form class
7374  * @cfg {String} method  GET | POST (default POST)
7375  * @cfg {String} labelAlign top | left (default top)
7376  * @cfg {String} align left  | right - for navbars
7377  * @cfg {Boolean} loadMask load mask when submit (default true)
7378
7379  * 
7380  * @constructor
7381  * Create a new Form
7382  * @param {Object} config The config object
7383  */
7384
7385
7386 Roo.bootstrap.Form = function(config){
7387     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7388     this.addEvents({
7389         /**
7390          * @event clientvalidation
7391          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7392          * @param {Form} this
7393          * @param {Boolean} valid true if the form has passed client-side validation
7394          */
7395         clientvalidation: true,
7396         /**
7397          * @event beforeaction
7398          * Fires before any action is performed. Return false to cancel the action.
7399          * @param {Form} this
7400          * @param {Action} action The action to be performed
7401          */
7402         beforeaction: true,
7403         /**
7404          * @event actionfailed
7405          * Fires when an action fails.
7406          * @param {Form} this
7407          * @param {Action} action The action that failed
7408          */
7409         actionfailed : true,
7410         /**
7411          * @event actioncomplete
7412          * Fires when an action is completed.
7413          * @param {Form} this
7414          * @param {Action} action The action that completed
7415          */
7416         actioncomplete : true
7417     });
7418     
7419 };
7420
7421 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7422       
7423      /**
7424      * @cfg {String} method
7425      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7426      */
7427     method : 'POST',
7428     /**
7429      * @cfg {String} url
7430      * The URL to use for form actions if one isn't supplied in the action options.
7431      */
7432     /**
7433      * @cfg {Boolean} fileUpload
7434      * Set to true if this form is a file upload.
7435      */
7436      
7437     /**
7438      * @cfg {Object} baseParams
7439      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7440      */
7441       
7442     /**
7443      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7444      */
7445     timeout: 30,
7446     /**
7447      * @cfg {Sting} align (left|right) for navbar forms
7448      */
7449     align : 'left',
7450
7451     // private
7452     activeAction : null,
7453  
7454     /**
7455      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7456      * element by passing it or its id or mask the form itself by passing in true.
7457      * @type Mixed
7458      */
7459     waitMsgTarget : false,
7460     
7461     loadMask : true,
7462     
7463     getAutoCreate : function(){
7464         
7465         var cfg = {
7466             tag: 'form',
7467             method : this.method || 'POST',
7468             id : this.id || Roo.id(),
7469             cls : ''
7470         };
7471         if (this.parent().xtype.match(/^Nav/)) {
7472             cfg.cls = 'navbar-form navbar-' + this.align;
7473             
7474         }
7475         
7476         if (this.labelAlign == 'left' ) {
7477             cfg.cls += ' form-horizontal';
7478         }
7479         
7480         
7481         return cfg;
7482     },
7483     initEvents : function()
7484     {
7485         this.el.on('submit', this.onSubmit, this);
7486         // this was added as random key presses on the form where triggering form submit.
7487         this.el.on('keypress', function(e) {
7488             if (e.getCharCode() != 13) {
7489                 return true;
7490             }
7491             // we might need to allow it for textareas.. and some other items.
7492             // check e.getTarget().
7493             
7494             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7495                 return true;
7496             }
7497         
7498             Roo.log("keypress blocked");
7499             
7500             e.preventDefault();
7501             return false;
7502         });
7503         
7504     },
7505     // private
7506     onSubmit : function(e){
7507         e.stopEvent();
7508     },
7509     
7510      /**
7511      * Returns true if client-side validation on the form is successful.
7512      * @return Boolean
7513      */
7514     isValid : function(){
7515         var items = this.getItems();
7516         var valid = true;
7517         items.each(function(f){
7518            if(!f.validate()){
7519                valid = false;
7520                
7521            }
7522         });
7523         return valid;
7524     },
7525     /**
7526      * Returns true if any fields in this form have changed since their original load.
7527      * @return Boolean
7528      */
7529     isDirty : function(){
7530         var dirty = false;
7531         var items = this.getItems();
7532         items.each(function(f){
7533            if(f.isDirty()){
7534                dirty = true;
7535                return false;
7536            }
7537            return true;
7538         });
7539         return dirty;
7540     },
7541      /**
7542      * Performs a predefined action (submit or load) or custom actions you define on this form.
7543      * @param {String} actionName The name of the action type
7544      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7545      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7546      * accept other config options):
7547      * <pre>
7548 Property          Type             Description
7549 ----------------  ---------------  ----------------------------------------------------------------------------------
7550 url               String           The url for the action (defaults to the form's url)
7551 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7552 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7553 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7554                                    validate the form on the client (defaults to false)
7555      * </pre>
7556      * @return {BasicForm} this
7557      */
7558     doAction : function(action, options){
7559         if(typeof action == 'string'){
7560             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7561         }
7562         if(this.fireEvent('beforeaction', this, action) !== false){
7563             this.beforeAction(action);
7564             action.run.defer(100, action);
7565         }
7566         return this;
7567     },
7568     
7569     // private
7570     beforeAction : function(action){
7571         var o = action.options;
7572         
7573         if(this.loadMask){
7574             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7575         }
7576         // not really supported yet.. ??
7577         
7578         //if(this.waitMsgTarget === true){
7579         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7580         //}else if(this.waitMsgTarget){
7581         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7582         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7583         //}else {
7584         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7585        // }
7586          
7587     },
7588
7589     // private
7590     afterAction : function(action, success){
7591         this.activeAction = null;
7592         var o = action.options;
7593         
7594         //if(this.waitMsgTarget === true){
7595             this.el.unmask();
7596         //}else if(this.waitMsgTarget){
7597         //    this.waitMsgTarget.unmask();
7598         //}else{
7599         //    Roo.MessageBox.updateProgress(1);
7600         //    Roo.MessageBox.hide();
7601        // }
7602         // 
7603         if(success){
7604             if(o.reset){
7605                 this.reset();
7606             }
7607             Roo.callback(o.success, o.scope, [this, action]);
7608             this.fireEvent('actioncomplete', this, action);
7609             
7610         }else{
7611             
7612             // failure condition..
7613             // we have a scenario where updates need confirming.
7614             // eg. if a locking scenario exists..
7615             // we look for { errors : { needs_confirm : true }} in the response.
7616             if (
7617                 (typeof(action.result) != 'undefined')  &&
7618                 (typeof(action.result.errors) != 'undefined')  &&
7619                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7620            ){
7621                 var _t = this;
7622                 Roo.log("not supported yet");
7623                  /*
7624                 
7625                 Roo.MessageBox.confirm(
7626                     "Change requires confirmation",
7627                     action.result.errorMsg,
7628                     function(r) {
7629                         if (r != 'yes') {
7630                             return;
7631                         }
7632                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7633                     }
7634                     
7635                 );
7636                 */
7637                 
7638                 
7639                 return;
7640             }
7641             
7642             Roo.callback(o.failure, o.scope, [this, action]);
7643             // show an error message if no failed handler is set..
7644             if (!this.hasListener('actionfailed')) {
7645                 Roo.log("need to add dialog support");
7646                 /*
7647                 Roo.MessageBox.alert("Error",
7648                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7649                         action.result.errorMsg :
7650                         "Saving Failed, please check your entries or try again"
7651                 );
7652                 */
7653             }
7654             
7655             this.fireEvent('actionfailed', this, action);
7656         }
7657         
7658     },
7659     /**
7660      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7661      * @param {String} id The value to search for
7662      * @return Field
7663      */
7664     findField : function(id){
7665         var items = this.getItems();
7666         var field = items.get(id);
7667         if(!field){
7668              items.each(function(f){
7669                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7670                     field = f;
7671                     return false;
7672                 }
7673                 return true;
7674             });
7675         }
7676         return field || null;
7677     },
7678      /**
7679      * Mark fields in this form invalid in bulk.
7680      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7681      * @return {BasicForm} this
7682      */
7683     markInvalid : function(errors){
7684         if(errors instanceof Array){
7685             for(var i = 0, len = errors.length; i < len; i++){
7686                 var fieldError = errors[i];
7687                 var f = this.findField(fieldError.id);
7688                 if(f){
7689                     f.markInvalid(fieldError.msg);
7690                 }
7691             }
7692         }else{
7693             var field, id;
7694             for(id in errors){
7695                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7696                     field.markInvalid(errors[id]);
7697                 }
7698             }
7699         }
7700         //Roo.each(this.childForms || [], function (f) {
7701         //    f.markInvalid(errors);
7702         //});
7703         
7704         return this;
7705     },
7706
7707     /**
7708      * Set values for fields in this form in bulk.
7709      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7710      * @return {BasicForm} this
7711      */
7712     setValues : function(values){
7713         if(values instanceof Array){ // array of objects
7714             for(var i = 0, len = values.length; i < len; i++){
7715                 var v = values[i];
7716                 var f = this.findField(v.id);
7717                 if(f){
7718                     f.setValue(v.value);
7719                     if(this.trackResetOnLoad){
7720                         f.originalValue = f.getValue();
7721                     }
7722                 }
7723             }
7724         }else{ // object hash
7725             var field, id;
7726             for(id in values){
7727                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7728                     
7729                     if (field.setFromData && 
7730                         field.valueField && 
7731                         field.displayField &&
7732                         // combos' with local stores can 
7733                         // be queried via setValue()
7734                         // to set their value..
7735                         (field.store && !field.store.isLocal)
7736                         ) {
7737                         // it's a combo
7738                         var sd = { };
7739                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7740                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7741                         field.setFromData(sd);
7742                         
7743                     } else {
7744                         field.setValue(values[id]);
7745                     }
7746                     
7747                     
7748                     if(this.trackResetOnLoad){
7749                         field.originalValue = field.getValue();
7750                     }
7751                 }
7752             }
7753         }
7754          
7755         //Roo.each(this.childForms || [], function (f) {
7756         //    f.setValues(values);
7757         //});
7758                 
7759         return this;
7760     },
7761
7762     /**
7763      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7764      * they are returned as an array.
7765      * @param {Boolean} asString
7766      * @return {Object}
7767      */
7768     getValues : function(asString){
7769         //if (this.childForms) {
7770             // copy values from the child forms
7771         //    Roo.each(this.childForms, function (f) {
7772         //        this.setValues(f.getValues());
7773         //    }, this);
7774         //}
7775         
7776         
7777         
7778         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7779         if(asString === true){
7780             return fs;
7781         }
7782         return Roo.urlDecode(fs);
7783     },
7784     
7785     /**
7786      * Returns the fields in this form as an object with key/value pairs. 
7787      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7788      * @return {Object}
7789      */
7790     getFieldValues : function(with_hidden)
7791     {
7792         var items = this.getItems();
7793         var ret = {};
7794         items.each(function(f){
7795             if (!f.getName()) {
7796                 return;
7797             }
7798             var v = f.getValue();
7799             if (f.inputType =='radio') {
7800                 if (typeof(ret[f.getName()]) == 'undefined') {
7801                     ret[f.getName()] = ''; // empty..
7802                 }
7803                 
7804                 if (!f.el.dom.checked) {
7805                     return;
7806                     
7807                 }
7808                 v = f.el.dom.value;
7809                 
7810             }
7811             
7812             // not sure if this supported any more..
7813             if ((typeof(v) == 'object') && f.getRawValue) {
7814                 v = f.getRawValue() ; // dates..
7815             }
7816             // combo boxes where name != hiddenName...
7817             if (f.name != f.getName()) {
7818                 ret[f.name] = f.getRawValue();
7819             }
7820             ret[f.getName()] = v;
7821         });
7822         
7823         return ret;
7824     },
7825
7826     /**
7827      * Clears all invalid messages in this form.
7828      * @return {BasicForm} this
7829      */
7830     clearInvalid : function(){
7831         var items = this.getItems();
7832         
7833         items.each(function(f){
7834            f.clearInvalid();
7835         });
7836         
7837         
7838         
7839         return this;
7840     },
7841
7842     /**
7843      * Resets this form.
7844      * @return {BasicForm} this
7845      */
7846     reset : function(){
7847         var items = this.getItems();
7848         items.each(function(f){
7849             f.reset();
7850         });
7851         
7852         Roo.each(this.childForms || [], function (f) {
7853             f.reset();
7854         });
7855        
7856         
7857         return this;
7858     },
7859     getItems : function()
7860     {
7861         var r=new Roo.util.MixedCollection(false, function(o){
7862             return o.id || (o.id = Roo.id());
7863         });
7864         var iter = function(el) {
7865             if (el.inputEl) {
7866                 r.add(el);
7867             }
7868             if (!el.items) {
7869                 return;
7870             }
7871             Roo.each(el.items,function(e) {
7872                 iter(e);
7873             });
7874             
7875             
7876         };
7877         
7878         iter(this);
7879         return r;
7880         
7881         
7882         
7883         
7884     }
7885     
7886 });
7887
7888  
7889 /*
7890  * Based on:
7891  * Ext JS Library 1.1.1
7892  * Copyright(c) 2006-2007, Ext JS, LLC.
7893  *
7894  * Originally Released Under LGPL - original licence link has changed is not relivant.
7895  *
7896  * Fork - LGPL
7897  * <script type="text/javascript">
7898  */
7899 /**
7900  * @class Roo.form.VTypes
7901  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
7902  * @singleton
7903  */
7904 Roo.form.VTypes = function(){
7905     // closure these in so they are only created once.
7906     var alpha = /^[a-zA-Z_]+$/;
7907     var alphanum = /^[a-zA-Z0-9_]+$/;
7908     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
7909     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
7910
7911     // All these messages and functions are configurable
7912     return {
7913         /**
7914          * The function used to validate email addresses
7915          * @param {String} value The email address
7916          */
7917         'email' : function(v){
7918             return email.test(v);
7919         },
7920         /**
7921          * The error text to display when the email validation function returns false
7922          * @type String
7923          */
7924         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
7925         /**
7926          * The keystroke filter mask to be applied on email input
7927          * @type RegExp
7928          */
7929         'emailMask' : /[a-z0-9_\.\-@]/i,
7930
7931         /**
7932          * The function used to validate URLs
7933          * @param {String} value The URL
7934          */
7935         'url' : function(v){
7936             return url.test(v);
7937         },
7938         /**
7939          * The error text to display when the url validation function returns false
7940          * @type String
7941          */
7942         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
7943         
7944         /**
7945          * The function used to validate alpha values
7946          * @param {String} value The value
7947          */
7948         'alpha' : function(v){
7949             return alpha.test(v);
7950         },
7951         /**
7952          * The error text to display when the alpha validation function returns false
7953          * @type String
7954          */
7955         'alphaText' : 'This field should only contain letters and _',
7956         /**
7957          * The keystroke filter mask to be applied on alpha input
7958          * @type RegExp
7959          */
7960         'alphaMask' : /[a-z_]/i,
7961
7962         /**
7963          * The function used to validate alphanumeric values
7964          * @param {String} value The value
7965          */
7966         'alphanum' : function(v){
7967             return alphanum.test(v);
7968         },
7969         /**
7970          * The error text to display when the alphanumeric validation function returns false
7971          * @type String
7972          */
7973         'alphanumText' : 'This field should only contain letters, numbers and _',
7974         /**
7975          * The keystroke filter mask to be applied on alphanumeric input
7976          * @type RegExp
7977          */
7978         'alphanumMask' : /[a-z0-9_]/i
7979     };
7980 }();/*
7981  * - LGPL
7982  *
7983  * Input
7984  * 
7985  */
7986
7987 /**
7988  * @class Roo.bootstrap.Input
7989  * @extends Roo.bootstrap.Component
7990  * Bootstrap Input class
7991  * @cfg {Boolean} disabled is it disabled
7992  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
7993  * @cfg {String} name name of the input
7994  * @cfg {string} fieldLabel - the label associated
7995  * @cfg {string} placeholder - placeholder to put in text.
7996  * @cfg {string}  before - input group add on before
7997  * @cfg {string} after - input group add on after
7998  * @cfg {string} size - (lg|sm) or leave empty..
7999  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8000  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8001  * @cfg {Number} md colspan out of 12 for computer-sized screens
8002  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8003  * @cfg {string} value default value of the input
8004  * @cfg {Number} labelWidth set the width of label (0-12)
8005  * @cfg {String} labelAlign (top|left)
8006  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8007  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8008
8009  * @cfg {String} align (left|center|right) Default left
8010  * @cfg {Boolean} forceFeedback (true|false) Default false
8011  * 
8012  * 
8013  * 
8014  * 
8015  * @constructor
8016  * Create a new Input
8017  * @param {Object} config The config object
8018  */
8019
8020 Roo.bootstrap.Input = function(config){
8021     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8022    
8023         this.addEvents({
8024             /**
8025              * @event focus
8026              * Fires when this field receives input focus.
8027              * @param {Roo.form.Field} this
8028              */
8029             focus : true,
8030             /**
8031              * @event blur
8032              * Fires when this field loses input focus.
8033              * @param {Roo.form.Field} this
8034              */
8035             blur : true,
8036             /**
8037              * @event specialkey
8038              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8039              * {@link Roo.EventObject#getKey} to determine which key was pressed.
8040              * @param {Roo.form.Field} this
8041              * @param {Roo.EventObject} e The event object
8042              */
8043             specialkey : true,
8044             /**
8045              * @event change
8046              * Fires just before the field blurs if the field value has changed.
8047              * @param {Roo.form.Field} this
8048              * @param {Mixed} newValue The new value
8049              * @param {Mixed} oldValue The original value
8050              */
8051             change : true,
8052             /**
8053              * @event invalid
8054              * Fires after the field has been marked as invalid.
8055              * @param {Roo.form.Field} this
8056              * @param {String} msg The validation message
8057              */
8058             invalid : true,
8059             /**
8060              * @event valid
8061              * Fires after the field has been validated with no errors.
8062              * @param {Roo.form.Field} this
8063              */
8064             valid : true,
8065              /**
8066              * @event keyup
8067              * Fires after the key up
8068              * @param {Roo.form.Field} this
8069              * @param {Roo.EventObject}  e The event Object
8070              */
8071             keyup : true
8072         });
8073 };
8074
8075 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8076      /**
8077      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8078       automatic validation (defaults to "keyup").
8079      */
8080     validationEvent : "keyup",
8081      /**
8082      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8083      */
8084     validateOnBlur : true,
8085     /**
8086      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8087      */
8088     validationDelay : 250,
8089      /**
8090      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8091      */
8092     focusClass : "x-form-focus",  // not needed???
8093     
8094        
8095     /**
8096      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8097      */
8098     invalidClass : "has-warning",
8099     
8100     /**
8101      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8102      */
8103     validClass : "has-success",
8104     
8105     /**
8106      * @cfg {Boolean} hasFeedback (true|false) default true
8107      */
8108     hasFeedback : true,
8109     
8110     /**
8111      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8112      */
8113     invalidFeedbackClass : "glyphicon-warning-sign",
8114     
8115     /**
8116      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8117      */
8118     validFeedbackClass : "glyphicon-ok",
8119     
8120     /**
8121      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8122      */
8123     selectOnFocus : false,
8124     
8125      /**
8126      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8127      */
8128     maskRe : null,
8129        /**
8130      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8131      */
8132     vtype : null,
8133     
8134       /**
8135      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8136      */
8137     disableKeyFilter : false,
8138     
8139        /**
8140      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8141      */
8142     disabled : false,
8143      /**
8144      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8145      */
8146     allowBlank : true,
8147     /**
8148      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8149      */
8150     blankText : "This field is required",
8151     
8152      /**
8153      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8154      */
8155     minLength : 0,
8156     /**
8157      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8158      */
8159     maxLength : Number.MAX_VALUE,
8160     /**
8161      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8162      */
8163     minLengthText : "The minimum length for this field is {0}",
8164     /**
8165      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8166      */
8167     maxLengthText : "The maximum length for this field is {0}",
8168   
8169     
8170     /**
8171      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8172      * If available, this function will be called only after the basic validators all return true, and will be passed the
8173      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8174      */
8175     validator : null,
8176     /**
8177      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8178      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8179      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8180      */
8181     regex : null,
8182     /**
8183      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8184      */
8185     regexText : "",
8186     
8187     autocomplete: false,
8188     
8189     
8190     fieldLabel : '',
8191     inputType : 'text',
8192     
8193     name : false,
8194     placeholder: false,
8195     before : false,
8196     after : false,
8197     size : false,
8198     hasFocus : false,
8199     preventMark: false,
8200     isFormField : true,
8201     value : '',
8202     labelWidth : 2,
8203     labelAlign : false,
8204     readOnly : false,
8205     align : false,
8206     formatedValue : false,
8207     forceFeedback : false,
8208     
8209     parentLabelAlign : function()
8210     {
8211         var parent = this;
8212         while (parent.parent()) {
8213             parent = parent.parent();
8214             if (typeof(parent.labelAlign) !='undefined') {
8215                 return parent.labelAlign;
8216             }
8217         }
8218         return 'left';
8219         
8220     },
8221     
8222     getAutoCreate : function(){
8223         
8224         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8225         
8226         var id = Roo.id();
8227         
8228         var cfg = {};
8229         
8230        
8231         
8232         if(this.inputType != 'hidden'){
8233             cfg.cls = 'form-group' //input-group
8234         }
8235         
8236         var input =  {
8237             tag: 'input',
8238             id : id,
8239             type : this.inputType,
8240             value : this.value,
8241             cls : 'form-control',
8242             placeholder : this.placeholder || '',
8243             autocomplete : this.autocomplete || 'new-password'
8244         };
8245         
8246         
8247         if(this.align){
8248             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8249         }
8250         
8251         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8252             input.maxLength = this.maxLength;
8253         }
8254         
8255         if (this.disabled) {
8256             input.disabled=true;
8257         }
8258         
8259         if (this.readOnly) {
8260             input.readonly=true;
8261         }
8262         
8263         if (this.name) {
8264             input.name = this.name;
8265         }
8266         if (this.size) {
8267             input.cls += ' input-' + this.size;
8268         }
8269         var settings=this;
8270         ['xs','sm','md','lg'].map(function(size){
8271             if (settings[size]) {
8272                 cfg.cls += ' col-' + size + '-' + settings[size];
8273             }
8274         });
8275         
8276         var inputblock = input;
8277         
8278         var feedback = {
8279             tag: 'span',
8280             cls: 'glyphicon form-control-feedback'
8281         };
8282             
8283         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8284             
8285             inputblock = {
8286                 cls : 'has-feedback',
8287                 cn :  [
8288                     input,
8289                     feedback
8290                 ] 
8291             };  
8292         }
8293         
8294         if (this.before || this.after) {
8295             
8296             inputblock = {
8297                 cls : 'input-group',
8298                 cn :  [] 
8299             };
8300             
8301             if (this.before && typeof(this.before) == 'string') {
8302                 
8303                 inputblock.cn.push({
8304                     tag :'span',
8305                     cls : 'roo-input-before input-group-addon',
8306                     html : this.before
8307                 });
8308             }
8309             if (this.before && typeof(this.before) == 'object') {
8310                 this.before = Roo.factory(this.before);
8311                 
8312                 inputblock.cn.push({
8313                     tag :'span',
8314                     cls : 'roo-input-before input-group-' +
8315                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8316                 });
8317             }
8318             
8319             inputblock.cn.push(input);
8320             
8321             if (this.after && typeof(this.after) == 'string') {
8322                 inputblock.cn.push({
8323                     tag :'span',
8324                     cls : 'roo-input-after input-group-addon',
8325                     html : this.after
8326                 });
8327             }
8328             if (this.after && typeof(this.after) == 'object') {
8329                 this.after = Roo.factory(this.after);
8330                 
8331                 inputblock.cn.push({
8332                     tag :'span',
8333                     cls : 'roo-input-after input-group-' +
8334                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8335                 });
8336             }
8337             
8338             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8339                 inputblock.cls += ' has-feedback';
8340                 inputblock.cn.push(feedback);
8341             }
8342         };
8343         
8344         if (align ==='left' && this.fieldLabel.length) {
8345                 
8346                 cfg.cn = [
8347                     
8348                     {
8349                         tag: 'label',
8350                         'for' :  id,
8351                         cls : 'control-label col-sm-' + this.labelWidth,
8352                         html : this.fieldLabel
8353                         
8354                     },
8355                     {
8356                         cls : "col-sm-" + (12 - this.labelWidth), 
8357                         cn: [
8358                             inputblock
8359                         ]
8360                     }
8361                     
8362                 ];
8363         } else if ( this.fieldLabel.length) {
8364                 
8365                  cfg.cn = [
8366                    
8367                     {
8368                         tag: 'label',
8369                         //cls : 'input-group-addon',
8370                         html : this.fieldLabel
8371                         
8372                     },
8373                     
8374                     inputblock
8375                     
8376                 ];
8377
8378         } else {
8379             
8380                 cfg.cn = [
8381                     
8382                         inputblock
8383                     
8384                 ];
8385                 
8386                 
8387         };
8388         
8389         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8390            cfg.cls += ' navbar-form';
8391         }
8392         if (this.parentType === 'NavGroup') {
8393            cfg.cls += ' navbar-form';
8394            cfg.tag = 'li';
8395         }
8396         return cfg;
8397         
8398     },
8399     /**
8400      * return the real input element.
8401      */
8402     inputEl: function ()
8403     {
8404         return this.el.select('input.form-control',true).first();
8405     },
8406     
8407     tooltipEl : function()
8408     {
8409         return this.inputEl();
8410     },
8411     
8412     setDisabled : function(v)
8413     {
8414         var i  = this.inputEl().dom;
8415         if (!v) {
8416             i.removeAttribute('disabled');
8417             return;
8418             
8419         }
8420         i.setAttribute('disabled','true');
8421     },
8422     initEvents : function()
8423     {
8424           
8425         this.inputEl().on("keydown" , this.fireKey,  this);
8426         this.inputEl().on("focus", this.onFocus,  this);
8427         this.inputEl().on("blur", this.onBlur,  this);
8428         
8429         this.inputEl().relayEvent('keyup', this);
8430  
8431         // reference to original value for reset
8432         this.originalValue = this.getValue();
8433         //Roo.form.TextField.superclass.initEvents.call(this);
8434         if(this.validationEvent == 'keyup'){
8435             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8436             this.inputEl().on('keyup', this.filterValidation, this);
8437         }
8438         else if(this.validationEvent !== false){
8439             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8440         }
8441         
8442         if(this.selectOnFocus){
8443             this.on("focus", this.preFocus, this);
8444             
8445         }
8446         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8447             this.inputEl().on("keypress", this.filterKeys, this);
8448         }
8449        /* if(this.grow){
8450             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8451             this.el.on("click", this.autoSize,  this);
8452         }
8453         */
8454         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8455             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8456         }
8457         
8458         if (typeof(this.before) == 'object') {
8459             this.before.render(this.el.select('.roo-input-before',true).first());
8460         }
8461         if (typeof(this.after) == 'object') {
8462             this.after.render(this.el.select('.roo-input-after',true).first());
8463         }
8464         
8465         
8466     },
8467     filterValidation : function(e){
8468         if(!e.isNavKeyPress()){
8469             this.validationTask.delay(this.validationDelay);
8470         }
8471     },
8472      /**
8473      * Validates the field value
8474      * @return {Boolean} True if the value is valid, else false
8475      */
8476     validate : function(){
8477         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8478         if(this.disabled || this.validateValue(this.getRawValue())){
8479             this.markValid();
8480             return true;
8481         }
8482         
8483         this.markInvalid();
8484         return false;
8485     },
8486     
8487     
8488     /**
8489      * Validates a value according to the field's validation rules and marks the field as invalid
8490      * if the validation fails
8491      * @param {Mixed} value The value to validate
8492      * @return {Boolean} True if the value is valid, else false
8493      */
8494     validateValue : function(value){
8495         if(value.length < 1)  { // if it's blank
8496             if(this.allowBlank){
8497                 return true;
8498             }
8499             return false;
8500         }
8501         
8502         if(value.length < this.minLength){
8503             return false;
8504         }
8505         if(value.length > this.maxLength){
8506             return false;
8507         }
8508         if(this.vtype){
8509             var vt = Roo.form.VTypes;
8510             if(!vt[this.vtype](value, this)){
8511                 return false;
8512             }
8513         }
8514         if(typeof this.validator == "function"){
8515             var msg = this.validator(value);
8516             if(msg !== true){
8517                 return false;
8518             }
8519         }
8520         
8521         if(this.regex && !this.regex.test(value)){
8522             return false;
8523         }
8524         
8525         return true;
8526     },
8527
8528     
8529     
8530      // private
8531     fireKey : function(e){
8532         //Roo.log('field ' + e.getKey());
8533         if(e.isNavKeyPress()){
8534             this.fireEvent("specialkey", this, e);
8535         }
8536     },
8537     focus : function (selectText){
8538         if(this.rendered){
8539             this.inputEl().focus();
8540             if(selectText === true){
8541                 this.inputEl().dom.select();
8542             }
8543         }
8544         return this;
8545     } ,
8546     
8547     onFocus : function(){
8548         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8549            // this.el.addClass(this.focusClass);
8550         }
8551         if(!this.hasFocus){
8552             this.hasFocus = true;
8553             this.startValue = this.getValue();
8554             this.fireEvent("focus", this);
8555         }
8556     },
8557     
8558     beforeBlur : Roo.emptyFn,
8559
8560     
8561     // private
8562     onBlur : function(){
8563         this.beforeBlur();
8564         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8565             //this.el.removeClass(this.focusClass);
8566         }
8567         this.hasFocus = false;
8568         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
8569             this.validate();
8570         }
8571         var v = this.getValue();
8572         if(String(v) !== String(this.startValue)){
8573             this.fireEvent('change', this, v, this.startValue);
8574         }
8575         this.fireEvent("blur", this);
8576     },
8577     
8578     /**
8579      * Resets the current field value to the originally loaded value and clears any validation messages
8580      */
8581     reset : function(){
8582         this.setValue(this.originalValue);
8583         this.validate();
8584     },
8585      /**
8586      * Returns the name of the field
8587      * @return {Mixed} name The name field
8588      */
8589     getName: function(){
8590         return this.name;
8591     },
8592      /**
8593      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
8594      * @return {Mixed} value The field value
8595      */
8596     getValue : function(){
8597         
8598         var v = this.inputEl().getValue();
8599         
8600         return v;
8601     },
8602     /**
8603      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
8604      * @return {Mixed} value The field value
8605      */
8606     getRawValue : function(){
8607         var v = this.inputEl().getValue();
8608         
8609         return v;
8610     },
8611     
8612     /**
8613      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
8614      * @param {Mixed} value The value to set
8615      */
8616     setRawValue : function(v){
8617         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8618     },
8619     
8620     selectText : function(start, end){
8621         var v = this.getRawValue();
8622         if(v.length > 0){
8623             start = start === undefined ? 0 : start;
8624             end = end === undefined ? v.length : end;
8625             var d = this.inputEl().dom;
8626             if(d.setSelectionRange){
8627                 d.setSelectionRange(start, end);
8628             }else if(d.createTextRange){
8629                 var range = d.createTextRange();
8630                 range.moveStart("character", start);
8631                 range.moveEnd("character", v.length-end);
8632                 range.select();
8633             }
8634         }
8635     },
8636     
8637     /**
8638      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
8639      * @param {Mixed} value The value to set
8640      */
8641     setValue : function(v){
8642         this.value = v;
8643         if(this.rendered){
8644             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
8645             this.validate();
8646         }
8647     },
8648     
8649     /*
8650     processValue : function(value){
8651         if(this.stripCharsRe){
8652             var newValue = value.replace(this.stripCharsRe, '');
8653             if(newValue !== value){
8654                 this.setRawValue(newValue);
8655                 return newValue;
8656             }
8657         }
8658         return value;
8659     },
8660   */
8661     preFocus : function(){
8662         
8663         if(this.selectOnFocus){
8664             this.inputEl().dom.select();
8665         }
8666     },
8667     filterKeys : function(e){
8668         var k = e.getKey();
8669         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
8670             return;
8671         }
8672         var c = e.getCharCode(), cc = String.fromCharCode(c);
8673         if(Roo.isIE && (e.isSpecialKey() || !cc)){
8674             return;
8675         }
8676         if(!this.maskRe.test(cc)){
8677             e.stopEvent();
8678         }
8679     },
8680      /**
8681      * Clear any invalid styles/messages for this field
8682      */
8683     clearInvalid : function(){
8684         
8685         if(!this.el || this.preventMark){ // not rendered
8686             return;
8687         }
8688         
8689         var label = this.el.select('label', true).first();
8690         var icon = this.el.select('i.fa-star', true).first();
8691         
8692         if(label && icon){
8693             icon.remove();
8694         }
8695         
8696         this.el.removeClass(this.invalidClass);
8697         
8698         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8699             
8700             var feedback = this.el.select('.form-control-feedback', true).first();
8701             
8702             if(feedback){
8703                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
8704             }
8705             
8706         }
8707         
8708         this.fireEvent('valid', this);
8709     },
8710     
8711      /**
8712      * Mark this field as valid
8713      */
8714     markValid : function()
8715     {
8716         if(!this.el  || this.preventMark){ // not rendered
8717             return;
8718         }
8719         
8720         this.el.removeClass([this.invalidClass, this.validClass]);
8721         
8722         var feedback = this.el.select('.form-control-feedback', true).first();
8723             
8724         if(feedback){
8725             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8726         }
8727
8728         if(this.disabled || this.allowBlank){
8729             return;
8730         }
8731         
8732         var formGroup = this.el.findParent('.form-group', false, true);
8733         
8734         if(formGroup){
8735             
8736             var label = formGroup.select('label', true).first();
8737             var icon = formGroup.select('i.fa-star', true).first();
8738             
8739             if(label && icon){
8740                 icon.remove();
8741             }
8742         }
8743         
8744         this.el.addClass(this.validClass);
8745         
8746         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
8747             
8748             var feedback = this.el.select('.form-control-feedback', true).first();
8749             
8750             if(feedback){
8751                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8752                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
8753             }
8754             
8755         }
8756         
8757         this.fireEvent('valid', this);
8758     },
8759     
8760      /**
8761      * Mark this field as invalid
8762      * @param {String} msg The validation message
8763      */
8764     markInvalid : function(msg)
8765     {
8766         if(!this.el  || this.preventMark){ // not rendered
8767             return;
8768         }
8769         
8770         this.el.removeClass([this.invalidClass, this.validClass]);
8771         
8772         var feedback = this.el.select('.form-control-feedback', true).first();
8773             
8774         if(feedback){
8775             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8776         }
8777
8778         if(this.disabled || this.allowBlank){
8779             return;
8780         }
8781         
8782         var formGroup = this.el.findParent('.form-group', false, true);
8783         
8784         if(formGroup){
8785             var label = formGroup.select('label', true).first();
8786             var icon = formGroup.select('i.fa-star', true).first();
8787
8788             if(!this.getValue().length && label && !icon){
8789                 this.el.findParent('.form-group', false, true).createChild({
8790                     tag : 'i',
8791                     cls : 'text-danger fa fa-lg fa-star',
8792                     tooltip : 'This field is required',
8793                     style : 'margin-right:5px;'
8794                 }, label, true);
8795             }
8796         }
8797         
8798         
8799         this.el.addClass(this.invalidClass);
8800         
8801         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8802             
8803             var feedback = this.el.select('.form-control-feedback', true).first();
8804             
8805             if(feedback){
8806                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
8807                 
8808                 if(this.getValue().length || this.forceFeedback){
8809                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
8810                 }
8811                 
8812             }
8813             
8814         }
8815         
8816         this.fireEvent('invalid', this, msg);
8817     },
8818     // private
8819     SafariOnKeyDown : function(event)
8820     {
8821         // this is a workaround for a password hang bug on chrome/ webkit.
8822         
8823         var isSelectAll = false;
8824         
8825         if(this.inputEl().dom.selectionEnd > 0){
8826             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
8827         }
8828         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
8829             event.preventDefault();
8830             this.setValue('');
8831             return;
8832         }
8833         
8834         if(isSelectAll  && event.getCharCode() > 31){ // not backspace and delete key
8835             
8836             event.preventDefault();
8837             // this is very hacky as keydown always get's upper case.
8838             //
8839             var cc = String.fromCharCode(event.getCharCode());
8840             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
8841             
8842         }
8843     },
8844     adjustWidth : function(tag, w){
8845         tag = tag.toLowerCase();
8846         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
8847             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
8848                 if(tag == 'input'){
8849                     return w + 2;
8850                 }
8851                 if(tag == 'textarea'){
8852                     return w-2;
8853                 }
8854             }else if(Roo.isOpera){
8855                 if(tag == 'input'){
8856                     return w + 2;
8857                 }
8858                 if(tag == 'textarea'){
8859                     return w-2;
8860                 }
8861             }
8862         }
8863         return w;
8864     }
8865     
8866 });
8867
8868  
8869 /*
8870  * - LGPL
8871  *
8872  * Input
8873  * 
8874  */
8875
8876 /**
8877  * @class Roo.bootstrap.TextArea
8878  * @extends Roo.bootstrap.Input
8879  * Bootstrap TextArea class
8880  * @cfg {Number} cols Specifies the visible width of a text area
8881  * @cfg {Number} rows Specifies the visible number of lines in a text area
8882  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
8883  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
8884  * @cfg {string} html text
8885  * 
8886  * @constructor
8887  * Create a new TextArea
8888  * @param {Object} config The config object
8889  */
8890
8891 Roo.bootstrap.TextArea = function(config){
8892     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
8893    
8894 };
8895
8896 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
8897      
8898     cols : false,
8899     rows : 5,
8900     readOnly : false,
8901     warp : 'soft',
8902     resize : false,
8903     value: false,
8904     html: false,
8905     
8906     getAutoCreate : function(){
8907         
8908         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8909         
8910         var id = Roo.id();
8911         
8912         var cfg = {};
8913         
8914         var input =  {
8915             tag: 'textarea',
8916             id : id,
8917             warp : this.warp,
8918             rows : this.rows,
8919             value : this.value || '',
8920             html: this.html || '',
8921             cls : 'form-control',
8922             placeholder : this.placeholder || '' 
8923             
8924         };
8925         
8926         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8927             input.maxLength = this.maxLength;
8928         }
8929         
8930         if(this.resize){
8931             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
8932         }
8933         
8934         if(this.cols){
8935             input.cols = this.cols;
8936         }
8937         
8938         if (this.readOnly) {
8939             input.readonly = true;
8940         }
8941         
8942         if (this.name) {
8943             input.name = this.name;
8944         }
8945         
8946         if (this.size) {
8947             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
8948         }
8949         
8950         var settings=this;
8951         ['xs','sm','md','lg'].map(function(size){
8952             if (settings[size]) {
8953                 cfg.cls += ' col-' + size + '-' + settings[size];
8954             }
8955         });
8956         
8957         var inputblock = input;
8958         
8959         if(this.hasFeedback && !this.allowBlank){
8960             
8961             var feedback = {
8962                 tag: 'span',
8963                 cls: 'glyphicon form-control-feedback'
8964             };
8965
8966             inputblock = {
8967                 cls : 'has-feedback',
8968                 cn :  [
8969                     input,
8970                     feedback
8971                 ] 
8972             };  
8973         }
8974         
8975         
8976         if (this.before || this.after) {
8977             
8978             inputblock = {
8979                 cls : 'input-group',
8980                 cn :  [] 
8981             };
8982             if (this.before) {
8983                 inputblock.cn.push({
8984                     tag :'span',
8985                     cls : 'input-group-addon',
8986                     html : this.before
8987                 });
8988             }
8989             
8990             inputblock.cn.push(input);
8991             
8992             if(this.hasFeedback && !this.allowBlank){
8993                 inputblock.cls += ' has-feedback';
8994                 inputblock.cn.push(feedback);
8995             }
8996             
8997             if (this.after) {
8998                 inputblock.cn.push({
8999                     tag :'span',
9000                     cls : 'input-group-addon',
9001                     html : this.after
9002                 });
9003             }
9004             
9005         }
9006         
9007         if (align ==='left' && this.fieldLabel.length) {
9008 //                Roo.log("left and has label");
9009                 cfg.cn = [
9010                     
9011                     {
9012                         tag: 'label',
9013                         'for' :  id,
9014                         cls : 'control-label col-sm-' + this.labelWidth,
9015                         html : this.fieldLabel
9016                         
9017                     },
9018                     {
9019                         cls : "col-sm-" + (12 - this.labelWidth), 
9020                         cn: [
9021                             inputblock
9022                         ]
9023                     }
9024                     
9025                 ];
9026         } else if ( this.fieldLabel.length) {
9027 //                Roo.log(" label");
9028                  cfg.cn = [
9029                    
9030                     {
9031                         tag: 'label',
9032                         //cls : 'input-group-addon',
9033                         html : this.fieldLabel
9034                         
9035                     },
9036                     
9037                     inputblock
9038                     
9039                 ];
9040
9041         } else {
9042             
9043 //                   Roo.log(" no label && no align");
9044                 cfg.cn = [
9045                     
9046                         inputblock
9047                     
9048                 ];
9049                 
9050                 
9051         }
9052         
9053         if (this.disabled) {
9054             input.disabled=true;
9055         }
9056         
9057         return cfg;
9058         
9059     },
9060     /**
9061      * return the real textarea element.
9062      */
9063     inputEl: function ()
9064     {
9065         return this.el.select('textarea.form-control',true).first();
9066     },
9067     
9068     /**
9069      * Clear any invalid styles/messages for this field
9070      */
9071     clearInvalid : function()
9072     {
9073         
9074         if(!this.el || this.preventMark){ // not rendered
9075             return;
9076         }
9077         
9078         var label = this.el.select('label', true).first();
9079         var icon = this.el.select('i.fa-star', true).first();
9080         
9081         if(label && icon){
9082             icon.remove();
9083         }
9084         
9085         this.el.removeClass(this.invalidClass);
9086         
9087         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9088             
9089             var feedback = this.el.select('.form-control-feedback', true).first();
9090             
9091             if(feedback){
9092                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9093             }
9094             
9095         }
9096         
9097         this.fireEvent('valid', this);
9098     },
9099     
9100      /**
9101      * Mark this field as valid
9102      */
9103     markValid : function()
9104     {
9105         if(!this.el  || this.preventMark){ // not rendered
9106             return;
9107         }
9108         
9109         this.el.removeClass([this.invalidClass, this.validClass]);
9110         
9111         var feedback = this.el.select('.form-control-feedback', true).first();
9112             
9113         if(feedback){
9114             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9115         }
9116
9117         if(this.disabled || this.allowBlank){
9118             return;
9119         }
9120         
9121         var label = this.el.select('label', true).first();
9122         var icon = this.el.select('i.fa-star', true).first();
9123         
9124         if(label && icon){
9125             icon.remove();
9126         }
9127         
9128         this.el.addClass(this.validClass);
9129         
9130         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9131             
9132             var feedback = this.el.select('.form-control-feedback', true).first();
9133             
9134             if(feedback){
9135                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9136                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9137             }
9138             
9139         }
9140         
9141         this.fireEvent('valid', this);
9142     },
9143     
9144      /**
9145      * Mark this field as invalid
9146      * @param {String} msg The validation message
9147      */
9148     markInvalid : function(msg)
9149     {
9150         if(!this.el  || this.preventMark){ // not rendered
9151             return;
9152         }
9153         
9154         this.el.removeClass([this.invalidClass, this.validClass]);
9155         
9156         var feedback = this.el.select('.form-control-feedback', true).first();
9157             
9158         if(feedback){
9159             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9160         }
9161
9162         if(this.disabled || this.allowBlank){
9163             return;
9164         }
9165         
9166         var label = this.el.select('label', true).first();
9167         var icon = this.el.select('i.fa-star', true).first();
9168         
9169         if(!this.getValue().length && label && !icon){
9170             this.el.createChild({
9171                 tag : 'i',
9172                 cls : 'text-danger fa fa-lg fa-star',
9173                 tooltip : 'This field is required',
9174                 style : 'margin-right:5px;'
9175             }, label, true);
9176         }
9177
9178         this.el.addClass(this.invalidClass);
9179         
9180         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9181             
9182             var feedback = this.el.select('.form-control-feedback', true).first();
9183             
9184             if(feedback){
9185                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9186                 
9187                 if(this.getValue().length || this.forceFeedback){
9188                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9189                 }
9190                 
9191             }
9192             
9193         }
9194         
9195         this.fireEvent('invalid', this, msg);
9196     }
9197 });
9198
9199  
9200 /*
9201  * - LGPL
9202  *
9203  * trigger field - base class for combo..
9204  * 
9205  */
9206  
9207 /**
9208  * @class Roo.bootstrap.TriggerField
9209  * @extends Roo.bootstrap.Input
9210  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9211  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9212  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9213  * for which you can provide a custom implementation.  For example:
9214  * <pre><code>
9215 var trigger = new Roo.bootstrap.TriggerField();
9216 trigger.onTriggerClick = myTriggerFn;
9217 trigger.applyTo('my-field');
9218 </code></pre>
9219  *
9220  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9221  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9222  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9223  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9224  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9225
9226  * @constructor
9227  * Create a new TriggerField.
9228  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9229  * to the base TextField)
9230  */
9231 Roo.bootstrap.TriggerField = function(config){
9232     this.mimicing = false;
9233     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9234 };
9235
9236 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9237     /**
9238      * @cfg {String} triggerClass A CSS class to apply to the trigger
9239      */
9240      /**
9241      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9242      */
9243     hideTrigger:false,
9244
9245     /**
9246      * @cfg {Boolean} removable (true|false) special filter default false
9247      */
9248     removable : false,
9249     
9250     /** @cfg {Boolean} grow @hide */
9251     /** @cfg {Number} growMin @hide */
9252     /** @cfg {Number} growMax @hide */
9253
9254     /**
9255      * @hide 
9256      * @method
9257      */
9258     autoSize: Roo.emptyFn,
9259     // private
9260     monitorTab : true,
9261     // private
9262     deferHeight : true,
9263
9264     
9265     actionMode : 'wrap',
9266     
9267     caret : false,
9268     
9269     
9270     getAutoCreate : function(){
9271        
9272         var align = this.labelAlign || this.parentLabelAlign();
9273         
9274         var id = Roo.id();
9275         
9276         var cfg = {
9277             cls: 'form-group' //input-group
9278         };
9279         
9280         
9281         var input =  {
9282             tag: 'input',
9283             id : id,
9284             type : this.inputType,
9285             cls : 'form-control',
9286             autocomplete: 'new-password',
9287             placeholder : this.placeholder || '' 
9288             
9289         };
9290         if (this.name) {
9291             input.name = this.name;
9292         }
9293         if (this.size) {
9294             input.cls += ' input-' + this.size;
9295         }
9296         
9297         if (this.disabled) {
9298             input.disabled=true;
9299         }
9300         
9301         var inputblock = input;
9302         
9303         if(this.hasFeedback && !this.allowBlank){
9304             
9305             var feedback = {
9306                 tag: 'span',
9307                 cls: 'glyphicon form-control-feedback'
9308             };
9309             
9310             if(this.removable && !this.editable && !this.tickable){
9311                 inputblock = {
9312                     cls : 'has-feedback',
9313                     cn :  [
9314                         inputblock,
9315                         {
9316                             tag: 'button',
9317                             html : 'x',
9318                             cls : 'roo-combo-removable-btn close'
9319                         },
9320                         feedback
9321                     ] 
9322                 };
9323             } else {
9324                 inputblock = {
9325                     cls : 'has-feedback',
9326                     cn :  [
9327                         inputblock,
9328                         feedback
9329                     ] 
9330                 };
9331             }
9332
9333         } else {
9334             if(this.removable && !this.editable && !this.tickable){
9335                 inputblock = {
9336                     cls : 'roo-removable',
9337                     cn :  [
9338                         inputblock,
9339                         {
9340                             tag: 'button',
9341                             html : 'x',
9342                             cls : 'roo-combo-removable-btn close'
9343                         }
9344                     ] 
9345                 };
9346             }
9347         }
9348         
9349         if (this.before || this.after) {
9350             
9351             inputblock = {
9352                 cls : 'input-group',
9353                 cn :  [] 
9354             };
9355             if (this.before) {
9356                 inputblock.cn.push({
9357                     tag :'span',
9358                     cls : 'input-group-addon',
9359                     html : this.before
9360                 });
9361             }
9362             
9363             inputblock.cn.push(input);
9364             
9365             if(this.hasFeedback && !this.allowBlank){
9366                 inputblock.cls += ' has-feedback';
9367                 inputblock.cn.push(feedback);
9368             }
9369             
9370             if (this.after) {
9371                 inputblock.cn.push({
9372                     tag :'span',
9373                     cls : 'input-group-addon',
9374                     html : this.after
9375                 });
9376             }
9377             
9378         };
9379         
9380         var box = {
9381             tag: 'div',
9382             cn: [
9383                 {
9384                     tag: 'input',
9385                     type : 'hidden',
9386                     cls: 'form-hidden-field'
9387                 },
9388                 inputblock
9389             ]
9390             
9391         };
9392         
9393         if(this.multiple){
9394             box = {
9395                 tag: 'div',
9396                 cn: [
9397                     {
9398                         tag: 'input',
9399                         type : 'hidden',
9400                         cls: 'form-hidden-field'
9401                     },
9402                     {
9403                         tag: 'ul',
9404                         cls: 'roo-select2-choices',
9405                         cn:[
9406                             {
9407                                 tag: 'li',
9408                                 cls: 'roo-select2-search-field',
9409                                 cn: [
9410
9411                                     inputblock
9412                                 ]
9413                             }
9414                         ]
9415                     }
9416                 ]
9417             }
9418         };
9419         
9420         var combobox = {
9421             cls: 'roo-select2-container input-group',
9422             cn: [
9423                 box
9424 //                {
9425 //                    tag: 'ul',
9426 //                    cls: 'typeahead typeahead-long dropdown-menu',
9427 //                    style: 'display:none'
9428 //                }
9429             ]
9430         };
9431         
9432         if(!this.multiple && this.showToggleBtn){
9433             
9434             var caret = {
9435                         tag: 'span',
9436                         cls: 'caret'
9437              };
9438             if (this.caret != false) {
9439                 caret = {
9440                      tag: 'i',
9441                      cls: 'fa fa-' + this.caret
9442                 };
9443                 
9444             }
9445             
9446             combobox.cn.push({
9447                 tag :'span',
9448                 cls : 'input-group-addon btn dropdown-toggle',
9449                 cn : [
9450                     caret,
9451                     {
9452                         tag: 'span',
9453                         cls: 'combobox-clear',
9454                         cn  : [
9455                             {
9456                                 tag : 'i',
9457                                 cls: 'icon-remove'
9458                             }
9459                         ]
9460                     }
9461                 ]
9462
9463             })
9464         }
9465         
9466         if(this.multiple){
9467             combobox.cls += ' roo-select2-container-multi';
9468         }
9469         
9470         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
9471             
9472 //                Roo.log("left and has label");
9473                 cfg.cn = [
9474                     
9475                     {
9476                         tag: 'label',
9477                         'for' :  id,
9478                         cls : 'control-label col-sm-' + this.labelWidth,
9479                         html : this.fieldLabel
9480                         
9481                     },
9482                     {
9483                         cls : "col-sm-" + (12 - this.labelWidth), 
9484                         cn: [
9485                             combobox
9486                         ]
9487                     }
9488                     
9489                 ];
9490         } else if ( this.fieldLabel.length) {
9491 //                Roo.log(" label");
9492                  cfg.cn = [
9493                    
9494                     {
9495                         tag: 'label',
9496                         //cls : 'input-group-addon',
9497                         html : this.fieldLabel
9498                         
9499                     },
9500                     
9501                     combobox
9502                     
9503                 ];
9504
9505         } else {
9506             
9507 //                Roo.log(" no label && no align");
9508                 cfg = combobox
9509                      
9510                 
9511         }
9512          
9513         var settings=this;
9514         ['xs','sm','md','lg'].map(function(size){
9515             if (settings[size]) {
9516                 cfg.cls += ' col-' + size + '-' + settings[size];
9517             }
9518         });
9519         
9520         return cfg;
9521         
9522     },
9523     
9524     
9525     
9526     // private
9527     onResize : function(w, h){
9528 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
9529 //        if(typeof w == 'number'){
9530 //            var x = w - this.trigger.getWidth();
9531 //            this.inputEl().setWidth(this.adjustWidth('input', x));
9532 //            this.trigger.setStyle('left', x+'px');
9533 //        }
9534     },
9535
9536     // private
9537     adjustSize : Roo.BoxComponent.prototype.adjustSize,
9538
9539     // private
9540     getResizeEl : function(){
9541         return this.inputEl();
9542     },
9543
9544     // private
9545     getPositionEl : function(){
9546         return this.inputEl();
9547     },
9548
9549     // private
9550     alignErrorIcon : function(){
9551         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
9552     },
9553
9554     // private
9555     initEvents : function(){
9556         
9557         this.createList();
9558         
9559         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
9560         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
9561         if(!this.multiple && this.showToggleBtn){
9562             this.trigger = this.el.select('span.dropdown-toggle',true).first();
9563             if(this.hideTrigger){
9564                 this.trigger.setDisplayed(false);
9565             }
9566             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
9567         }
9568         
9569         if(this.multiple){
9570             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
9571         }
9572         
9573         if(this.removable && !this.editable && !this.tickable){
9574             var close = this.closeTriggerEl();
9575             
9576             if(close){
9577                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
9578                 close.on('click', this.removeBtnClick, this, close);
9579             }
9580         }
9581         
9582         //this.trigger.addClassOnOver('x-form-trigger-over');
9583         //this.trigger.addClassOnClick('x-form-trigger-click');
9584         
9585         //if(!this.width){
9586         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
9587         //}
9588     },
9589     
9590     closeTriggerEl : function()
9591     {
9592         var close = this.el.select('.roo-combo-removable-btn', true).first();
9593         return close ? close : false;
9594     },
9595     
9596     removeBtnClick : function(e, h, el)
9597     {
9598         e.preventDefault();
9599         
9600         if(this.fireEvent("remove", this) !== false){
9601             this.reset();
9602             this.fireEvent("afterremove", this)
9603         }
9604     },
9605     
9606     createList : function()
9607     {
9608         this.list = Roo.get(document.body).createChild({
9609             tag: 'ul',
9610             cls: 'typeahead typeahead-long dropdown-menu',
9611             style: 'display:none'
9612         });
9613         
9614         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
9615         
9616     },
9617
9618     // private
9619     initTrigger : function(){
9620        
9621     },
9622
9623     // private
9624     onDestroy : function(){
9625         if(this.trigger){
9626             this.trigger.removeAllListeners();
9627           //  this.trigger.remove();
9628         }
9629         //if(this.wrap){
9630         //    this.wrap.remove();
9631         //}
9632         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
9633     },
9634
9635     // private
9636     onFocus : function(){
9637         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
9638         /*
9639         if(!this.mimicing){
9640             this.wrap.addClass('x-trigger-wrap-focus');
9641             this.mimicing = true;
9642             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
9643             if(this.monitorTab){
9644                 this.el.on("keydown", this.checkTab, this);
9645             }
9646         }
9647         */
9648     },
9649
9650     // private
9651     checkTab : function(e){
9652         if(e.getKey() == e.TAB){
9653             this.triggerBlur();
9654         }
9655     },
9656
9657     // private
9658     onBlur : function(){
9659         // do nothing
9660     },
9661
9662     // private
9663     mimicBlur : function(e, t){
9664         /*
9665         if(!this.wrap.contains(t) && this.validateBlur()){
9666             this.triggerBlur();
9667         }
9668         */
9669     },
9670
9671     // private
9672     triggerBlur : function(){
9673         this.mimicing = false;
9674         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
9675         if(this.monitorTab){
9676             this.el.un("keydown", this.checkTab, this);
9677         }
9678         //this.wrap.removeClass('x-trigger-wrap-focus');
9679         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
9680     },
9681
9682     // private
9683     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
9684     validateBlur : function(e, t){
9685         return true;
9686     },
9687
9688     // private
9689     onDisable : function(){
9690         this.inputEl().dom.disabled = true;
9691         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
9692         //if(this.wrap){
9693         //    this.wrap.addClass('x-item-disabled');
9694         //}
9695     },
9696
9697     // private
9698     onEnable : function(){
9699         this.inputEl().dom.disabled = false;
9700         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
9701         //if(this.wrap){
9702         //    this.el.removeClass('x-item-disabled');
9703         //}
9704     },
9705
9706     // private
9707     onShow : function(){
9708         var ae = this.getActionEl();
9709         
9710         if(ae){
9711             ae.dom.style.display = '';
9712             ae.dom.style.visibility = 'visible';
9713         }
9714     },
9715
9716     // private
9717     
9718     onHide : function(){
9719         var ae = this.getActionEl();
9720         ae.dom.style.display = 'none';
9721     },
9722
9723     /**
9724      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
9725      * by an implementing function.
9726      * @method
9727      * @param {EventObject} e
9728      */
9729     onTriggerClick : Roo.emptyFn
9730 });
9731  /*
9732  * Based on:
9733  * Ext JS Library 1.1.1
9734  * Copyright(c) 2006-2007, Ext JS, LLC.
9735  *
9736  * Originally Released Under LGPL - original licence link has changed is not relivant.
9737  *
9738  * Fork - LGPL
9739  * <script type="text/javascript">
9740  */
9741
9742
9743 /**
9744  * @class Roo.data.SortTypes
9745  * @singleton
9746  * Defines the default sorting (casting?) comparison functions used when sorting data.
9747  */
9748 Roo.data.SortTypes = {
9749     /**
9750      * Default sort that does nothing
9751      * @param {Mixed} s The value being converted
9752      * @return {Mixed} The comparison value
9753      */
9754     none : function(s){
9755         return s;
9756     },
9757     
9758     /**
9759      * The regular expression used to strip tags
9760      * @type {RegExp}
9761      * @property
9762      */
9763     stripTagsRE : /<\/?[^>]+>/gi,
9764     
9765     /**
9766      * Strips all HTML tags to sort on text only
9767      * @param {Mixed} s The value being converted
9768      * @return {String} The comparison value
9769      */
9770     asText : function(s){
9771         return String(s).replace(this.stripTagsRE, "");
9772     },
9773     
9774     /**
9775      * Strips all HTML tags to sort on text only - Case insensitive
9776      * @param {Mixed} s The value being converted
9777      * @return {String} The comparison value
9778      */
9779     asUCText : function(s){
9780         return String(s).toUpperCase().replace(this.stripTagsRE, "");
9781     },
9782     
9783     /**
9784      * Case insensitive string
9785      * @param {Mixed} s The value being converted
9786      * @return {String} The comparison value
9787      */
9788     asUCString : function(s) {
9789         return String(s).toUpperCase();
9790     },
9791     
9792     /**
9793      * Date sorting
9794      * @param {Mixed} s The value being converted
9795      * @return {Number} The comparison value
9796      */
9797     asDate : function(s) {
9798         if(!s){
9799             return 0;
9800         }
9801         if(s instanceof Date){
9802             return s.getTime();
9803         }
9804         return Date.parse(String(s));
9805     },
9806     
9807     /**
9808      * Float sorting
9809      * @param {Mixed} s The value being converted
9810      * @return {Float} The comparison value
9811      */
9812     asFloat : function(s) {
9813         var val = parseFloat(String(s).replace(/,/g, ""));
9814         if(isNaN(val)) {
9815             val = 0;
9816         }
9817         return val;
9818     },
9819     
9820     /**
9821      * Integer sorting
9822      * @param {Mixed} s The value being converted
9823      * @return {Number} The comparison value
9824      */
9825     asInt : function(s) {
9826         var val = parseInt(String(s).replace(/,/g, ""));
9827         if(isNaN(val)) {
9828             val = 0;
9829         }
9830         return val;
9831     }
9832 };/*
9833  * Based on:
9834  * Ext JS Library 1.1.1
9835  * Copyright(c) 2006-2007, Ext JS, LLC.
9836  *
9837  * Originally Released Under LGPL - original licence link has changed is not relivant.
9838  *
9839  * Fork - LGPL
9840  * <script type="text/javascript">
9841  */
9842
9843 /**
9844 * @class Roo.data.Record
9845  * Instances of this class encapsulate both record <em>definition</em> information, and record
9846  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
9847  * to access Records cached in an {@link Roo.data.Store} object.<br>
9848  * <p>
9849  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
9850  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
9851  * objects.<br>
9852  * <p>
9853  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
9854  * @constructor
9855  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
9856  * {@link #create}. The parameters are the same.
9857  * @param {Array} data An associative Array of data values keyed by the field name.
9858  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
9859  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
9860  * not specified an integer id is generated.
9861  */
9862 Roo.data.Record = function(data, id){
9863     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
9864     this.data = data;
9865 };
9866
9867 /**
9868  * Generate a constructor for a specific record layout.
9869  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
9870  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
9871  * Each field definition object may contain the following properties: <ul>
9872  * <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,
9873  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
9874  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
9875  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
9876  * is being used, then this is a string containing the javascript expression to reference the data relative to 
9877  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
9878  * to the data item relative to the record element. If the mapping expression is the same as the field name,
9879  * this may be omitted.</p></li>
9880  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
9881  * <ul><li>auto (Default, implies no conversion)</li>
9882  * <li>string</li>
9883  * <li>int</li>
9884  * <li>float</li>
9885  * <li>boolean</li>
9886  * <li>date</li></ul></p></li>
9887  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
9888  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
9889  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
9890  * by the Reader into an object that will be stored in the Record. It is passed the
9891  * following parameters:<ul>
9892  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
9893  * </ul></p></li>
9894  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
9895  * </ul>
9896  * <br>usage:<br><pre><code>
9897 var TopicRecord = Roo.data.Record.create(
9898     {name: 'title', mapping: 'topic_title'},
9899     {name: 'author', mapping: 'username'},
9900     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
9901     {name: 'lastPost', mapping: 'post_time', type: 'date'},
9902     {name: 'lastPoster', mapping: 'user2'},
9903     {name: 'excerpt', mapping: 'post_text'}
9904 );
9905
9906 var myNewRecord = new TopicRecord({
9907     title: 'Do my job please',
9908     author: 'noobie',
9909     totalPosts: 1,
9910     lastPost: new Date(),
9911     lastPoster: 'Animal',
9912     excerpt: 'No way dude!'
9913 });
9914 myStore.add(myNewRecord);
9915 </code></pre>
9916  * @method create
9917  * @static
9918  */
9919 Roo.data.Record.create = function(o){
9920     var f = function(){
9921         f.superclass.constructor.apply(this, arguments);
9922     };
9923     Roo.extend(f, Roo.data.Record);
9924     var p = f.prototype;
9925     p.fields = new Roo.util.MixedCollection(false, function(field){
9926         return field.name;
9927     });
9928     for(var i = 0, len = o.length; i < len; i++){
9929         p.fields.add(new Roo.data.Field(o[i]));
9930     }
9931     f.getField = function(name){
9932         return p.fields.get(name);  
9933     };
9934     return f;
9935 };
9936
9937 Roo.data.Record.AUTO_ID = 1000;
9938 Roo.data.Record.EDIT = 'edit';
9939 Roo.data.Record.REJECT = 'reject';
9940 Roo.data.Record.COMMIT = 'commit';
9941
9942 Roo.data.Record.prototype = {
9943     /**
9944      * Readonly flag - true if this record has been modified.
9945      * @type Boolean
9946      */
9947     dirty : false,
9948     editing : false,
9949     error: null,
9950     modified: null,
9951
9952     // private
9953     join : function(store){
9954         this.store = store;
9955     },
9956
9957     /**
9958      * Set the named field to the specified value.
9959      * @param {String} name The name of the field to set.
9960      * @param {Object} value The value to set the field to.
9961      */
9962     set : function(name, value){
9963         if(this.data[name] == value){
9964             return;
9965         }
9966         this.dirty = true;
9967         if(!this.modified){
9968             this.modified = {};
9969         }
9970         if(typeof this.modified[name] == 'undefined'){
9971             this.modified[name] = this.data[name];
9972         }
9973         this.data[name] = value;
9974         if(!this.editing && this.store){
9975             this.store.afterEdit(this);
9976         }       
9977     },
9978
9979     /**
9980      * Get the value of the named field.
9981      * @param {String} name The name of the field to get the value of.
9982      * @return {Object} The value of the field.
9983      */
9984     get : function(name){
9985         return this.data[name]; 
9986     },
9987
9988     // private
9989     beginEdit : function(){
9990         this.editing = true;
9991         this.modified = {}; 
9992     },
9993
9994     // private
9995     cancelEdit : function(){
9996         this.editing = false;
9997         delete this.modified;
9998     },
9999
10000     // private
10001     endEdit : function(){
10002         this.editing = false;
10003         if(this.dirty && this.store){
10004             this.store.afterEdit(this);
10005         }
10006     },
10007
10008     /**
10009      * Usually called by the {@link Roo.data.Store} which owns the Record.
10010      * Rejects all changes made to the Record since either creation, or the last commit operation.
10011      * Modified fields are reverted to their original values.
10012      * <p>
10013      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10014      * of reject operations.
10015      */
10016     reject : function(){
10017         var m = this.modified;
10018         for(var n in m){
10019             if(typeof m[n] != "function"){
10020                 this.data[n] = m[n];
10021             }
10022         }
10023         this.dirty = false;
10024         delete this.modified;
10025         this.editing = false;
10026         if(this.store){
10027             this.store.afterReject(this);
10028         }
10029     },
10030
10031     /**
10032      * Usually called by the {@link Roo.data.Store} which owns the Record.
10033      * Commits all changes made to the Record since either creation, or the last commit operation.
10034      * <p>
10035      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10036      * of commit operations.
10037      */
10038     commit : function(){
10039         this.dirty = false;
10040         delete this.modified;
10041         this.editing = false;
10042         if(this.store){
10043             this.store.afterCommit(this);
10044         }
10045     },
10046
10047     // private
10048     hasError : function(){
10049         return this.error != null;
10050     },
10051
10052     // private
10053     clearError : function(){
10054         this.error = null;
10055     },
10056
10057     /**
10058      * Creates a copy of this record.
10059      * @param {String} id (optional) A new record id if you don't want to use this record's id
10060      * @return {Record}
10061      */
10062     copy : function(newId) {
10063         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10064     }
10065 };/*
10066  * Based on:
10067  * Ext JS Library 1.1.1
10068  * Copyright(c) 2006-2007, Ext JS, LLC.
10069  *
10070  * Originally Released Under LGPL - original licence link has changed is not relivant.
10071  *
10072  * Fork - LGPL
10073  * <script type="text/javascript">
10074  */
10075
10076
10077
10078 /**
10079  * @class Roo.data.Store
10080  * @extends Roo.util.Observable
10081  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10082  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10083  * <p>
10084  * 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
10085  * has no knowledge of the format of the data returned by the Proxy.<br>
10086  * <p>
10087  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10088  * instances from the data object. These records are cached and made available through accessor functions.
10089  * @constructor
10090  * Creates a new Store.
10091  * @param {Object} config A config object containing the objects needed for the Store to access data,
10092  * and read the data into Records.
10093  */
10094 Roo.data.Store = function(config){
10095     this.data = new Roo.util.MixedCollection(false);
10096     this.data.getKey = function(o){
10097         return o.id;
10098     };
10099     this.baseParams = {};
10100     // private
10101     this.paramNames = {
10102         "start" : "start",
10103         "limit" : "limit",
10104         "sort" : "sort",
10105         "dir" : "dir",
10106         "multisort" : "_multisort"
10107     };
10108
10109     if(config && config.data){
10110         this.inlineData = config.data;
10111         delete config.data;
10112     }
10113
10114     Roo.apply(this, config);
10115     
10116     if(this.reader){ // reader passed
10117         this.reader = Roo.factory(this.reader, Roo.data);
10118         this.reader.xmodule = this.xmodule || false;
10119         if(!this.recordType){
10120             this.recordType = this.reader.recordType;
10121         }
10122         if(this.reader.onMetaChange){
10123             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10124         }
10125     }
10126
10127     if(this.recordType){
10128         this.fields = this.recordType.prototype.fields;
10129     }
10130     this.modified = [];
10131
10132     this.addEvents({
10133         /**
10134          * @event datachanged
10135          * Fires when the data cache has changed, and a widget which is using this Store
10136          * as a Record cache should refresh its view.
10137          * @param {Store} this
10138          */
10139         datachanged : true,
10140         /**
10141          * @event metachange
10142          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10143          * @param {Store} this
10144          * @param {Object} meta The JSON metadata
10145          */
10146         metachange : true,
10147         /**
10148          * @event add
10149          * Fires when Records have been added to the Store
10150          * @param {Store} this
10151          * @param {Roo.data.Record[]} records The array of Records added
10152          * @param {Number} index The index at which the record(s) were added
10153          */
10154         add : true,
10155         /**
10156          * @event remove
10157          * Fires when a Record has been removed from the Store
10158          * @param {Store} this
10159          * @param {Roo.data.Record} record The Record that was removed
10160          * @param {Number} index The index at which the record was removed
10161          */
10162         remove : true,
10163         /**
10164          * @event update
10165          * Fires when a Record has been updated
10166          * @param {Store} this
10167          * @param {Roo.data.Record} record The Record that was updated
10168          * @param {String} operation The update operation being performed.  Value may be one of:
10169          * <pre><code>
10170  Roo.data.Record.EDIT
10171  Roo.data.Record.REJECT
10172  Roo.data.Record.COMMIT
10173          * </code></pre>
10174          */
10175         update : true,
10176         /**
10177          * @event clear
10178          * Fires when the data cache has been cleared.
10179          * @param {Store} this
10180          */
10181         clear : true,
10182         /**
10183          * @event beforeload
10184          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10185          * the load action will be canceled.
10186          * @param {Store} this
10187          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10188          */
10189         beforeload : true,
10190         /**
10191          * @event beforeloadadd
10192          * Fires after a new set of Records has been loaded.
10193          * @param {Store} this
10194          * @param {Roo.data.Record[]} records The Records that were loaded
10195          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10196          */
10197         beforeloadadd : true,
10198         /**
10199          * @event load
10200          * Fires after a new set of Records has been loaded, before they are added to the store.
10201          * @param {Store} this
10202          * @param {Roo.data.Record[]} records The Records that were loaded
10203          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10204          * @params {Object} return from reader
10205          */
10206         load : true,
10207         /**
10208          * @event loadexception
10209          * Fires if an exception occurs in the Proxy during loading.
10210          * Called with the signature of the Proxy's "loadexception" event.
10211          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10212          * 
10213          * @param {Proxy} 
10214          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10215          * @param {Object} load options 
10216          * @param {Object} jsonData from your request (normally this contains the Exception)
10217          */
10218         loadexception : true
10219     });
10220     
10221     if(this.proxy){
10222         this.proxy = Roo.factory(this.proxy, Roo.data);
10223         this.proxy.xmodule = this.xmodule || false;
10224         this.relayEvents(this.proxy,  ["loadexception"]);
10225     }
10226     this.sortToggle = {};
10227     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10228
10229     Roo.data.Store.superclass.constructor.call(this);
10230
10231     if(this.inlineData){
10232         this.loadData(this.inlineData);
10233         delete this.inlineData;
10234     }
10235 };
10236
10237 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10238      /**
10239     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10240     * without a remote query - used by combo/forms at present.
10241     */
10242     
10243     /**
10244     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10245     */
10246     /**
10247     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10248     */
10249     /**
10250     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10251     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10252     */
10253     /**
10254     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10255     * on any HTTP request
10256     */
10257     /**
10258     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10259     */
10260     /**
10261     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10262     */
10263     multiSort: false,
10264     /**
10265     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10266     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10267     */
10268     remoteSort : false,
10269
10270     /**
10271     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10272      * loaded or when a record is removed. (defaults to false).
10273     */
10274     pruneModifiedRecords : false,
10275
10276     // private
10277     lastOptions : null,
10278
10279     /**
10280      * Add Records to the Store and fires the add event.
10281      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10282      */
10283     add : function(records){
10284         records = [].concat(records);
10285         for(var i = 0, len = records.length; i < len; i++){
10286             records[i].join(this);
10287         }
10288         var index = this.data.length;
10289         this.data.addAll(records);
10290         this.fireEvent("add", this, records, index);
10291     },
10292
10293     /**
10294      * Remove a Record from the Store and fires the remove event.
10295      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10296      */
10297     remove : function(record){
10298         var index = this.data.indexOf(record);
10299         this.data.removeAt(index);
10300         if(this.pruneModifiedRecords){
10301             this.modified.remove(record);
10302         }
10303         this.fireEvent("remove", this, record, index);
10304     },
10305
10306     /**
10307      * Remove all Records from the Store and fires the clear event.
10308      */
10309     removeAll : function(){
10310         this.data.clear();
10311         if(this.pruneModifiedRecords){
10312             this.modified = [];
10313         }
10314         this.fireEvent("clear", this);
10315     },
10316
10317     /**
10318      * Inserts Records to the Store at the given index and fires the add event.
10319      * @param {Number} index The start index at which to insert the passed Records.
10320      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10321      */
10322     insert : function(index, records){
10323         records = [].concat(records);
10324         for(var i = 0, len = records.length; i < len; i++){
10325             this.data.insert(index, records[i]);
10326             records[i].join(this);
10327         }
10328         this.fireEvent("add", this, records, index);
10329     },
10330
10331     /**
10332      * Get the index within the cache of the passed Record.
10333      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10334      * @return {Number} The index of the passed Record. Returns -1 if not found.
10335      */
10336     indexOf : function(record){
10337         return this.data.indexOf(record);
10338     },
10339
10340     /**
10341      * Get the index within the cache of the Record with the passed id.
10342      * @param {String} id The id of the Record to find.
10343      * @return {Number} The index of the Record. Returns -1 if not found.
10344      */
10345     indexOfId : function(id){
10346         return this.data.indexOfKey(id);
10347     },
10348
10349     /**
10350      * Get the Record with the specified id.
10351      * @param {String} id The id of the Record to find.
10352      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10353      */
10354     getById : function(id){
10355         return this.data.key(id);
10356     },
10357
10358     /**
10359      * Get the Record at the specified index.
10360      * @param {Number} index The index of the Record to find.
10361      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10362      */
10363     getAt : function(index){
10364         return this.data.itemAt(index);
10365     },
10366
10367     /**
10368      * Returns a range of Records between specified indices.
10369      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10370      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10371      * @return {Roo.data.Record[]} An array of Records
10372      */
10373     getRange : function(start, end){
10374         return this.data.getRange(start, end);
10375     },
10376
10377     // private
10378     storeOptions : function(o){
10379         o = Roo.apply({}, o);
10380         delete o.callback;
10381         delete o.scope;
10382         this.lastOptions = o;
10383     },
10384
10385     /**
10386      * Loads the Record cache from the configured Proxy using the configured Reader.
10387      * <p>
10388      * If using remote paging, then the first load call must specify the <em>start</em>
10389      * and <em>limit</em> properties in the options.params property to establish the initial
10390      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10391      * <p>
10392      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10393      * and this call will return before the new data has been loaded. Perform any post-processing
10394      * in a callback function, or in a "load" event handler.</strong>
10395      * <p>
10396      * @param {Object} options An object containing properties which control loading options:<ul>
10397      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10398      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10399      * passed the following arguments:<ul>
10400      * <li>r : Roo.data.Record[]</li>
10401      * <li>options: Options object from the load call</li>
10402      * <li>success: Boolean success indicator</li></ul></li>
10403      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10404      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10405      * </ul>
10406      */
10407     load : function(options){
10408         options = options || {};
10409         if(this.fireEvent("beforeload", this, options) !== false){
10410             this.storeOptions(options);
10411             var p = Roo.apply(options.params || {}, this.baseParams);
10412             // if meta was not loaded from remote source.. try requesting it.
10413             if (!this.reader.metaFromRemote) {
10414                 p._requestMeta = 1;
10415             }
10416             if(this.sortInfo && this.remoteSort){
10417                 var pn = this.paramNames;
10418                 p[pn["sort"]] = this.sortInfo.field;
10419                 p[pn["dir"]] = this.sortInfo.direction;
10420             }
10421             if (this.multiSort) {
10422                 var pn = this.paramNames;
10423                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10424             }
10425             
10426             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10427         }
10428     },
10429
10430     /**
10431      * Reloads the Record cache from the configured Proxy using the configured Reader and
10432      * the options from the last load operation performed.
10433      * @param {Object} options (optional) An object containing properties which may override the options
10434      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10435      * the most recently used options are reused).
10436      */
10437     reload : function(options){
10438         this.load(Roo.applyIf(options||{}, this.lastOptions));
10439     },
10440
10441     // private
10442     // Called as a callback by the Reader during a load operation.
10443     loadRecords : function(o, options, success){
10444         if(!o || success === false){
10445             if(success !== false){
10446                 this.fireEvent("load", this, [], options, o);
10447             }
10448             if(options.callback){
10449                 options.callback.call(options.scope || this, [], options, false);
10450             }
10451             return;
10452         }
10453         // if data returned failure - throw an exception.
10454         if (o.success === false) {
10455             // show a message if no listener is registered.
10456             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
10457                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
10458             }
10459             // loadmask wil be hooked into this..
10460             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
10461             return;
10462         }
10463         var r = o.records, t = o.totalRecords || r.length;
10464         
10465         this.fireEvent("beforeloadadd", this, r, options, o);
10466         
10467         if(!options || options.add !== true){
10468             if(this.pruneModifiedRecords){
10469                 this.modified = [];
10470             }
10471             for(var i = 0, len = r.length; i < len; i++){
10472                 r[i].join(this);
10473             }
10474             if(this.snapshot){
10475                 this.data = this.snapshot;
10476                 delete this.snapshot;
10477             }
10478             this.data.clear();
10479             this.data.addAll(r);
10480             this.totalLength = t;
10481             this.applySort();
10482             this.fireEvent("datachanged", this);
10483         }else{
10484             this.totalLength = Math.max(t, this.data.length+r.length);
10485             this.add(r);
10486         }
10487         this.fireEvent("load", this, r, options, o);
10488         if(options.callback){
10489             options.callback.call(options.scope || this, r, options, true);
10490         }
10491     },
10492
10493
10494     /**
10495      * Loads data from a passed data block. A Reader which understands the format of the data
10496      * must have been configured in the constructor.
10497      * @param {Object} data The data block from which to read the Records.  The format of the data expected
10498      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
10499      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
10500      */
10501     loadData : function(o, append){
10502         var r = this.reader.readRecords(o);
10503         this.loadRecords(r, {add: append}, true);
10504     },
10505
10506     /**
10507      * Gets the number of cached records.
10508      * <p>
10509      * <em>If using paging, this may not be the total size of the dataset. If the data object
10510      * used by the Reader contains the dataset size, then the getTotalCount() function returns
10511      * the data set size</em>
10512      */
10513     getCount : function(){
10514         return this.data.length || 0;
10515     },
10516
10517     /**
10518      * Gets the total number of records in the dataset as returned by the server.
10519      * <p>
10520      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
10521      * the dataset size</em>
10522      */
10523     getTotalCount : function(){
10524         return this.totalLength || 0;
10525     },
10526
10527     /**
10528      * Returns the sort state of the Store as an object with two properties:
10529      * <pre><code>
10530  field {String} The name of the field by which the Records are sorted
10531  direction {String} The sort order, "ASC" or "DESC"
10532      * </code></pre>
10533      */
10534     getSortState : function(){
10535         return this.sortInfo;
10536     },
10537
10538     // private
10539     applySort : function(){
10540         if(this.sortInfo && !this.remoteSort){
10541             var s = this.sortInfo, f = s.field;
10542             var st = this.fields.get(f).sortType;
10543             var fn = function(r1, r2){
10544                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
10545                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
10546             };
10547             this.data.sort(s.direction, fn);
10548             if(this.snapshot && this.snapshot != this.data){
10549                 this.snapshot.sort(s.direction, fn);
10550             }
10551         }
10552     },
10553
10554     /**
10555      * Sets the default sort column and order to be used by the next load operation.
10556      * @param {String} fieldName The name of the field to sort by.
10557      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10558      */
10559     setDefaultSort : function(field, dir){
10560         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
10561     },
10562
10563     /**
10564      * Sort the Records.
10565      * If remote sorting is used, the sort is performed on the server, and the cache is
10566      * reloaded. If local sorting is used, the cache is sorted internally.
10567      * @param {String} fieldName The name of the field to sort by.
10568      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
10569      */
10570     sort : function(fieldName, dir){
10571         var f = this.fields.get(fieldName);
10572         if(!dir){
10573             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
10574             
10575             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
10576                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
10577             }else{
10578                 dir = f.sortDir;
10579             }
10580         }
10581         this.sortToggle[f.name] = dir;
10582         this.sortInfo = {field: f.name, direction: dir};
10583         if(!this.remoteSort){
10584             this.applySort();
10585             this.fireEvent("datachanged", this);
10586         }else{
10587             this.load(this.lastOptions);
10588         }
10589     },
10590
10591     /**
10592      * Calls the specified function for each of the Records in the cache.
10593      * @param {Function} fn The function to call. The Record is passed as the first parameter.
10594      * Returning <em>false</em> aborts and exits the iteration.
10595      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
10596      */
10597     each : function(fn, scope){
10598         this.data.each(fn, scope);
10599     },
10600
10601     /**
10602      * Gets all records modified since the last commit.  Modified records are persisted across load operations
10603      * (e.g., during paging).
10604      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
10605      */
10606     getModifiedRecords : function(){
10607         return this.modified;
10608     },
10609
10610     // private
10611     createFilterFn : function(property, value, anyMatch){
10612         if(!value.exec){ // not a regex
10613             value = String(value);
10614             if(value.length == 0){
10615                 return false;
10616             }
10617             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
10618         }
10619         return function(r){
10620             return value.test(r.data[property]);
10621         };
10622     },
10623
10624     /**
10625      * Sums the value of <i>property</i> for each record between start and end and returns the result.
10626      * @param {String} property A field on your records
10627      * @param {Number} start The record index to start at (defaults to 0)
10628      * @param {Number} end The last record index to include (defaults to length - 1)
10629      * @return {Number} The sum
10630      */
10631     sum : function(property, start, end){
10632         var rs = this.data.items, v = 0;
10633         start = start || 0;
10634         end = (end || end === 0) ? end : rs.length-1;
10635
10636         for(var i = start; i <= end; i++){
10637             v += (rs[i].data[property] || 0);
10638         }
10639         return v;
10640     },
10641
10642     /**
10643      * Filter the records by a specified property.
10644      * @param {String} field A field on your records
10645      * @param {String/RegExp} value Either a string that the field
10646      * should start with or a RegExp to test against the field
10647      * @param {Boolean} anyMatch True to match any part not just the beginning
10648      */
10649     filter : function(property, value, anyMatch){
10650         var fn = this.createFilterFn(property, value, anyMatch);
10651         return fn ? this.filterBy(fn) : this.clearFilter();
10652     },
10653
10654     /**
10655      * Filter by a function. The specified function will be called with each
10656      * record in this data source. If the function returns true the record is included,
10657      * otherwise it is filtered.
10658      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10659      * @param {Object} scope (optional) The scope of the function (defaults to this)
10660      */
10661     filterBy : function(fn, scope){
10662         this.snapshot = this.snapshot || this.data;
10663         this.data = this.queryBy(fn, scope||this);
10664         this.fireEvent("datachanged", this);
10665     },
10666
10667     /**
10668      * Query the records by a specified property.
10669      * @param {String} field A field on your records
10670      * @param {String/RegExp} value Either a string that the field
10671      * should start with or a RegExp to test against the field
10672      * @param {Boolean} anyMatch True to match any part not just the beginning
10673      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10674      */
10675     query : function(property, value, anyMatch){
10676         var fn = this.createFilterFn(property, value, anyMatch);
10677         return fn ? this.queryBy(fn) : this.data.clone();
10678     },
10679
10680     /**
10681      * Query by a function. The specified function will be called with each
10682      * record in this data source. If the function returns true the record is included
10683      * in the results.
10684      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
10685      * @param {Object} scope (optional) The scope of the function (defaults to this)
10686       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
10687      **/
10688     queryBy : function(fn, scope){
10689         var data = this.snapshot || this.data;
10690         return data.filterBy(fn, scope||this);
10691     },
10692
10693     /**
10694      * Collects unique values for a particular dataIndex from this store.
10695      * @param {String} dataIndex The property to collect
10696      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
10697      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
10698      * @return {Array} An array of the unique values
10699      **/
10700     collect : function(dataIndex, allowNull, bypassFilter){
10701         var d = (bypassFilter === true && this.snapshot) ?
10702                 this.snapshot.items : this.data.items;
10703         var v, sv, r = [], l = {};
10704         for(var i = 0, len = d.length; i < len; i++){
10705             v = d[i].data[dataIndex];
10706             sv = String(v);
10707             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
10708                 l[sv] = true;
10709                 r[r.length] = v;
10710             }
10711         }
10712         return r;
10713     },
10714
10715     /**
10716      * Revert to a view of the Record cache with no filtering applied.
10717      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
10718      */
10719     clearFilter : function(suppressEvent){
10720         if(this.snapshot && this.snapshot != this.data){
10721             this.data = this.snapshot;
10722             delete this.snapshot;
10723             if(suppressEvent !== true){
10724                 this.fireEvent("datachanged", this);
10725             }
10726         }
10727     },
10728
10729     // private
10730     afterEdit : function(record){
10731         if(this.modified.indexOf(record) == -1){
10732             this.modified.push(record);
10733         }
10734         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
10735     },
10736     
10737     // private
10738     afterReject : function(record){
10739         this.modified.remove(record);
10740         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
10741     },
10742
10743     // private
10744     afterCommit : function(record){
10745         this.modified.remove(record);
10746         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
10747     },
10748
10749     /**
10750      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
10751      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
10752      */
10753     commitChanges : function(){
10754         var m = this.modified.slice(0);
10755         this.modified = [];
10756         for(var i = 0, len = m.length; i < len; i++){
10757             m[i].commit();
10758         }
10759     },
10760
10761     /**
10762      * Cancel outstanding changes on all changed records.
10763      */
10764     rejectChanges : function(){
10765         var m = this.modified.slice(0);
10766         this.modified = [];
10767         for(var i = 0, len = m.length; i < len; i++){
10768             m[i].reject();
10769         }
10770     },
10771
10772     onMetaChange : function(meta, rtype, o){
10773         this.recordType = rtype;
10774         this.fields = rtype.prototype.fields;
10775         delete this.snapshot;
10776         this.sortInfo = meta.sortInfo || this.sortInfo;
10777         this.modified = [];
10778         this.fireEvent('metachange', this, this.reader.meta);
10779     },
10780     
10781     moveIndex : function(data, type)
10782     {
10783         var index = this.indexOf(data);
10784         
10785         var newIndex = index + type;
10786         
10787         this.remove(data);
10788         
10789         this.insert(newIndex, data);
10790         
10791     }
10792 });/*
10793  * Based on:
10794  * Ext JS Library 1.1.1
10795  * Copyright(c) 2006-2007, Ext JS, LLC.
10796  *
10797  * Originally Released Under LGPL - original licence link has changed is not relivant.
10798  *
10799  * Fork - LGPL
10800  * <script type="text/javascript">
10801  */
10802
10803 /**
10804  * @class Roo.data.SimpleStore
10805  * @extends Roo.data.Store
10806  * Small helper class to make creating Stores from Array data easier.
10807  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
10808  * @cfg {Array} fields An array of field definition objects, or field name strings.
10809  * @cfg {Array} data The multi-dimensional array of data
10810  * @constructor
10811  * @param {Object} config
10812  */
10813 Roo.data.SimpleStore = function(config){
10814     Roo.data.SimpleStore.superclass.constructor.call(this, {
10815         isLocal : true,
10816         reader: new Roo.data.ArrayReader({
10817                 id: config.id
10818             },
10819             Roo.data.Record.create(config.fields)
10820         ),
10821         proxy : new Roo.data.MemoryProxy(config.data)
10822     });
10823     this.load();
10824 };
10825 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
10826  * Based on:
10827  * Ext JS Library 1.1.1
10828  * Copyright(c) 2006-2007, Ext JS, LLC.
10829  *
10830  * Originally Released Under LGPL - original licence link has changed is not relivant.
10831  *
10832  * Fork - LGPL
10833  * <script type="text/javascript">
10834  */
10835
10836 /**
10837 /**
10838  * @extends Roo.data.Store
10839  * @class Roo.data.JsonStore
10840  * Small helper class to make creating Stores for JSON data easier. <br/>
10841 <pre><code>
10842 var store = new Roo.data.JsonStore({
10843     url: 'get-images.php',
10844     root: 'images',
10845     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
10846 });
10847 </code></pre>
10848  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
10849  * JsonReader and HttpProxy (unless inline data is provided).</b>
10850  * @cfg {Array} fields An array of field definition objects, or field name strings.
10851  * @constructor
10852  * @param {Object} config
10853  */
10854 Roo.data.JsonStore = function(c){
10855     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
10856         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
10857         reader: new Roo.data.JsonReader(c, c.fields)
10858     }));
10859 };
10860 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
10861  * Based on:
10862  * Ext JS Library 1.1.1
10863  * Copyright(c) 2006-2007, Ext JS, LLC.
10864  *
10865  * Originally Released Under LGPL - original licence link has changed is not relivant.
10866  *
10867  * Fork - LGPL
10868  * <script type="text/javascript">
10869  */
10870
10871  
10872 Roo.data.Field = function(config){
10873     if(typeof config == "string"){
10874         config = {name: config};
10875     }
10876     Roo.apply(this, config);
10877     
10878     if(!this.type){
10879         this.type = "auto";
10880     }
10881     
10882     var st = Roo.data.SortTypes;
10883     // named sortTypes are supported, here we look them up
10884     if(typeof this.sortType == "string"){
10885         this.sortType = st[this.sortType];
10886     }
10887     
10888     // set default sortType for strings and dates
10889     if(!this.sortType){
10890         switch(this.type){
10891             case "string":
10892                 this.sortType = st.asUCString;
10893                 break;
10894             case "date":
10895                 this.sortType = st.asDate;
10896                 break;
10897             default:
10898                 this.sortType = st.none;
10899         }
10900     }
10901
10902     // define once
10903     var stripRe = /[\$,%]/g;
10904
10905     // prebuilt conversion function for this field, instead of
10906     // switching every time we're reading a value
10907     if(!this.convert){
10908         var cv, dateFormat = this.dateFormat;
10909         switch(this.type){
10910             case "":
10911             case "auto":
10912             case undefined:
10913                 cv = function(v){ return v; };
10914                 break;
10915             case "string":
10916                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
10917                 break;
10918             case "int":
10919                 cv = function(v){
10920                     return v !== undefined && v !== null && v !== '' ?
10921                            parseInt(String(v).replace(stripRe, ""), 10) : '';
10922                     };
10923                 break;
10924             case "float":
10925                 cv = function(v){
10926                     return v !== undefined && v !== null && v !== '' ?
10927                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
10928                     };
10929                 break;
10930             case "bool":
10931             case "boolean":
10932                 cv = function(v){ return v === true || v === "true" || v == 1; };
10933                 break;
10934             case "date":
10935                 cv = function(v){
10936                     if(!v){
10937                         return '';
10938                     }
10939                     if(v instanceof Date){
10940                         return v;
10941                     }
10942                     if(dateFormat){
10943                         if(dateFormat == "timestamp"){
10944                             return new Date(v*1000);
10945                         }
10946                         return Date.parseDate(v, dateFormat);
10947                     }
10948                     var parsed = Date.parse(v);
10949                     return parsed ? new Date(parsed) : null;
10950                 };
10951              break;
10952             
10953         }
10954         this.convert = cv;
10955     }
10956 };
10957
10958 Roo.data.Field.prototype = {
10959     dateFormat: null,
10960     defaultValue: "",
10961     mapping: null,
10962     sortType : null,
10963     sortDir : "ASC"
10964 };/*
10965  * Based on:
10966  * Ext JS Library 1.1.1
10967  * Copyright(c) 2006-2007, Ext JS, LLC.
10968  *
10969  * Originally Released Under LGPL - original licence link has changed is not relivant.
10970  *
10971  * Fork - LGPL
10972  * <script type="text/javascript">
10973  */
10974  
10975 // Base class for reading structured data from a data source.  This class is intended to be
10976 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
10977
10978 /**
10979  * @class Roo.data.DataReader
10980  * Base class for reading structured data from a data source.  This class is intended to be
10981  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
10982  */
10983
10984 Roo.data.DataReader = function(meta, recordType){
10985     
10986     this.meta = meta;
10987     
10988     this.recordType = recordType instanceof Array ? 
10989         Roo.data.Record.create(recordType) : recordType;
10990 };
10991
10992 Roo.data.DataReader.prototype = {
10993      /**
10994      * Create an empty record
10995      * @param {Object} data (optional) - overlay some values
10996      * @return {Roo.data.Record} record created.
10997      */
10998     newRow :  function(d) {
10999         var da =  {};
11000         this.recordType.prototype.fields.each(function(c) {
11001             switch( c.type) {
11002                 case 'int' : da[c.name] = 0; break;
11003                 case 'date' : da[c.name] = new Date(); break;
11004                 case 'float' : da[c.name] = 0.0; break;
11005                 case 'boolean' : da[c.name] = false; break;
11006                 default : da[c.name] = ""; break;
11007             }
11008             
11009         });
11010         return new this.recordType(Roo.apply(da, d));
11011     }
11012     
11013 };/*
11014  * Based on:
11015  * Ext JS Library 1.1.1
11016  * Copyright(c) 2006-2007, Ext JS, LLC.
11017  *
11018  * Originally Released Under LGPL - original licence link has changed is not relivant.
11019  *
11020  * Fork - LGPL
11021  * <script type="text/javascript">
11022  */
11023
11024 /**
11025  * @class Roo.data.DataProxy
11026  * @extends Roo.data.Observable
11027  * This class is an abstract base class for implementations which provide retrieval of
11028  * unformatted data objects.<br>
11029  * <p>
11030  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11031  * (of the appropriate type which knows how to parse the data object) to provide a block of
11032  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11033  * <p>
11034  * Custom implementations must implement the load method as described in
11035  * {@link Roo.data.HttpProxy#load}.
11036  */
11037 Roo.data.DataProxy = function(){
11038     this.addEvents({
11039         /**
11040          * @event beforeload
11041          * Fires before a network request is made to retrieve a data object.
11042          * @param {Object} This DataProxy object.
11043          * @param {Object} params The params parameter to the load function.
11044          */
11045         beforeload : true,
11046         /**
11047          * @event load
11048          * Fires before the load method's callback is called.
11049          * @param {Object} This DataProxy object.
11050          * @param {Object} o The data object.
11051          * @param {Object} arg The callback argument object passed to the load function.
11052          */
11053         load : true,
11054         /**
11055          * @event loadexception
11056          * Fires if an Exception occurs during data retrieval.
11057          * @param {Object} This DataProxy object.
11058          * @param {Object} o The data object.
11059          * @param {Object} arg The callback argument object passed to the load function.
11060          * @param {Object} e The Exception.
11061          */
11062         loadexception : true
11063     });
11064     Roo.data.DataProxy.superclass.constructor.call(this);
11065 };
11066
11067 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11068
11069     /**
11070      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11071      */
11072 /*
11073  * Based on:
11074  * Ext JS Library 1.1.1
11075  * Copyright(c) 2006-2007, Ext JS, LLC.
11076  *
11077  * Originally Released Under LGPL - original licence link has changed is not relivant.
11078  *
11079  * Fork - LGPL
11080  * <script type="text/javascript">
11081  */
11082 /**
11083  * @class Roo.data.MemoryProxy
11084  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11085  * to the Reader when its load method is called.
11086  * @constructor
11087  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11088  */
11089 Roo.data.MemoryProxy = function(data){
11090     if (data.data) {
11091         data = data.data;
11092     }
11093     Roo.data.MemoryProxy.superclass.constructor.call(this);
11094     this.data = data;
11095 };
11096
11097 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11098     
11099     /**
11100      * Load data from the requested source (in this case an in-memory
11101      * data object passed to the constructor), read the data object into
11102      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11103      * process that block using the passed callback.
11104      * @param {Object} params This parameter is not used by the MemoryProxy class.
11105      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11106      * object into a block of Roo.data.Records.
11107      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11108      * The function must be passed <ul>
11109      * <li>The Record block object</li>
11110      * <li>The "arg" argument from the load function</li>
11111      * <li>A boolean success indicator</li>
11112      * </ul>
11113      * @param {Object} scope The scope in which to call the callback
11114      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11115      */
11116     load : function(params, reader, callback, scope, arg){
11117         params = params || {};
11118         var result;
11119         try {
11120             result = reader.readRecords(this.data);
11121         }catch(e){
11122             this.fireEvent("loadexception", this, arg, null, e);
11123             callback.call(scope, null, arg, false);
11124             return;
11125         }
11126         callback.call(scope, result, arg, true);
11127     },
11128     
11129     // private
11130     update : function(params, records){
11131         
11132     }
11133 });/*
11134  * Based on:
11135  * Ext JS Library 1.1.1
11136  * Copyright(c) 2006-2007, Ext JS, LLC.
11137  *
11138  * Originally Released Under LGPL - original licence link has changed is not relivant.
11139  *
11140  * Fork - LGPL
11141  * <script type="text/javascript">
11142  */
11143 /**
11144  * @class Roo.data.HttpProxy
11145  * @extends Roo.data.DataProxy
11146  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11147  * configured to reference a certain URL.<br><br>
11148  * <p>
11149  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11150  * from which the running page was served.<br><br>
11151  * <p>
11152  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11153  * <p>
11154  * Be aware that to enable the browser to parse an XML document, the server must set
11155  * the Content-Type header in the HTTP response to "text/xml".
11156  * @constructor
11157  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11158  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11159  * will be used to make the request.
11160  */
11161 Roo.data.HttpProxy = function(conn){
11162     Roo.data.HttpProxy.superclass.constructor.call(this);
11163     // is conn a conn config or a real conn?
11164     this.conn = conn;
11165     this.useAjax = !conn || !conn.events;
11166   
11167 };
11168
11169 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11170     // thse are take from connection...
11171     
11172     /**
11173      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11174      */
11175     /**
11176      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11177      * extra parameters to each request made by this object. (defaults to undefined)
11178      */
11179     /**
11180      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11181      *  to each request made by this object. (defaults to undefined)
11182      */
11183     /**
11184      * @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)
11185      */
11186     /**
11187      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11188      */
11189      /**
11190      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11191      * @type Boolean
11192      */
11193   
11194
11195     /**
11196      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11197      * @type Boolean
11198      */
11199     /**
11200      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11201      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11202      * a finer-grained basis than the DataProxy events.
11203      */
11204     getConnection : function(){
11205         return this.useAjax ? Roo.Ajax : this.conn;
11206     },
11207
11208     /**
11209      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11210      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11211      * process that block using the passed callback.
11212      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11213      * for the request to the remote server.
11214      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11215      * object into a block of Roo.data.Records.
11216      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11217      * The function must be passed <ul>
11218      * <li>The Record block object</li>
11219      * <li>The "arg" argument from the load function</li>
11220      * <li>A boolean success indicator</li>
11221      * </ul>
11222      * @param {Object} scope The scope in which to call the callback
11223      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11224      */
11225     load : function(params, reader, callback, scope, arg){
11226         if(this.fireEvent("beforeload", this, params) !== false){
11227             var  o = {
11228                 params : params || {},
11229                 request: {
11230                     callback : callback,
11231                     scope : scope,
11232                     arg : arg
11233                 },
11234                 reader: reader,
11235                 callback : this.loadResponse,
11236                 scope: this
11237             };
11238             if(this.useAjax){
11239                 Roo.applyIf(o, this.conn);
11240                 if(this.activeRequest){
11241                     Roo.Ajax.abort(this.activeRequest);
11242                 }
11243                 this.activeRequest = Roo.Ajax.request(o);
11244             }else{
11245                 this.conn.request(o);
11246             }
11247         }else{
11248             callback.call(scope||this, null, arg, false);
11249         }
11250     },
11251
11252     // private
11253     loadResponse : function(o, success, response){
11254         delete this.activeRequest;
11255         if(!success){
11256             this.fireEvent("loadexception", this, o, response);
11257             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11258             return;
11259         }
11260         var result;
11261         try {
11262             result = o.reader.read(response);
11263         }catch(e){
11264             this.fireEvent("loadexception", this, o, response, e);
11265             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11266             return;
11267         }
11268         
11269         this.fireEvent("load", this, o, o.request.arg);
11270         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11271     },
11272
11273     // private
11274     update : function(dataSet){
11275
11276     },
11277
11278     // private
11279     updateResponse : function(dataSet){
11280
11281     }
11282 });/*
11283  * Based on:
11284  * Ext JS Library 1.1.1
11285  * Copyright(c) 2006-2007, Ext JS, LLC.
11286  *
11287  * Originally Released Under LGPL - original licence link has changed is not relivant.
11288  *
11289  * Fork - LGPL
11290  * <script type="text/javascript">
11291  */
11292
11293 /**
11294  * @class Roo.data.ScriptTagProxy
11295  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11296  * other than the originating domain of the running page.<br><br>
11297  * <p>
11298  * <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
11299  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11300  * <p>
11301  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11302  * source code that is used as the source inside a &lt;script> tag.<br><br>
11303  * <p>
11304  * In order for the browser to process the returned data, the server must wrap the data object
11305  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11306  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11307  * depending on whether the callback name was passed:
11308  * <p>
11309  * <pre><code>
11310 boolean scriptTag = false;
11311 String cb = request.getParameter("callback");
11312 if (cb != null) {
11313     scriptTag = true;
11314     response.setContentType("text/javascript");
11315 } else {
11316     response.setContentType("application/x-json");
11317 }
11318 Writer out = response.getWriter();
11319 if (scriptTag) {
11320     out.write(cb + "(");
11321 }
11322 out.print(dataBlock.toJsonString());
11323 if (scriptTag) {
11324     out.write(");");
11325 }
11326 </pre></code>
11327  *
11328  * @constructor
11329  * @param {Object} config A configuration object.
11330  */
11331 Roo.data.ScriptTagProxy = function(config){
11332     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11333     Roo.apply(this, config);
11334     this.head = document.getElementsByTagName("head")[0];
11335 };
11336
11337 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11338
11339 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11340     /**
11341      * @cfg {String} url The URL from which to request the data object.
11342      */
11343     /**
11344      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11345      */
11346     timeout : 30000,
11347     /**
11348      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11349      * the server the name of the callback function set up by the load call to process the returned data object.
11350      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11351      * javascript output which calls this named function passing the data object as its only parameter.
11352      */
11353     callbackParam : "callback",
11354     /**
11355      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11356      * name to the request.
11357      */
11358     nocache : true,
11359
11360     /**
11361      * Load data from the configured URL, read the data object into
11362      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11363      * process that block using the passed callback.
11364      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11365      * for the request to the remote server.
11366      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11367      * object into a block of Roo.data.Records.
11368      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11369      * The function must be passed <ul>
11370      * <li>The Record block object</li>
11371      * <li>The "arg" argument from the load function</li>
11372      * <li>A boolean success indicator</li>
11373      * </ul>
11374      * @param {Object} scope The scope in which to call the callback
11375      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11376      */
11377     load : function(params, reader, callback, scope, arg){
11378         if(this.fireEvent("beforeload", this, params) !== false){
11379
11380             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11381
11382             var url = this.url;
11383             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11384             if(this.nocache){
11385                 url += "&_dc=" + (new Date().getTime());
11386             }
11387             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11388             var trans = {
11389                 id : transId,
11390                 cb : "stcCallback"+transId,
11391                 scriptId : "stcScript"+transId,
11392                 params : params,
11393                 arg : arg,
11394                 url : url,
11395                 callback : callback,
11396                 scope : scope,
11397                 reader : reader
11398             };
11399             var conn = this;
11400
11401             window[trans.cb] = function(o){
11402                 conn.handleResponse(o, trans);
11403             };
11404
11405             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11406
11407             if(this.autoAbort !== false){
11408                 this.abort();
11409             }
11410
11411             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11412
11413             var script = document.createElement("script");
11414             script.setAttribute("src", url);
11415             script.setAttribute("type", "text/javascript");
11416             script.setAttribute("id", trans.scriptId);
11417             this.head.appendChild(script);
11418
11419             this.trans = trans;
11420         }else{
11421             callback.call(scope||this, null, arg, false);
11422         }
11423     },
11424
11425     // private
11426     isLoading : function(){
11427         return this.trans ? true : false;
11428     },
11429
11430     /**
11431      * Abort the current server request.
11432      */
11433     abort : function(){
11434         if(this.isLoading()){
11435             this.destroyTrans(this.trans);
11436         }
11437     },
11438
11439     // private
11440     destroyTrans : function(trans, isLoaded){
11441         this.head.removeChild(document.getElementById(trans.scriptId));
11442         clearTimeout(trans.timeoutId);
11443         if(isLoaded){
11444             window[trans.cb] = undefined;
11445             try{
11446                 delete window[trans.cb];
11447             }catch(e){}
11448         }else{
11449             // if hasn't been loaded, wait for load to remove it to prevent script error
11450             window[trans.cb] = function(){
11451                 window[trans.cb] = undefined;
11452                 try{
11453                     delete window[trans.cb];
11454                 }catch(e){}
11455             };
11456         }
11457     },
11458
11459     // private
11460     handleResponse : function(o, trans){
11461         this.trans = false;
11462         this.destroyTrans(trans, true);
11463         var result;
11464         try {
11465             result = trans.reader.readRecords(o);
11466         }catch(e){
11467             this.fireEvent("loadexception", this, o, trans.arg, e);
11468             trans.callback.call(trans.scope||window, null, trans.arg, false);
11469             return;
11470         }
11471         this.fireEvent("load", this, o, trans.arg);
11472         trans.callback.call(trans.scope||window, result, trans.arg, true);
11473     },
11474
11475     // private
11476     handleFailure : function(trans){
11477         this.trans = false;
11478         this.destroyTrans(trans, false);
11479         this.fireEvent("loadexception", this, null, trans.arg);
11480         trans.callback.call(trans.scope||window, null, trans.arg, false);
11481     }
11482 });/*
11483  * Based on:
11484  * Ext JS Library 1.1.1
11485  * Copyright(c) 2006-2007, Ext JS, LLC.
11486  *
11487  * Originally Released Under LGPL - original licence link has changed is not relivant.
11488  *
11489  * Fork - LGPL
11490  * <script type="text/javascript">
11491  */
11492
11493 /**
11494  * @class Roo.data.JsonReader
11495  * @extends Roo.data.DataReader
11496  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
11497  * based on mappings in a provided Roo.data.Record constructor.
11498  * 
11499  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
11500  * in the reply previously. 
11501  * 
11502  * <p>
11503  * Example code:
11504  * <pre><code>
11505 var RecordDef = Roo.data.Record.create([
11506     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
11507     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
11508 ]);
11509 var myReader = new Roo.data.JsonReader({
11510     totalProperty: "results",    // The property which contains the total dataset size (optional)
11511     root: "rows",                // The property which contains an Array of row objects
11512     id: "id"                     // The property within each row object that provides an ID for the record (optional)
11513 }, RecordDef);
11514 </code></pre>
11515  * <p>
11516  * This would consume a JSON file like this:
11517  * <pre><code>
11518 { 'results': 2, 'rows': [
11519     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
11520     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
11521 }
11522 </code></pre>
11523  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
11524  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
11525  * paged from the remote server.
11526  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
11527  * @cfg {String} root name of the property which contains the Array of row objects.
11528  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
11529  * @cfg {Array} fields Array of field definition objects
11530  * @constructor
11531  * Create a new JsonReader
11532  * @param {Object} meta Metadata configuration options
11533  * @param {Object} recordType Either an Array of field definition objects,
11534  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
11535  */
11536 Roo.data.JsonReader = function(meta, recordType){
11537     
11538     meta = meta || {};
11539     // set some defaults:
11540     Roo.applyIf(meta, {
11541         totalProperty: 'total',
11542         successProperty : 'success',
11543         root : 'data',
11544         id : 'id'
11545     });
11546     
11547     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
11548 };
11549 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
11550     
11551     /**
11552      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
11553      * Used by Store query builder to append _requestMeta to params.
11554      * 
11555      */
11556     metaFromRemote : false,
11557     /**
11558      * This method is only used by a DataProxy which has retrieved data from a remote server.
11559      * @param {Object} response The XHR object which contains the JSON data in its responseText.
11560      * @return {Object} data A data block which is used by an Roo.data.Store object as
11561      * a cache of Roo.data.Records.
11562      */
11563     read : function(response){
11564         var json = response.responseText;
11565        
11566         var o = /* eval:var:o */ eval("("+json+")");
11567         if(!o) {
11568             throw {message: "JsonReader.read: Json object not found"};
11569         }
11570         
11571         if(o.metaData){
11572             
11573             delete this.ef;
11574             this.metaFromRemote = true;
11575             this.meta = o.metaData;
11576             this.recordType = Roo.data.Record.create(o.metaData.fields);
11577             this.onMetaChange(this.meta, this.recordType, o);
11578         }
11579         return this.readRecords(o);
11580     },
11581
11582     // private function a store will implement
11583     onMetaChange : function(meta, recordType, o){
11584
11585     },
11586
11587     /**
11588          * @ignore
11589          */
11590     simpleAccess: function(obj, subsc) {
11591         return obj[subsc];
11592     },
11593
11594         /**
11595          * @ignore
11596          */
11597     getJsonAccessor: function(){
11598         var re = /[\[\.]/;
11599         return function(expr) {
11600             try {
11601                 return(re.test(expr))
11602                     ? new Function("obj", "return obj." + expr)
11603                     : function(obj){
11604                         return obj[expr];
11605                     };
11606             } catch(e){}
11607             return Roo.emptyFn;
11608         };
11609     }(),
11610
11611     /**
11612      * Create a data block containing Roo.data.Records from an XML document.
11613      * @param {Object} o An object which contains an Array of row objects in the property specified
11614      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
11615      * which contains the total size of the dataset.
11616      * @return {Object} data A data block which is used by an Roo.data.Store object as
11617      * a cache of Roo.data.Records.
11618      */
11619     readRecords : function(o){
11620         /**
11621          * After any data loads, the raw JSON data is available for further custom processing.
11622          * @type Object
11623          */
11624         this.o = o;
11625         var s = this.meta, Record = this.recordType,
11626             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
11627
11628 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
11629         if (!this.ef) {
11630             if(s.totalProperty) {
11631                     this.getTotal = this.getJsonAccessor(s.totalProperty);
11632                 }
11633                 if(s.successProperty) {
11634                     this.getSuccess = this.getJsonAccessor(s.successProperty);
11635                 }
11636                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
11637                 if (s.id) {
11638                         var g = this.getJsonAccessor(s.id);
11639                         this.getId = function(rec) {
11640                                 var r = g(rec);  
11641                                 return (r === undefined || r === "") ? null : r;
11642                         };
11643                 } else {
11644                         this.getId = function(){return null;};
11645                 }
11646             this.ef = [];
11647             for(var jj = 0; jj < fl; jj++){
11648                 f = fi[jj];
11649                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
11650                 this.ef[jj] = this.getJsonAccessor(map);
11651             }
11652         }
11653
11654         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
11655         if(s.totalProperty){
11656             var vt = parseInt(this.getTotal(o), 10);
11657             if(!isNaN(vt)){
11658                 totalRecords = vt;
11659             }
11660         }
11661         if(s.successProperty){
11662             var vs = this.getSuccess(o);
11663             if(vs === false || vs === 'false'){
11664                 success = false;
11665             }
11666         }
11667         var records = [];
11668         for(var i = 0; i < c; i++){
11669                 var n = root[i];
11670             var values = {};
11671             var id = this.getId(n);
11672             for(var j = 0; j < fl; j++){
11673                 f = fi[j];
11674             var v = this.ef[j](n);
11675             if (!f.convert) {
11676                 Roo.log('missing convert for ' + f.name);
11677                 Roo.log(f);
11678                 continue;
11679             }
11680             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
11681             }
11682             var record = new Record(values, id);
11683             record.json = n;
11684             records[i] = record;
11685         }
11686         return {
11687             raw : o,
11688             success : success,
11689             records : records,
11690             totalRecords : totalRecords
11691         };
11692     }
11693 });/*
11694  * Based on:
11695  * Ext JS Library 1.1.1
11696  * Copyright(c) 2006-2007, Ext JS, LLC.
11697  *
11698  * Originally Released Under LGPL - original licence link has changed is not relivant.
11699  *
11700  * Fork - LGPL
11701  * <script type="text/javascript">
11702  */
11703
11704 /**
11705  * @class Roo.data.ArrayReader
11706  * @extends Roo.data.DataReader
11707  * Data reader class to create an Array of Roo.data.Record objects from an Array.
11708  * Each element of that Array represents a row of data fields. The
11709  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
11710  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
11711  * <p>
11712  * Example code:.
11713  * <pre><code>
11714 var RecordDef = Roo.data.Record.create([
11715     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
11716     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
11717 ]);
11718 var myReader = new Roo.data.ArrayReader({
11719     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
11720 }, RecordDef);
11721 </code></pre>
11722  * <p>
11723  * This would consume an Array like this:
11724  * <pre><code>
11725 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
11726   </code></pre>
11727  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
11728  * @constructor
11729  * Create a new JsonReader
11730  * @param {Object} meta Metadata configuration options.
11731  * @param {Object} recordType Either an Array of field definition objects
11732  * as specified to {@link Roo.data.Record#create},
11733  * or an {@link Roo.data.Record} object
11734  * created using {@link Roo.data.Record#create}.
11735  */
11736 Roo.data.ArrayReader = function(meta, recordType){
11737     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
11738 };
11739
11740 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
11741     /**
11742      * Create a data block containing Roo.data.Records from an XML document.
11743      * @param {Object} o An Array of row objects which represents the dataset.
11744      * @return {Object} data A data block which is used by an Roo.data.Store object as
11745      * a cache of Roo.data.Records.
11746      */
11747     readRecords : function(o){
11748         var sid = this.meta ? this.meta.id : null;
11749         var recordType = this.recordType, fields = recordType.prototype.fields;
11750         var records = [];
11751         var root = o;
11752             for(var i = 0; i < root.length; i++){
11753                     var n = root[i];
11754                 var values = {};
11755                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
11756                 for(var j = 0, jlen = fields.length; j < jlen; j++){
11757                 var f = fields.items[j];
11758                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
11759                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
11760                 v = f.convert(v);
11761                 values[f.name] = v;
11762             }
11763                 var record = new recordType(values, id);
11764                 record.json = n;
11765                 records[records.length] = record;
11766             }
11767             return {
11768                 records : records,
11769                 totalRecords : records.length
11770             };
11771     }
11772 });/*
11773  * - LGPL
11774  * * 
11775  */
11776
11777 /**
11778  * @class Roo.bootstrap.ComboBox
11779  * @extends Roo.bootstrap.TriggerField
11780  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
11781  * @cfg {Boolean} append (true|false) default false
11782  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
11783  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
11784  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
11785  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
11786  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
11787  * @cfg {Boolean} animate default true
11788  * @cfg {Boolean} emptyResultText only for touch device
11789  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
11790  * @constructor
11791  * Create a new ComboBox.
11792  * @param {Object} config Configuration options
11793  */
11794 Roo.bootstrap.ComboBox = function(config){
11795     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
11796     this.addEvents({
11797         /**
11798          * @event expand
11799          * Fires when the dropdown list is expanded
11800              * @param {Roo.bootstrap.ComboBox} combo This combo box
11801              */
11802         'expand' : true,
11803         /**
11804          * @event collapse
11805          * Fires when the dropdown list is collapsed
11806              * @param {Roo.bootstrap.ComboBox} combo This combo box
11807              */
11808         'collapse' : true,
11809         /**
11810          * @event beforeselect
11811          * Fires before a list item is selected. Return false to cancel the selection.
11812              * @param {Roo.bootstrap.ComboBox} combo This combo box
11813              * @param {Roo.data.Record} record The data record returned from the underlying store
11814              * @param {Number} index The index of the selected item in the dropdown list
11815              */
11816         'beforeselect' : true,
11817         /**
11818          * @event select
11819          * Fires when a list item is selected
11820              * @param {Roo.bootstrap.ComboBox} combo This combo box
11821              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
11822              * @param {Number} index The index of the selected item in the dropdown list
11823              */
11824         'select' : true,
11825         /**
11826          * @event beforequery
11827          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
11828          * The event object passed has these properties:
11829              * @param {Roo.bootstrap.ComboBox} combo This combo box
11830              * @param {String} query The query
11831              * @param {Boolean} forceAll true to force "all" query
11832              * @param {Boolean} cancel true to cancel the query
11833              * @param {Object} e The query event object
11834              */
11835         'beforequery': true,
11836          /**
11837          * @event add
11838          * Fires when the 'add' icon is pressed (add a listener to enable add button)
11839              * @param {Roo.bootstrap.ComboBox} combo This combo box
11840              */
11841         'add' : true,
11842         /**
11843          * @event edit
11844          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
11845              * @param {Roo.bootstrap.ComboBox} combo This combo box
11846              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
11847              */
11848         'edit' : true,
11849         /**
11850          * @event remove
11851          * Fires when the remove value from the combobox array
11852              * @param {Roo.bootstrap.ComboBox} combo This combo box
11853              */
11854         'remove' : true,
11855         /**
11856          * @event afterremove
11857          * Fires when the remove value from the combobox array
11858              * @param {Roo.bootstrap.ComboBox} combo This combo box
11859              */
11860         'afterremove' : true,
11861         /**
11862          * @event specialfilter
11863          * Fires when specialfilter
11864             * @param {Roo.bootstrap.ComboBox} combo This combo box
11865             */
11866         'specialfilter' : true,
11867         /**
11868          * @event tick
11869          * Fires when tick the element
11870             * @param {Roo.bootstrap.ComboBox} combo This combo box
11871             */
11872         'tick' : true,
11873         /**
11874          * @event touchviewdisplay
11875          * Fires when touch view require special display (default is using displayField)
11876             * @param {Roo.bootstrap.ComboBox} combo This combo box
11877             * @param {Object} cfg set html .
11878             */
11879         'touchviewdisplay' : true
11880         
11881     });
11882     
11883     this.item = [];
11884     this.tickItems = [];
11885     
11886     this.selectedIndex = -1;
11887     if(this.mode == 'local'){
11888         if(config.queryDelay === undefined){
11889             this.queryDelay = 10;
11890         }
11891         if(config.minChars === undefined){
11892             this.minChars = 0;
11893         }
11894     }
11895 };
11896
11897 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
11898      
11899     /**
11900      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
11901      * rendering into an Roo.Editor, defaults to false)
11902      */
11903     /**
11904      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
11905      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
11906      */
11907     /**
11908      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
11909      */
11910     /**
11911      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
11912      * the dropdown list (defaults to undefined, with no header element)
11913      */
11914
11915      /**
11916      * @cfg {String/Roo.Template} tpl The template to use to render the output
11917      */
11918      
11919      /**
11920      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
11921      */
11922     listWidth: undefined,
11923     /**
11924      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
11925      * mode = 'remote' or 'text' if mode = 'local')
11926      */
11927     displayField: undefined,
11928     
11929     /**
11930      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
11931      * mode = 'remote' or 'value' if mode = 'local'). 
11932      * Note: use of a valueField requires the user make a selection
11933      * in order for a value to be mapped.
11934      */
11935     valueField: undefined,
11936     
11937     
11938     /**
11939      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
11940      * field's data value (defaults to the underlying DOM element's name)
11941      */
11942     hiddenName: undefined,
11943     /**
11944      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
11945      */
11946     listClass: '',
11947     /**
11948      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
11949      */
11950     selectedClass: 'active',
11951     
11952     /**
11953      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
11954      */
11955     shadow:'sides',
11956     /**
11957      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
11958      * anchor positions (defaults to 'tl-bl')
11959      */
11960     listAlign: 'tl-bl?',
11961     /**
11962      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
11963      */
11964     maxHeight: 300,
11965     /**
11966      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
11967      * query specified by the allQuery config option (defaults to 'query')
11968      */
11969     triggerAction: 'query',
11970     /**
11971      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
11972      * (defaults to 4, does not apply if editable = false)
11973      */
11974     minChars : 4,
11975     /**
11976      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
11977      * delay (typeAheadDelay) if it matches a known value (defaults to false)
11978      */
11979     typeAhead: false,
11980     /**
11981      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
11982      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
11983      */
11984     queryDelay: 500,
11985     /**
11986      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
11987      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
11988      */
11989     pageSize: 0,
11990     /**
11991      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
11992      * when editable = true (defaults to false)
11993      */
11994     selectOnFocus:false,
11995     /**
11996      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
11997      */
11998     queryParam: 'query',
11999     /**
12000      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12001      * when mode = 'remote' (defaults to 'Loading...')
12002      */
12003     loadingText: 'Loading...',
12004     /**
12005      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12006      */
12007     resizable: false,
12008     /**
12009      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12010      */
12011     handleHeight : 8,
12012     /**
12013      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12014      * traditional select (defaults to true)
12015      */
12016     editable: true,
12017     /**
12018      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12019      */
12020     allQuery: '',
12021     /**
12022      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12023      */
12024     mode: 'remote',
12025     /**
12026      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12027      * listWidth has a higher value)
12028      */
12029     minListWidth : 70,
12030     /**
12031      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12032      * allow the user to set arbitrary text into the field (defaults to false)
12033      */
12034     forceSelection:false,
12035     /**
12036      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12037      * if typeAhead = true (defaults to 250)
12038      */
12039     typeAheadDelay : 250,
12040     /**
12041      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12042      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12043      */
12044     valueNotFoundText : undefined,
12045     /**
12046      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12047      */
12048     blockFocus : false,
12049     
12050     /**
12051      * @cfg {Boolean} disableClear Disable showing of clear button.
12052      */
12053     disableClear : false,
12054     /**
12055      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12056      */
12057     alwaysQuery : false,
12058     
12059     /**
12060      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12061      */
12062     multiple : false,
12063     
12064     /**
12065      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12066      */
12067     invalidClass : "has-warning",
12068     
12069     /**
12070      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12071      */
12072     validClass : "has-success",
12073     
12074     /**
12075      * @cfg {Boolean} specialFilter (true|false) special filter default false
12076      */
12077     specialFilter : false,
12078     
12079     /**
12080      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12081      */
12082     mobileTouchView : true,
12083     
12084     //private
12085     addicon : false,
12086     editicon: false,
12087     
12088     page: 0,
12089     hasQuery: false,
12090     append: false,
12091     loadNext: false,
12092     autoFocus : true,
12093     tickable : false,
12094     btnPosition : 'right',
12095     triggerList : true,
12096     showToggleBtn : true,
12097     animate : true,
12098     emptyResultText: 'Empty',
12099     triggerText : 'Select',
12100     
12101     // element that contains real text value.. (when hidden is used..)
12102     
12103     getAutoCreate : function()
12104     {
12105         var cfg = false;
12106         
12107         /*
12108          * Touch Devices
12109          */
12110         
12111         if(Roo.isTouch && this.mobileTouchView){
12112             cfg = this.getAutoCreateTouchView();
12113             return cfg;;
12114         }
12115         
12116         /*
12117          *  Normal ComboBox
12118          */
12119         if(!this.tickable){
12120             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12121             return cfg;
12122         }
12123         
12124         /*
12125          *  ComboBox with tickable selections
12126          */
12127              
12128         var align = this.labelAlign || this.parentLabelAlign();
12129         
12130         cfg = {
12131             cls : 'form-group roo-combobox-tickable' //input-group
12132         };
12133         
12134         var buttons = {
12135             tag : 'div',
12136             cls : 'tickable-buttons',
12137             cn : [
12138                 {
12139                     tag : 'button',
12140                     type : 'button',
12141                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12142                     html : this.triggerText
12143                 },
12144                 {
12145                     tag : 'button',
12146                     type : 'button',
12147                     name : 'ok',
12148                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12149                     html : 'Done'
12150                 },
12151                 {
12152                     tag : 'button',
12153                     type : 'button',
12154                     name : 'cancel',
12155                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12156                     html : 'Cancel'
12157                 }
12158             ]
12159         };
12160         
12161         if(this.editable){
12162             buttons.cn.unshift({
12163                 tag: 'input',
12164                 cls: 'roo-select2-search-field-input'
12165             });
12166         }
12167         
12168         var _this = this;
12169         
12170         Roo.each(buttons.cn, function(c){
12171             if (_this.size) {
12172                 c.cls += ' btn-' + _this.size;
12173             }
12174
12175             if (_this.disabled) {
12176                 c.disabled = true;
12177             }
12178         });
12179         
12180         var box = {
12181             tag: 'div',
12182             cn: [
12183                 {
12184                     tag: 'input',
12185                     type : 'hidden',
12186                     cls: 'form-hidden-field'
12187                 },
12188                 {
12189                     tag: 'ul',
12190                     cls: 'roo-select2-choices',
12191                     cn:[
12192                         {
12193                             tag: 'li',
12194                             cls: 'roo-select2-search-field',
12195                             cn: [
12196
12197                                 buttons
12198                             ]
12199                         }
12200                     ]
12201                 }
12202             ]
12203         };
12204         
12205         var combobox = {
12206             cls: 'roo-select2-container input-group roo-select2-container-multi',
12207             cn: [
12208                 box
12209 //                {
12210 //                    tag: 'ul',
12211 //                    cls: 'typeahead typeahead-long dropdown-menu',
12212 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12213 //                }
12214             ]
12215         };
12216         
12217         if(this.hasFeedback && !this.allowBlank){
12218             
12219             var feedback = {
12220                 tag: 'span',
12221                 cls: 'glyphicon form-control-feedback'
12222             };
12223
12224             combobox.cn.push(feedback);
12225         }
12226         
12227         if (align ==='left' && this.fieldLabel.length && this.labelWidth) {
12228             
12229 //                Roo.log("left and has label");
12230                 cfg.cn = [
12231                     
12232                     {
12233                         tag: 'label',
12234                         'for' :  id,
12235                         cls : 'control-label col-sm-' + this.labelWidth,
12236                         html : this.fieldLabel
12237                         
12238                     },
12239                     {
12240                         cls : "col-sm-" + (12 - this.labelWidth), 
12241                         cn: [
12242                             combobox
12243                         ]
12244                     }
12245                     
12246                 ];
12247         } else if ( this.fieldLabel.length) {
12248 //                Roo.log(" label");
12249                  cfg.cn = [
12250                    
12251                     {
12252                         tag: 'label',
12253                         //cls : 'input-group-addon',
12254                         html : this.fieldLabel
12255                         
12256                     },
12257                     
12258                     combobox
12259                     
12260                 ];
12261
12262         } else {
12263             
12264 //                Roo.log(" no label && no align");
12265                 cfg = combobox
12266                      
12267                 
12268         }
12269          
12270         var settings=this;
12271         ['xs','sm','md','lg'].map(function(size){
12272             if (settings[size]) {
12273                 cfg.cls += ' col-' + size + '-' + settings[size];
12274             }
12275         });
12276         
12277         return cfg;
12278         
12279     },
12280     
12281     _initEventsCalled : false,
12282     
12283     // private
12284     initEvents: function()
12285     {
12286         
12287         if (this._initEventsCalled) { // as we call render... prevent looping...
12288             return;
12289         }
12290         this._initEventsCalled = true;
12291         
12292         if (!this.store) {
12293             throw "can not find store for combo";
12294         }
12295         
12296         this.store = Roo.factory(this.store, Roo.data);
12297         
12298         // if we are building from html. then this element is so complex, that we can not really
12299         // use the rendered HTML.
12300         // so we have to trash and replace the previous code.
12301         if (Roo.XComponent.build_from_html) {
12302             
12303             // remove this element....
12304             var e = this.el.dom, k=0;
12305             while (e ) { e = e.previousSibling;  ++k;}
12306
12307             this.el.remove();
12308             
12309             this.el=false;
12310             this.rendered = false;
12311             
12312             this.render(this.parent().getChildContainer(true), k);
12313             
12314             
12315             
12316         }
12317         
12318         
12319         /*
12320          * Touch Devices
12321          */
12322         
12323         if(Roo.isTouch && this.mobileTouchView){
12324             this.initTouchView();
12325             return;
12326         }
12327         
12328         if(this.tickable){
12329             this.initTickableEvents();
12330             return;
12331         }
12332         
12333         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
12334         
12335         if(this.hiddenName){
12336             
12337             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12338             
12339             this.hiddenField.dom.value =
12340                 this.hiddenValue !== undefined ? this.hiddenValue :
12341                 this.value !== undefined ? this.value : '';
12342
12343             // prevent input submission
12344             this.el.dom.removeAttribute('name');
12345             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12346              
12347              
12348         }
12349         //if(Roo.isGecko){
12350         //    this.el.dom.setAttribute('autocomplete', 'off');
12351         //}
12352         
12353         var cls = 'x-combo-list';
12354         
12355         //this.list = new Roo.Layer({
12356         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
12357         //});
12358         
12359         var _this = this;
12360         
12361         (function(){
12362             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12363             _this.list.setWidth(lw);
12364         }).defer(100);
12365         
12366         this.list.on('mouseover', this.onViewOver, this);
12367         this.list.on('mousemove', this.onViewMove, this);
12368         
12369         this.list.on('scroll', this.onViewScroll, this);
12370         
12371         /*
12372         this.list.swallowEvent('mousewheel');
12373         this.assetHeight = 0;
12374
12375         if(this.title){
12376             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
12377             this.assetHeight += this.header.getHeight();
12378         }
12379
12380         this.innerList = this.list.createChild({cls:cls+'-inner'});
12381         this.innerList.on('mouseover', this.onViewOver, this);
12382         this.innerList.on('mousemove', this.onViewMove, this);
12383         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12384         
12385         if(this.allowBlank && !this.pageSize && !this.disableClear){
12386             this.footer = this.list.createChild({cls:cls+'-ft'});
12387             this.pageTb = new Roo.Toolbar(this.footer);
12388            
12389         }
12390         if(this.pageSize){
12391             this.footer = this.list.createChild({cls:cls+'-ft'});
12392             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
12393                     {pageSize: this.pageSize});
12394             
12395         }
12396         
12397         if (this.pageTb && this.allowBlank && !this.disableClear) {
12398             var _this = this;
12399             this.pageTb.add(new Roo.Toolbar.Fill(), {
12400                 cls: 'x-btn-icon x-btn-clear',
12401                 text: '&#160;',
12402                 handler: function()
12403                 {
12404                     _this.collapse();
12405                     _this.clearValue();
12406                     _this.onSelect(false, -1);
12407                 }
12408             });
12409         }
12410         if (this.footer) {
12411             this.assetHeight += this.footer.getHeight();
12412         }
12413         */
12414             
12415         if(!this.tpl){
12416             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
12417         }
12418
12419         this.view = new Roo.View(this.list, this.tpl, {
12420             singleSelect:true, store: this.store, selectedClass: this.selectedClass
12421         });
12422         //this.view.wrapEl.setDisplayed(false);
12423         this.view.on('click', this.onViewClick, this);
12424         
12425         
12426         
12427         this.store.on('beforeload', this.onBeforeLoad, this);
12428         this.store.on('load', this.onLoad, this);
12429         this.store.on('loadexception', this.onLoadException, this);
12430         /*
12431         if(this.resizable){
12432             this.resizer = new Roo.Resizable(this.list,  {
12433                pinned:true, handles:'se'
12434             });
12435             this.resizer.on('resize', function(r, w, h){
12436                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
12437                 this.listWidth = w;
12438                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
12439                 this.restrictHeight();
12440             }, this);
12441             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
12442         }
12443         */
12444         if(!this.editable){
12445             this.editable = true;
12446             this.setEditable(false);
12447         }
12448         
12449         /*
12450         
12451         if (typeof(this.events.add.listeners) != 'undefined') {
12452             
12453             this.addicon = this.wrap.createChild(
12454                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
12455        
12456             this.addicon.on('click', function(e) {
12457                 this.fireEvent('add', this);
12458             }, this);
12459         }
12460         if (typeof(this.events.edit.listeners) != 'undefined') {
12461             
12462             this.editicon = this.wrap.createChild(
12463                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
12464             if (this.addicon) {
12465                 this.editicon.setStyle('margin-left', '40px');
12466             }
12467             this.editicon.on('click', function(e) {
12468                 
12469                 // we fire even  if inothing is selected..
12470                 this.fireEvent('edit', this, this.lastData );
12471                 
12472             }, this);
12473         }
12474         */
12475         
12476         this.keyNav = new Roo.KeyNav(this.inputEl(), {
12477             "up" : function(e){
12478                 this.inKeyMode = true;
12479                 this.selectPrev();
12480             },
12481
12482             "down" : function(e){
12483                 if(!this.isExpanded()){
12484                     this.onTriggerClick();
12485                 }else{
12486                     this.inKeyMode = true;
12487                     this.selectNext();
12488                 }
12489             },
12490
12491             "enter" : function(e){
12492 //                this.onViewClick();
12493                 //return true;
12494                 this.collapse();
12495                 
12496                 if(this.fireEvent("specialkey", this, e)){
12497                     this.onViewClick(false);
12498                 }
12499                 
12500                 return true;
12501             },
12502
12503             "esc" : function(e){
12504                 this.collapse();
12505             },
12506
12507             "tab" : function(e){
12508                 this.collapse();
12509                 
12510                 if(this.fireEvent("specialkey", this, e)){
12511                     this.onViewClick(false);
12512                 }
12513                 
12514                 return true;
12515             },
12516
12517             scope : this,
12518
12519             doRelay : function(foo, bar, hname){
12520                 if(hname == 'down' || this.scope.isExpanded()){
12521                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12522                 }
12523                 return true;
12524             },
12525
12526             forceKeyDown: true
12527         });
12528         
12529         
12530         this.queryDelay = Math.max(this.queryDelay || 10,
12531                 this.mode == 'local' ? 10 : 250);
12532         
12533         
12534         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12535         
12536         if(this.typeAhead){
12537             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12538         }
12539         if(this.editable !== false){
12540             this.inputEl().on("keyup", this.onKeyUp, this);
12541         }
12542         if(this.forceSelection){
12543             this.inputEl().on('blur', this.doForce, this);
12544         }
12545         
12546         if(this.multiple){
12547             this.choices = this.el.select('ul.roo-select2-choices', true).first();
12548             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12549         }
12550     },
12551     
12552     initTickableEvents: function()
12553     {   
12554         this.createList();
12555         
12556         if(this.hiddenName){
12557             
12558             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
12559             
12560             this.hiddenField.dom.value =
12561                 this.hiddenValue !== undefined ? this.hiddenValue :
12562                 this.value !== undefined ? this.value : '';
12563
12564             // prevent input submission
12565             this.el.dom.removeAttribute('name');
12566             this.hiddenField.dom.setAttribute('name', this.hiddenName);
12567              
12568              
12569         }
12570         
12571 //        this.list = this.el.select('ul.dropdown-menu',true).first();
12572         
12573         this.choices = this.el.select('ul.roo-select2-choices', true).first();
12574         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
12575         if(this.triggerList){
12576             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
12577         }
12578          
12579         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
12580         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
12581         
12582         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
12583         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
12584         
12585         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
12586         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
12587         
12588         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
12589         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
12590         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
12591         
12592         this.okBtn.hide();
12593         this.cancelBtn.hide();
12594         
12595         var _this = this;
12596         
12597         (function(){
12598             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
12599             _this.list.setWidth(lw);
12600         }).defer(100);
12601         
12602         this.list.on('mouseover', this.onViewOver, this);
12603         this.list.on('mousemove', this.onViewMove, this);
12604         
12605         this.list.on('scroll', this.onViewScroll, this);
12606         
12607         if(!this.tpl){
12608             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></li>';
12609         }
12610
12611         this.view = new Roo.View(this.list, this.tpl, {
12612             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
12613         });
12614         
12615         //this.view.wrapEl.setDisplayed(false);
12616         this.view.on('click', this.onViewClick, this);
12617         
12618         
12619         
12620         this.store.on('beforeload', this.onBeforeLoad, this);
12621         this.store.on('load', this.onLoad, this);
12622         this.store.on('loadexception', this.onLoadException, this);
12623         
12624         if(this.editable){
12625             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
12626                 "up" : function(e){
12627                     this.inKeyMode = true;
12628                     this.selectPrev();
12629                 },
12630
12631                 "down" : function(e){
12632                     this.inKeyMode = true;
12633                     this.selectNext();
12634                 },
12635
12636                 "enter" : function(e){
12637                     if(this.fireEvent("specialkey", this, e)){
12638                         this.onViewClick(false);
12639                     }
12640                     
12641                     return true;
12642                 },
12643
12644                 "esc" : function(e){
12645                     this.onTickableFooterButtonClick(e, false, false);
12646                 },
12647
12648                 "tab" : function(e){
12649                     this.fireEvent("specialkey", this, e);
12650                     
12651                     this.onTickableFooterButtonClick(e, false, false);
12652                     
12653                     return true;
12654                 },
12655
12656                 scope : this,
12657
12658                 doRelay : function(e, fn, key){
12659                     if(this.scope.isExpanded()){
12660                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
12661                     }
12662                     return true;
12663                 },
12664
12665                 forceKeyDown: true
12666             });
12667         }
12668         
12669         this.queryDelay = Math.max(this.queryDelay || 10,
12670                 this.mode == 'local' ? 10 : 250);
12671         
12672         
12673         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
12674         
12675         if(this.typeAhead){
12676             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
12677         }
12678         
12679         if(this.editable !== false){
12680             this.tickableInputEl().on("keyup", this.onKeyUp, this);
12681         }
12682         
12683     },
12684
12685     onDestroy : function(){
12686         if(this.view){
12687             this.view.setStore(null);
12688             this.view.el.removeAllListeners();
12689             this.view.el.remove();
12690             this.view.purgeListeners();
12691         }
12692         if(this.list){
12693             this.list.dom.innerHTML  = '';
12694         }
12695         
12696         if(this.store){
12697             this.store.un('beforeload', this.onBeforeLoad, this);
12698             this.store.un('load', this.onLoad, this);
12699             this.store.un('loadexception', this.onLoadException, this);
12700         }
12701         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
12702     },
12703
12704     // private
12705     fireKey : function(e){
12706         if(e.isNavKeyPress() && !this.list.isVisible()){
12707             this.fireEvent("specialkey", this, e);
12708         }
12709     },
12710
12711     // private
12712     onResize: function(w, h){
12713 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
12714 //        
12715 //        if(typeof w != 'number'){
12716 //            // we do not handle it!?!?
12717 //            return;
12718 //        }
12719 //        var tw = this.trigger.getWidth();
12720 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
12721 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
12722 //        var x = w - tw;
12723 //        this.inputEl().setWidth( this.adjustWidth('input', x));
12724 //            
12725 //        //this.trigger.setStyle('left', x+'px');
12726 //        
12727 //        if(this.list && this.listWidth === undefined){
12728 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
12729 //            this.list.setWidth(lw);
12730 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
12731 //        }
12732         
12733     
12734         
12735     },
12736
12737     /**
12738      * Allow or prevent the user from directly editing the field text.  If false is passed,
12739      * the user will only be able to select from the items defined in the dropdown list.  This method
12740      * is the runtime equivalent of setting the 'editable' config option at config time.
12741      * @param {Boolean} value True to allow the user to directly edit the field text
12742      */
12743     setEditable : function(value){
12744         if(value == this.editable){
12745             return;
12746         }
12747         this.editable = value;
12748         if(!value){
12749             this.inputEl().dom.setAttribute('readOnly', true);
12750             this.inputEl().on('mousedown', this.onTriggerClick,  this);
12751             this.inputEl().addClass('x-combo-noedit');
12752         }else{
12753             this.inputEl().dom.setAttribute('readOnly', false);
12754             this.inputEl().un('mousedown', this.onTriggerClick,  this);
12755             this.inputEl().removeClass('x-combo-noedit');
12756         }
12757     },
12758
12759     // private
12760     
12761     onBeforeLoad : function(combo,opts){
12762         if(!this.hasFocus){
12763             return;
12764         }
12765          if (!opts.add) {
12766             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
12767          }
12768         this.restrictHeight();
12769         this.selectedIndex = -1;
12770     },
12771
12772     // private
12773     onLoad : function(){
12774         
12775         this.hasQuery = false;
12776         
12777         if(!this.hasFocus){
12778             return;
12779         }
12780         
12781         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12782             this.loading.hide();
12783         }
12784              
12785         if(this.store.getCount() > 0){
12786             this.expand();
12787             this.restrictHeight();
12788             if(this.lastQuery == this.allQuery){
12789                 if(this.editable && !this.tickable){
12790                     this.inputEl().dom.select();
12791                 }
12792                 
12793                 if(
12794                     !this.selectByValue(this.value, true) &&
12795                     this.autoFocus && 
12796                     (
12797                         !this.store.lastOptions ||
12798                         typeof(this.store.lastOptions.add) == 'undefined' || 
12799                         this.store.lastOptions.add != true
12800                     )
12801                 ){
12802                     this.select(0, true);
12803                 }
12804             }else{
12805                 if(this.autoFocus){
12806                     this.selectNext();
12807                 }
12808                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
12809                     this.taTask.delay(this.typeAheadDelay);
12810                 }
12811             }
12812         }else{
12813             this.onEmptyResults();
12814         }
12815         
12816         //this.el.focus();
12817     },
12818     // private
12819     onLoadException : function()
12820     {
12821         this.hasQuery = false;
12822         
12823         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
12824             this.loading.hide();
12825         }
12826         
12827         if(this.tickable && this.editable){
12828             return;
12829         }
12830         
12831         this.collapse();
12832         // only causes errors at present
12833         //Roo.log(this.store.reader.jsonData);
12834         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
12835             // fixme
12836             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
12837         //}
12838         
12839         
12840     },
12841     // private
12842     onTypeAhead : function(){
12843         if(this.store.getCount() > 0){
12844             var r = this.store.getAt(0);
12845             var newValue = r.data[this.displayField];
12846             var len = newValue.length;
12847             var selStart = this.getRawValue().length;
12848             
12849             if(selStart != len){
12850                 this.setRawValue(newValue);
12851                 this.selectText(selStart, newValue.length);
12852             }
12853         }
12854     },
12855
12856     // private
12857     onSelect : function(record, index){
12858         
12859         if(this.fireEvent('beforeselect', this, record, index) !== false){
12860         
12861             this.setFromData(index > -1 ? record.data : false);
12862             
12863             this.collapse();
12864             this.fireEvent('select', this, record, index);
12865         }
12866     },
12867
12868     /**
12869      * Returns the currently selected field value or empty string if no value is set.
12870      * @return {String} value The selected value
12871      */
12872     getValue : function(){
12873         
12874         if(this.multiple){
12875             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
12876         }
12877         
12878         if(this.valueField){
12879             return typeof this.value != 'undefined' ? this.value : '';
12880         }else{
12881             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
12882         }
12883     },
12884
12885     /**
12886      * Clears any text/value currently set in the field
12887      */
12888     clearValue : function(){
12889         if(this.hiddenField){
12890             this.hiddenField.dom.value = '';
12891         }
12892         this.value = '';
12893         this.setRawValue('');
12894         this.lastSelectionText = '';
12895         this.lastData = false;
12896         
12897         var close = this.closeTriggerEl();
12898         
12899         if(close){
12900             close.hide();
12901         }
12902         
12903     },
12904
12905     /**
12906      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
12907      * will be displayed in the field.  If the value does not match the data value of an existing item,
12908      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
12909      * Otherwise the field will be blank (although the value will still be set).
12910      * @param {String} value The value to match
12911      */
12912     setValue : function(v){
12913         if(this.multiple){
12914             this.syncValue();
12915             return;
12916         }
12917         
12918         var text = v;
12919         if(this.valueField){
12920             var r = this.findRecord(this.valueField, v);
12921             if(r){
12922                 text = r.data[this.displayField];
12923             }else if(this.valueNotFoundText !== undefined){
12924                 text = this.valueNotFoundText;
12925             }
12926         }
12927         this.lastSelectionText = text;
12928         if(this.hiddenField){
12929             this.hiddenField.dom.value = v;
12930         }
12931         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
12932         this.value = v;
12933         
12934         var close = this.closeTriggerEl();
12935         
12936         if(close){
12937             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
12938         }
12939     },
12940     /**
12941      * @property {Object} the last set data for the element
12942      */
12943     
12944     lastData : false,
12945     /**
12946      * Sets the value of the field based on a object which is related to the record format for the store.
12947      * @param {Object} value the value to set as. or false on reset?
12948      */
12949     setFromData : function(o){
12950         
12951         if(this.multiple){
12952             this.addItem(o);
12953             return;
12954         }
12955             
12956         var dv = ''; // display value
12957         var vv = ''; // value value..
12958         this.lastData = o;
12959         if (this.displayField) {
12960             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
12961         } else {
12962             // this is an error condition!!!
12963             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
12964         }
12965         
12966         if(this.valueField){
12967             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
12968         }
12969         
12970         var close = this.closeTriggerEl();
12971         
12972         if(close){
12973             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
12974         }
12975         
12976         if(this.hiddenField){
12977             this.hiddenField.dom.value = vv;
12978             
12979             this.lastSelectionText = dv;
12980             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12981             this.value = vv;
12982             return;
12983         }
12984         // no hidden field.. - we store the value in 'value', but still display
12985         // display field!!!!
12986         this.lastSelectionText = dv;
12987         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
12988         this.value = vv;
12989         
12990         
12991         
12992     },
12993     // private
12994     reset : function(){
12995         // overridden so that last data is reset..
12996         
12997         if(this.multiple){
12998             this.clearItem();
12999             return;
13000         }
13001         
13002         this.setValue(this.originalValue);
13003         this.clearInvalid();
13004         this.lastData = false;
13005         if (this.view) {
13006             this.view.clearSelections();
13007         }
13008     },
13009     // private
13010     findRecord : function(prop, value){
13011         var record;
13012         if(this.store.getCount() > 0){
13013             this.store.each(function(r){
13014                 if(r.data[prop] == value){
13015                     record = r;
13016                     return false;
13017                 }
13018                 return true;
13019             });
13020         }
13021         return record;
13022     },
13023     
13024     getName: function()
13025     {
13026         // returns hidden if it's set..
13027         if (!this.rendered) {return ''};
13028         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13029         
13030     },
13031     // private
13032     onViewMove : function(e, t){
13033         this.inKeyMode = false;
13034     },
13035
13036     // private
13037     onViewOver : function(e, t){
13038         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13039             return;
13040         }
13041         var item = this.view.findItemFromChild(t);
13042         
13043         if(item){
13044             var index = this.view.indexOf(item);
13045             this.select(index, false);
13046         }
13047     },
13048
13049     // private
13050     onViewClick : function(view, doFocus, el, e)
13051     {
13052         var index = this.view.getSelectedIndexes()[0];
13053         
13054         var r = this.store.getAt(index);
13055         
13056         if(this.tickable){
13057             
13058             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13059                 return;
13060             }
13061             
13062             var rm = false;
13063             var _this = this;
13064             
13065             Roo.each(this.tickItems, function(v,k){
13066                 
13067                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13068                     Roo.log(v);
13069                     _this.tickItems.splice(k, 1);
13070                     
13071                     if(typeof(e) == 'undefined' && view == false){
13072                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13073                     }
13074                     
13075                     rm = true;
13076                     return;
13077                 }
13078             });
13079             
13080             if(rm){
13081                 return;
13082             }
13083             
13084             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13085                 this.tickItems.push(r.data);
13086             }
13087             
13088             if(typeof(e) == 'undefined' && view == false){
13089                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13090             }
13091                     
13092             return;
13093         }
13094         
13095         if(r){
13096             this.onSelect(r, index);
13097         }
13098         if(doFocus !== false && !this.blockFocus){
13099             this.inputEl().focus();
13100         }
13101     },
13102
13103     // private
13104     restrictHeight : function(){
13105         //this.innerList.dom.style.height = '';
13106         //var inner = this.innerList.dom;
13107         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13108         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13109         //this.list.beginUpdate();
13110         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13111         this.list.alignTo(this.inputEl(), this.listAlign);
13112         this.list.alignTo(this.inputEl(), this.listAlign);
13113         //this.list.endUpdate();
13114     },
13115
13116     // private
13117     onEmptyResults : function(){
13118         
13119         if(this.tickable && this.editable){
13120             this.restrictHeight();
13121             return;
13122         }
13123         
13124         this.collapse();
13125     },
13126
13127     /**
13128      * Returns true if the dropdown list is expanded, else false.
13129      */
13130     isExpanded : function(){
13131         return this.list.isVisible();
13132     },
13133
13134     /**
13135      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13136      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13137      * @param {String} value The data value of the item to select
13138      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13139      * selected item if it is not currently in view (defaults to true)
13140      * @return {Boolean} True if the value matched an item in the list, else false
13141      */
13142     selectByValue : function(v, scrollIntoView){
13143         if(v !== undefined && v !== null){
13144             var r = this.findRecord(this.valueField || this.displayField, v);
13145             if(r){
13146                 this.select(this.store.indexOf(r), scrollIntoView);
13147                 return true;
13148             }
13149         }
13150         return false;
13151     },
13152
13153     /**
13154      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13155      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13156      * @param {Number} index The zero-based index of the list item to select
13157      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13158      * selected item if it is not currently in view (defaults to true)
13159      */
13160     select : function(index, scrollIntoView){
13161         this.selectedIndex = index;
13162         this.view.select(index);
13163         if(scrollIntoView !== false){
13164             var el = this.view.getNode(index);
13165             /*
13166              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13167              */
13168             if(el){
13169                 this.list.scrollChildIntoView(el, false);
13170             }
13171         }
13172     },
13173
13174     // private
13175     selectNext : function(){
13176         var ct = this.store.getCount();
13177         if(ct > 0){
13178             if(this.selectedIndex == -1){
13179                 this.select(0);
13180             }else if(this.selectedIndex < ct-1){
13181                 this.select(this.selectedIndex+1);
13182             }
13183         }
13184     },
13185
13186     // private
13187     selectPrev : function(){
13188         var ct = this.store.getCount();
13189         if(ct > 0){
13190             if(this.selectedIndex == -1){
13191                 this.select(0);
13192             }else if(this.selectedIndex != 0){
13193                 this.select(this.selectedIndex-1);
13194             }
13195         }
13196     },
13197
13198     // private
13199     onKeyUp : function(e){
13200         if(this.editable !== false && !e.isSpecialKey()){
13201             this.lastKey = e.getKey();
13202             this.dqTask.delay(this.queryDelay);
13203         }
13204     },
13205
13206     // private
13207     validateBlur : function(){
13208         return !this.list || !this.list.isVisible();   
13209     },
13210
13211     // private
13212     initQuery : function(){
13213         
13214         var v = this.getRawValue();
13215         
13216         if(this.tickable && this.editable){
13217             v = this.tickableInputEl().getValue();
13218         }
13219         
13220         this.doQuery(v);
13221     },
13222
13223     // private
13224     doForce : function(){
13225         if(this.inputEl().dom.value.length > 0){
13226             this.inputEl().dom.value =
13227                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13228              
13229         }
13230     },
13231
13232     /**
13233      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13234      * query allowing the query action to be canceled if needed.
13235      * @param {String} query The SQL query to execute
13236      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13237      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13238      * saved in the current store (defaults to false)
13239      */
13240     doQuery : function(q, forceAll){
13241         
13242         if(q === undefined || q === null){
13243             q = '';
13244         }
13245         var qe = {
13246             query: q,
13247             forceAll: forceAll,
13248             combo: this,
13249             cancel:false
13250         };
13251         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13252             return false;
13253         }
13254         q = qe.query;
13255         
13256         forceAll = qe.forceAll;
13257         if(forceAll === true || (q.length >= this.minChars)){
13258             
13259             this.hasQuery = true;
13260             
13261             if(this.lastQuery != q || this.alwaysQuery){
13262                 this.lastQuery = q;
13263                 if(this.mode == 'local'){
13264                     this.selectedIndex = -1;
13265                     if(forceAll){
13266                         this.store.clearFilter();
13267                     }else{
13268                         
13269                         if(this.specialFilter){
13270                             this.fireEvent('specialfilter', this);
13271                             this.onLoad();
13272                             return;
13273                         }
13274                         
13275                         this.store.filter(this.displayField, q);
13276                     }
13277                     
13278                     this.store.fireEvent("datachanged", this.store);
13279                     
13280                     this.onLoad();
13281                     
13282                     
13283                 }else{
13284                     
13285                     this.store.baseParams[this.queryParam] = q;
13286                     
13287                     var options = {params : this.getParams(q)};
13288                     
13289                     if(this.loadNext){
13290                         options.add = true;
13291                         options.params.start = this.page * this.pageSize;
13292                     }
13293                     
13294                     this.store.load(options);
13295                     
13296                     /*
13297                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
13298                      *  we should expand the list on onLoad
13299                      *  so command out it
13300                      */
13301 //                    this.expand();
13302                 }
13303             }else{
13304                 this.selectedIndex = -1;
13305                 this.onLoad();   
13306             }
13307         }
13308         
13309         this.loadNext = false;
13310     },
13311     
13312     // private
13313     getParams : function(q){
13314         var p = {};
13315         //p[this.queryParam] = q;
13316         
13317         if(this.pageSize){
13318             p.start = 0;
13319             p.limit = this.pageSize;
13320         }
13321         return p;
13322     },
13323
13324     /**
13325      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
13326      */
13327     collapse : function(){
13328         if(!this.isExpanded()){
13329             return;
13330         }
13331         
13332         this.list.hide();
13333         
13334         if(this.tickable){
13335             this.hasFocus = false;
13336             this.okBtn.hide();
13337             this.cancelBtn.hide();
13338             this.trigger.show();
13339             
13340             if(this.editable){
13341                 this.tickableInputEl().dom.value = '';
13342                 this.tickableInputEl().blur();
13343             }
13344             
13345         }
13346         
13347         Roo.get(document).un('mousedown', this.collapseIf, this);
13348         Roo.get(document).un('mousewheel', this.collapseIf, this);
13349         if (!this.editable) {
13350             Roo.get(document).un('keydown', this.listKeyPress, this);
13351         }
13352         this.fireEvent('collapse', this);
13353     },
13354
13355     // private
13356     collapseIf : function(e){
13357         var in_combo  = e.within(this.el);
13358         var in_list =  e.within(this.list);
13359         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
13360         
13361         if (in_combo || in_list || is_list) {
13362             //e.stopPropagation();
13363             return;
13364         }
13365         
13366         if(this.tickable){
13367             this.onTickableFooterButtonClick(e, false, false);
13368         }
13369
13370         this.collapse();
13371         
13372     },
13373
13374     /**
13375      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
13376      */
13377     expand : function(){
13378        
13379         if(this.isExpanded() || !this.hasFocus){
13380             return;
13381         }
13382         
13383         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
13384         this.list.setWidth(lw);
13385         
13386         
13387          Roo.log('expand');
13388         
13389         this.list.show();
13390         
13391         this.restrictHeight();
13392         
13393         if(this.tickable){
13394             
13395             this.tickItems = Roo.apply([], this.item);
13396             
13397             this.okBtn.show();
13398             this.cancelBtn.show();
13399             this.trigger.hide();
13400             
13401             if(this.editable){
13402                 this.tickableInputEl().focus();
13403             }
13404             
13405         }
13406         
13407         Roo.get(document).on('mousedown', this.collapseIf, this);
13408         Roo.get(document).on('mousewheel', this.collapseIf, this);
13409         if (!this.editable) {
13410             Roo.get(document).on('keydown', this.listKeyPress, this);
13411         }
13412         
13413         this.fireEvent('expand', this);
13414     },
13415
13416     // private
13417     // Implements the default empty TriggerField.onTriggerClick function
13418     onTriggerClick : function(e)
13419     {
13420         Roo.log('trigger click');
13421         
13422         if(this.disabled || !this.triggerList){
13423             return;
13424         }
13425         
13426         this.page = 0;
13427         this.loadNext = false;
13428         
13429         if(this.isExpanded()){
13430             this.collapse();
13431             if (!this.blockFocus) {
13432                 this.inputEl().focus();
13433             }
13434             
13435         }else {
13436             this.hasFocus = true;
13437             if(this.triggerAction == 'all') {
13438                 this.doQuery(this.allQuery, true);
13439             } else {
13440                 this.doQuery(this.getRawValue());
13441             }
13442             if (!this.blockFocus) {
13443                 this.inputEl().focus();
13444             }
13445         }
13446     },
13447     
13448     onTickableTriggerClick : function(e)
13449     {
13450         if(this.disabled){
13451             return;
13452         }
13453         
13454         this.page = 0;
13455         this.loadNext = false;
13456         this.hasFocus = true;
13457         
13458         if(this.triggerAction == 'all') {
13459             this.doQuery(this.allQuery, true);
13460         } else {
13461             this.doQuery(this.getRawValue());
13462         }
13463     },
13464     
13465     onSearchFieldClick : function(e)
13466     {
13467         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
13468             this.onTickableFooterButtonClick(e, false, false);
13469             return;
13470         }
13471         
13472         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
13473             return;
13474         }
13475         
13476         this.page = 0;
13477         this.loadNext = false;
13478         this.hasFocus = true;
13479         
13480         if(this.triggerAction == 'all') {
13481             this.doQuery(this.allQuery, true);
13482         } else {
13483             this.doQuery(this.getRawValue());
13484         }
13485     },
13486     
13487     listKeyPress : function(e)
13488     {
13489         //Roo.log('listkeypress');
13490         // scroll to first matching element based on key pres..
13491         if (e.isSpecialKey()) {
13492             return false;
13493         }
13494         var k = String.fromCharCode(e.getKey()).toUpperCase();
13495         //Roo.log(k);
13496         var match  = false;
13497         var csel = this.view.getSelectedNodes();
13498         var cselitem = false;
13499         if (csel.length) {
13500             var ix = this.view.indexOf(csel[0]);
13501             cselitem  = this.store.getAt(ix);
13502             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
13503                 cselitem = false;
13504             }
13505             
13506         }
13507         
13508         this.store.each(function(v) { 
13509             if (cselitem) {
13510                 // start at existing selection.
13511                 if (cselitem.id == v.id) {
13512                     cselitem = false;
13513                 }
13514                 return true;
13515             }
13516                 
13517             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
13518                 match = this.store.indexOf(v);
13519                 return false;
13520             }
13521             return true;
13522         }, this);
13523         
13524         if (match === false) {
13525             return true; // no more action?
13526         }
13527         // scroll to?
13528         this.view.select(match);
13529         var sn = Roo.get(this.view.getSelectedNodes()[0]);
13530         sn.scrollIntoView(sn.dom.parentNode, false);
13531     },
13532     
13533     onViewScroll : function(e, t){
13534         
13535         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){
13536             return;
13537         }
13538         
13539         this.hasQuery = true;
13540         
13541         this.loading = this.list.select('.loading', true).first();
13542         
13543         if(this.loading === null){
13544             this.list.createChild({
13545                 tag: 'div',
13546                 cls: 'loading roo-select2-more-results roo-select2-active',
13547                 html: 'Loading more results...'
13548             });
13549             
13550             this.loading = this.list.select('.loading', true).first();
13551             
13552             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
13553             
13554             this.loading.hide();
13555         }
13556         
13557         this.loading.show();
13558         
13559         var _combo = this;
13560         
13561         this.page++;
13562         this.loadNext = true;
13563         
13564         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
13565         
13566         return;
13567     },
13568     
13569     addItem : function(o)
13570     {   
13571         var dv = ''; // display value
13572         
13573         if (this.displayField) {
13574             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13575         } else {
13576             // this is an error condition!!!
13577             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13578         }
13579         
13580         if(!dv.length){
13581             return;
13582         }
13583         
13584         var choice = this.choices.createChild({
13585             tag: 'li',
13586             cls: 'roo-select2-search-choice',
13587             cn: [
13588                 {
13589                     tag: 'div',
13590                     html: dv
13591                 },
13592                 {
13593                     tag: 'a',
13594                     href: '#',
13595                     cls: 'roo-select2-search-choice-close',
13596                     tabindex: '-1'
13597                 }
13598             ]
13599             
13600         }, this.searchField);
13601         
13602         var close = choice.select('a.roo-select2-search-choice-close', true).first();
13603         
13604         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
13605         
13606         this.item.push(o);
13607         
13608         this.lastData = o;
13609         
13610         this.syncValue();
13611         
13612         this.inputEl().dom.value = '';
13613         
13614         this.validate();
13615     },
13616     
13617     onRemoveItem : function(e, _self, o)
13618     {
13619         e.preventDefault();
13620         
13621         this.lastItem = Roo.apply([], this.item);
13622         
13623         var index = this.item.indexOf(o.data) * 1;
13624         
13625         if( index < 0){
13626             Roo.log('not this item?!');
13627             return;
13628         }
13629         
13630         this.item.splice(index, 1);
13631         o.item.remove();
13632         
13633         this.syncValue();
13634         
13635         this.fireEvent('remove', this, e);
13636         
13637         this.validate();
13638         
13639     },
13640     
13641     syncValue : function()
13642     {
13643         if(!this.item.length){
13644             this.clearValue();
13645             return;
13646         }
13647             
13648         var value = [];
13649         var _this = this;
13650         Roo.each(this.item, function(i){
13651             if(_this.valueField){
13652                 value.push(i[_this.valueField]);
13653                 return;
13654             }
13655
13656             value.push(i);
13657         });
13658
13659         this.value = value.join(',');
13660
13661         if(this.hiddenField){
13662             this.hiddenField.dom.value = this.value;
13663         }
13664         
13665         this.store.fireEvent("datachanged", this.store);
13666     },
13667     
13668     clearItem : function()
13669     {
13670         if(!this.multiple){
13671             return;
13672         }
13673         
13674         this.item = [];
13675         
13676         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
13677            c.remove();
13678         });
13679         
13680         this.syncValue();
13681         
13682         this.validate();
13683         
13684         if(this.tickable && !Roo.isTouch){
13685             this.view.refresh();
13686         }
13687     },
13688     
13689     inputEl: function ()
13690     {
13691         if(Roo.isTouch && this.mobileTouchView){
13692             return this.el.select('input.form-control',true).first();
13693         }
13694         
13695         if(this.tickable){
13696             return this.searchField;
13697         }
13698         
13699         return this.el.select('input.form-control',true).first();
13700     },
13701     
13702     
13703     onTickableFooterButtonClick : function(e, btn, el)
13704     {
13705         e.preventDefault();
13706         
13707         this.lastItem = Roo.apply([], this.item);
13708         
13709         if(btn && btn.name == 'cancel'){
13710             this.tickItems = Roo.apply([], this.item);
13711             this.collapse();
13712             return;
13713         }
13714         
13715         this.clearItem();
13716         
13717         var _this = this;
13718         
13719         Roo.each(this.tickItems, function(o){
13720             _this.addItem(o);
13721         });
13722         
13723         this.collapse();
13724         
13725     },
13726     
13727     validate : function()
13728     {
13729         var v = this.getRawValue();
13730         
13731         if(this.multiple){
13732             v = this.getValue();
13733         }
13734         
13735         if(this.disabled || this.allowBlank || v.length){
13736             this.markValid();
13737             return true;
13738         }
13739         
13740         this.markInvalid();
13741         return false;
13742     },
13743     
13744     tickableInputEl : function()
13745     {
13746         if(!this.tickable || !this.editable){
13747             return this.inputEl();
13748         }
13749         
13750         return this.inputEl().select('.roo-select2-search-field-input', true).first();
13751     },
13752     
13753     
13754     getAutoCreateTouchView : function()
13755     {
13756         var id = Roo.id();
13757         
13758         var cfg = {
13759             cls: 'form-group' //input-group
13760         };
13761         
13762         var input =  {
13763             tag: 'input',
13764             id : id,
13765             type : this.inputType,
13766             cls : 'form-control x-combo-noedit',
13767             autocomplete: 'new-password',
13768             placeholder : this.placeholder || '',
13769             readonly : true
13770         };
13771         
13772         if (this.name) {
13773             input.name = this.name;
13774         }
13775         
13776         if (this.size) {
13777             input.cls += ' input-' + this.size;
13778         }
13779         
13780         if (this.disabled) {
13781             input.disabled = true;
13782         }
13783         
13784         var inputblock = {
13785             cls : '',
13786             cn : [
13787                 input
13788             ]
13789         };
13790         
13791         if(this.before){
13792             inputblock.cls += ' input-group';
13793             
13794             inputblock.cn.unshift({
13795                 tag :'span',
13796                 cls : 'input-group-addon',
13797                 html : this.before
13798             });
13799         }
13800         
13801         if(this.removable && !this.multiple){
13802             inputblock.cls += ' roo-removable';
13803             
13804             inputblock.cn.push({
13805                 tag: 'button',
13806                 html : 'x',
13807                 cls : 'roo-combo-removable-btn close'
13808             });
13809         }
13810
13811         if(this.hasFeedback && !this.allowBlank){
13812             
13813             inputblock.cls += ' has-feedback';
13814             
13815             inputblock.cn.push({
13816                 tag: 'span',
13817                 cls: 'glyphicon form-control-feedback'
13818             });
13819             
13820         }
13821         
13822         if (this.after) {
13823             
13824             inputblock.cls += (this.before) ? '' : ' input-group';
13825             
13826             inputblock.cn.push({
13827                 tag :'span',
13828                 cls : 'input-group-addon',
13829                 html : this.after
13830             });
13831         }
13832
13833         var box = {
13834             tag: 'div',
13835             cn: [
13836                 {
13837                     tag: 'input',
13838                     type : 'hidden',
13839                     cls: 'form-hidden-field'
13840                 },
13841                 inputblock
13842             ]
13843             
13844         };
13845         
13846         if(this.multiple){
13847             box = {
13848                 tag: 'div',
13849                 cn: [
13850                     {
13851                         tag: 'input',
13852                         type : 'hidden',
13853                         cls: 'form-hidden-field'
13854                     },
13855                     {
13856                         tag: 'ul',
13857                         cls: 'roo-select2-choices',
13858                         cn:[
13859                             {
13860                                 tag: 'li',
13861                                 cls: 'roo-select2-search-field',
13862                                 cn: [
13863
13864                                     inputblock
13865                                 ]
13866                             }
13867                         ]
13868                     }
13869                 ]
13870             }
13871         };
13872         
13873         var combobox = {
13874             cls: 'roo-select2-container input-group',
13875             cn: [
13876                 box
13877             ]
13878         };
13879         
13880 //        if(!this.multiple && this.showToggleBtn){
13881 //            
13882 //            var caret = {
13883 //                        tag: 'span',
13884 //                        cls: 'caret'
13885 //            };
13886 //            
13887 //            if (this.caret != false) {
13888 //                caret = {
13889 //                     tag: 'i',
13890 //                     cls: 'fa fa-' + this.caret
13891 //                };
13892 //                
13893 //            }
13894 //            
13895 //            combobox.cn.push({
13896 //                tag :'span',
13897 //                cls : 'input-group-addon btn dropdown-toggle',
13898 //                cn : [
13899 //                    caret,
13900 //                    {
13901 //                        tag: 'span',
13902 //                        cls: 'combobox-clear',
13903 //                        cn  : [
13904 //                            {
13905 //                                tag : 'i',
13906 //                                cls: 'icon-remove'
13907 //                            }
13908 //                        ]
13909 //                    }
13910 //                ]
13911 //
13912 //            })
13913 //        }
13914         
13915         if(this.multiple){
13916             combobox.cls += ' roo-select2-container-multi';
13917         }
13918         
13919         var align = this.labelAlign || this.parentLabelAlign();
13920         
13921         cfg.cn = combobox;
13922         
13923         if(this.fieldLabel.length && this.labelWidth){
13924             
13925             var lw = align === 'left' ? ('col-sm' + this.labelWidth) : '';
13926             var cw = align === 'left' ? ('col-sm' + (12 - this.labelWidth)) : '';
13927             
13928             cfg.cn = [
13929                 {
13930                     tag: 'label',
13931                     cls : 'control-label ' + lw,
13932                     html : this.fieldLabel
13933
13934                 },
13935                 {
13936                     cls : cw, 
13937                     cn: [
13938                         combobox
13939                     ]
13940                 }
13941             ];
13942         }
13943         
13944         var settings = this;
13945         
13946         ['xs','sm','md','lg'].map(function(size){
13947             if (settings[size]) {
13948                 cfg.cls += ' col-' + size + '-' + settings[size];
13949             }
13950         });
13951         
13952         return cfg;
13953     },
13954     
13955     initTouchView : function()
13956     {
13957         this.renderTouchView();
13958         
13959         this.touchViewEl.on('scroll', function(){
13960             this.el.dom.scrollTop = 0;
13961         }, this);
13962         
13963         this.originalValue = this.getValue();
13964         
13965         this.inputEl().on("touch", this.showTouchView, this);
13966         
13967         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
13968         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
13969         
13970         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
13971         
13972         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
13973         this.store.on('load', this.onTouchViewLoad, this);
13974         this.store.on('loadexception', this.onTouchViewLoadException, this);
13975         
13976         if(this.hiddenName){
13977             
13978             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13979             
13980             this.hiddenField.dom.value =
13981                 this.hiddenValue !== undefined ? this.hiddenValue :
13982                 this.value !== undefined ? this.value : '';
13983         
13984             this.el.dom.removeAttribute('name');
13985             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13986         }
13987         
13988         if(this.multiple){
13989             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13990             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13991         }
13992         
13993         if(this.removable && !this.multiple){
13994             var close = this.closeTriggerEl();
13995             if(close){
13996                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13997                 close.on('click', this.removeBtnClick, this, close);
13998             }
13999         }
14000         /*
14001          * fix the bug in Safari iOS8
14002          */
14003         this.inputEl().on("focus", function(e){
14004             document.activeElement.blur();
14005         }, this);
14006         
14007         return;
14008         
14009         
14010     },
14011     
14012     renderTouchView : function()
14013     {
14014         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14015         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14016         
14017         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14018         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14019         
14020         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14021         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14022         this.touchViewBodyEl.setStyle('overflow', 'auto');
14023         
14024         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14025         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14026         
14027         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14028         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14029         
14030     },
14031     
14032     showTouchView : function()
14033     {
14034         if(this.disabled){
14035             return;
14036         }
14037         
14038         this.touchViewHeaderEl.hide();
14039
14040         if(this.fieldLabel.length){
14041             this.touchViewHeaderEl.dom.innerHTML = this.fieldLabel;
14042             this.touchViewHeaderEl.show();
14043         }
14044
14045         this.touchViewEl.show();
14046
14047         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14048         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14049                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14050
14051         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14052
14053         if(this.fieldLabel.length){
14054             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14055         }
14056         
14057         this.touchViewBodyEl.setHeight(bodyHeight);
14058
14059         if(this.animate){
14060             var _this = this;
14061             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14062         }else{
14063             this.touchViewEl.addClass('in');
14064         }
14065
14066         this.doTouchViewQuery();
14067         
14068     },
14069     
14070     hideTouchView : function()
14071     {
14072         this.touchViewEl.removeClass('in');
14073
14074         if(this.animate){
14075             var _this = this;
14076             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14077         }else{
14078             this.touchViewEl.setStyle('display', 'none');
14079         }
14080         
14081     },
14082     
14083     setTouchViewValue : function()
14084     {
14085         if(this.multiple){
14086             this.clearItem();
14087         
14088             var _this = this;
14089
14090             Roo.each(this.tickItems, function(o){
14091                 this.addItem(o);
14092             }, this);
14093         }
14094         
14095         this.hideTouchView();
14096     },
14097     
14098     doTouchViewQuery : function()
14099     {
14100         var qe = {
14101             query: '',
14102             forceAll: true,
14103             combo: this,
14104             cancel:false
14105         };
14106         
14107         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14108             return false;
14109         }
14110         
14111         if(!this.alwaysQuery || this.mode == 'local'){
14112             this.onTouchViewLoad();
14113             return;
14114         }
14115         
14116         this.store.load();
14117     },
14118     
14119     onTouchViewBeforeLoad : function(combo,opts)
14120     {
14121         return;
14122     },
14123
14124     // private
14125     onTouchViewLoad : function()
14126     {
14127         if(this.store.getCount() < 1){
14128             this.onTouchViewEmptyResults();
14129             return;
14130         }
14131         
14132         this.clearTouchView();
14133         
14134         var rawValue = this.getRawValue();
14135         
14136         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14137         
14138         this.tickItems = [];
14139         
14140         this.store.data.each(function(d, rowIndex){
14141             var row = this.touchViewListGroup.createChild(template);
14142             
14143             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14144                 row.addClass(d.data.cls);
14145             }
14146             
14147             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14148                 var cfg = {
14149                     data : d.data,
14150                     html : d.data[this.displayField]
14151                 };
14152                 
14153                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14154                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
14155                 }
14156             }
14157             row.removeClass('selected');
14158             if(!this.multiple && this.valueField &&
14159                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
14160             {
14161                 // radio buttons..
14162                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14163                 row.addClass('selected');
14164             }
14165             
14166             if(this.multiple && this.valueField &&
14167                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
14168             {
14169                 
14170                 // checkboxes...
14171                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14172                 this.tickItems.push(d.data);
14173             }
14174             
14175             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
14176             
14177         }, this);
14178         
14179         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
14180         
14181         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14182
14183         if(this.fieldLabel.length){
14184             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14185         }
14186
14187         var listHeight = this.touchViewListGroup.getHeight();
14188         
14189         var _this = this;
14190         
14191         if(firstChecked && listHeight > bodyHeight){
14192             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
14193         }
14194         
14195     },
14196     
14197     onTouchViewLoadException : function()
14198     {
14199         this.hideTouchView();
14200     },
14201     
14202     onTouchViewEmptyResults : function()
14203     {
14204         this.clearTouchView();
14205         
14206         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
14207         
14208         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
14209         
14210     },
14211     
14212     clearTouchView : function()
14213     {
14214         this.touchViewListGroup.dom.innerHTML = '';
14215     },
14216     
14217     onTouchViewClick : function(e, el, o)
14218     {
14219         e.preventDefault();
14220         
14221         var row = o.row;
14222         var rowIndex = o.rowIndex;
14223         
14224         var r = this.store.getAt(rowIndex);
14225         
14226         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
14227             
14228             if(!this.multiple){
14229                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
14230                     c.dom.removeAttribute('checked');
14231                 }, this);
14232
14233                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14234
14235                 this.setFromData(r.data);
14236
14237                 var close = this.closeTriggerEl();
14238
14239                 if(close){
14240                     close.show();
14241                 }
14242
14243                 this.hideTouchView();
14244
14245                 this.fireEvent('select', this, r, rowIndex);
14246
14247                 return;
14248             }
14249
14250             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
14251                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
14252                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
14253                 return;
14254             }
14255
14256             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
14257             this.addItem(r.data);
14258             this.tickItems.push(r.data);
14259         }
14260     }
14261     
14262
14263     /** 
14264     * @cfg {Boolean} grow 
14265     * @hide 
14266     */
14267     /** 
14268     * @cfg {Number} growMin 
14269     * @hide 
14270     */
14271     /** 
14272     * @cfg {Number} growMax 
14273     * @hide 
14274     */
14275     /**
14276      * @hide
14277      * @method autoSize
14278      */
14279 });
14280
14281 Roo.apply(Roo.bootstrap.ComboBox,  {
14282     
14283     header : {
14284         tag: 'div',
14285         cls: 'modal-header',
14286         cn: [
14287             {
14288                 tag: 'h4',
14289                 cls: 'modal-title'
14290             }
14291         ]
14292     },
14293     
14294     body : {
14295         tag: 'div',
14296         cls: 'modal-body',
14297         cn: [
14298             {
14299                 tag: 'ul',
14300                 cls: 'list-group'
14301             }
14302         ]
14303     },
14304     
14305     listItemRadio : {
14306         tag: 'li',
14307         cls: 'list-group-item',
14308         cn: [
14309             {
14310                 tag: 'span',
14311                 cls: 'roo-combobox-list-group-item-value'
14312             },
14313             {
14314                 tag: 'div',
14315                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
14316                 cn: [
14317                     {
14318                         tag: 'input',
14319                         type: 'radio'
14320                     },
14321                     {
14322                         tag: 'label'
14323                     }
14324                 ]
14325             }
14326         ]
14327     },
14328     
14329     listItemCheckbox : {
14330         tag: 'li',
14331         cls: 'list-group-item',
14332         cn: [
14333             {
14334                 tag: 'span',
14335                 cls: 'roo-combobox-list-group-item-value'
14336             },
14337             {
14338                 tag: 'div',
14339                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
14340                 cn: [
14341                     {
14342                         tag: 'input',
14343                         type: 'checkbox'
14344                     },
14345                     {
14346                         tag: 'label'
14347                     }
14348                 ]
14349             }
14350         ]
14351     },
14352     
14353     emptyResult : {
14354         tag: 'div',
14355         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
14356     },
14357     
14358     footer : {
14359         tag: 'div',
14360         cls: 'modal-footer',
14361         cn: [
14362             {
14363                 tag: 'div',
14364                 cls: 'row',
14365                 cn: [
14366                     {
14367                         tag: 'div',
14368                         cls: 'col-xs-6 text-left',
14369                         cn: {
14370                             tag: 'button',
14371                             cls: 'btn btn-danger roo-touch-view-cancel',
14372                             html: 'Cancel'
14373                         }
14374                     },
14375                     {
14376                         tag: 'div',
14377                         cls: 'col-xs-6 text-right',
14378                         cn: {
14379                             tag: 'button',
14380                             cls: 'btn btn-success roo-touch-view-ok',
14381                             html: 'OK'
14382                         }
14383                     }
14384                 ]
14385             }
14386         ]
14387         
14388     }
14389 });
14390
14391 Roo.apply(Roo.bootstrap.ComboBox,  {
14392     
14393     touchViewTemplate : {
14394         tag: 'div',
14395         cls: 'modal fade roo-combobox-touch-view',
14396         cn: [
14397             {
14398                 tag: 'div',
14399                 cls: 'modal-dialog',
14400                 style : 'position:fixed', // we have to fix position....
14401                 cn: [
14402                     {
14403                         tag: 'div',
14404                         cls: 'modal-content',
14405                         cn: [
14406                             Roo.bootstrap.ComboBox.header,
14407                             Roo.bootstrap.ComboBox.body,
14408                             Roo.bootstrap.ComboBox.footer
14409                         ]
14410                     }
14411                 ]
14412             }
14413         ]
14414     }
14415 });/*
14416  * Based on:
14417  * Ext JS Library 1.1.1
14418  * Copyright(c) 2006-2007, Ext JS, LLC.
14419  *
14420  * Originally Released Under LGPL - original licence link has changed is not relivant.
14421  *
14422  * Fork - LGPL
14423  * <script type="text/javascript">
14424  */
14425
14426 /**
14427  * @class Roo.View
14428  * @extends Roo.util.Observable
14429  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
14430  * This class also supports single and multi selection modes. <br>
14431  * Create a data model bound view:
14432  <pre><code>
14433  var store = new Roo.data.Store(...);
14434
14435  var view = new Roo.View({
14436     el : "my-element",
14437     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
14438  
14439     singleSelect: true,
14440     selectedClass: "ydataview-selected",
14441     store: store
14442  });
14443
14444  // listen for node click?
14445  view.on("click", function(vw, index, node, e){
14446  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
14447  });
14448
14449  // load XML data
14450  dataModel.load("foobar.xml");
14451  </code></pre>
14452  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
14453  * <br><br>
14454  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
14455  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
14456  * 
14457  * Note: old style constructor is still suported (container, template, config)
14458  * 
14459  * @constructor
14460  * Create a new View
14461  * @param {Object} config The config object
14462  * 
14463  */
14464 Roo.View = function(config, depreciated_tpl, depreciated_config){
14465     
14466     this.parent = false;
14467     
14468     if (typeof(depreciated_tpl) == 'undefined') {
14469         // new way.. - universal constructor.
14470         Roo.apply(this, config);
14471         this.el  = Roo.get(this.el);
14472     } else {
14473         // old format..
14474         this.el  = Roo.get(config);
14475         this.tpl = depreciated_tpl;
14476         Roo.apply(this, depreciated_config);
14477     }
14478     this.wrapEl  = this.el.wrap().wrap();
14479     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
14480     
14481     
14482     if(typeof(this.tpl) == "string"){
14483         this.tpl = new Roo.Template(this.tpl);
14484     } else {
14485         // support xtype ctors..
14486         this.tpl = new Roo.factory(this.tpl, Roo);
14487     }
14488     
14489     
14490     this.tpl.compile();
14491     
14492     /** @private */
14493     this.addEvents({
14494         /**
14495          * @event beforeclick
14496          * Fires before a click is processed. Returns false to cancel the default action.
14497          * @param {Roo.View} this
14498          * @param {Number} index The index of the target node
14499          * @param {HTMLElement} node The target node
14500          * @param {Roo.EventObject} e The raw event object
14501          */
14502             "beforeclick" : true,
14503         /**
14504          * @event click
14505          * Fires when a template node is clicked.
14506          * @param {Roo.View} this
14507          * @param {Number} index The index of the target node
14508          * @param {HTMLElement} node The target node
14509          * @param {Roo.EventObject} e The raw event object
14510          */
14511             "click" : true,
14512         /**
14513          * @event dblclick
14514          * Fires when a template node is double clicked.
14515          * @param {Roo.View} this
14516          * @param {Number} index The index of the target node
14517          * @param {HTMLElement} node The target node
14518          * @param {Roo.EventObject} e The raw event object
14519          */
14520             "dblclick" : true,
14521         /**
14522          * @event contextmenu
14523          * Fires when a template node is right clicked.
14524          * @param {Roo.View} this
14525          * @param {Number} index The index of the target node
14526          * @param {HTMLElement} node The target node
14527          * @param {Roo.EventObject} e The raw event object
14528          */
14529             "contextmenu" : true,
14530         /**
14531          * @event selectionchange
14532          * Fires when the selected nodes change.
14533          * @param {Roo.View} this
14534          * @param {Array} selections Array of the selected nodes
14535          */
14536             "selectionchange" : true,
14537     
14538         /**
14539          * @event beforeselect
14540          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
14541          * @param {Roo.View} this
14542          * @param {HTMLElement} node The node to be selected
14543          * @param {Array} selections Array of currently selected nodes
14544          */
14545             "beforeselect" : true,
14546         /**
14547          * @event preparedata
14548          * Fires on every row to render, to allow you to change the data.
14549          * @param {Roo.View} this
14550          * @param {Object} data to be rendered (change this)
14551          */
14552           "preparedata" : true
14553           
14554           
14555         });
14556
14557
14558
14559     this.el.on({
14560         "click": this.onClick,
14561         "dblclick": this.onDblClick,
14562         "contextmenu": this.onContextMenu,
14563         scope:this
14564     });
14565
14566     this.selections = [];
14567     this.nodes = [];
14568     this.cmp = new Roo.CompositeElementLite([]);
14569     if(this.store){
14570         this.store = Roo.factory(this.store, Roo.data);
14571         this.setStore(this.store, true);
14572     }
14573     
14574     if ( this.footer && this.footer.xtype) {
14575            
14576          var fctr = this.wrapEl.appendChild(document.createElement("div"));
14577         
14578         this.footer.dataSource = this.store;
14579         this.footer.container = fctr;
14580         this.footer = Roo.factory(this.footer, Roo);
14581         fctr.insertFirst(this.el);
14582         
14583         // this is a bit insane - as the paging toolbar seems to detach the el..
14584 //        dom.parentNode.parentNode.parentNode
14585          // they get detached?
14586     }
14587     
14588     
14589     Roo.View.superclass.constructor.call(this);
14590     
14591     
14592 };
14593
14594 Roo.extend(Roo.View, Roo.util.Observable, {
14595     
14596      /**
14597      * @cfg {Roo.data.Store} store Data store to load data from.
14598      */
14599     store : false,
14600     
14601     /**
14602      * @cfg {String|Roo.Element} el The container element.
14603      */
14604     el : '',
14605     
14606     /**
14607      * @cfg {String|Roo.Template} tpl The template used by this View 
14608      */
14609     tpl : false,
14610     /**
14611      * @cfg {String} dataName the named area of the template to use as the data area
14612      *                          Works with domtemplates roo-name="name"
14613      */
14614     dataName: false,
14615     /**
14616      * @cfg {String} selectedClass The css class to add to selected nodes
14617      */
14618     selectedClass : "x-view-selected",
14619      /**
14620      * @cfg {String} emptyText The empty text to show when nothing is loaded.
14621      */
14622     emptyText : "",
14623     
14624     /**
14625      * @cfg {String} text to display on mask (default Loading)
14626      */
14627     mask : false,
14628     /**
14629      * @cfg {Boolean} multiSelect Allow multiple selection
14630      */
14631     multiSelect : false,
14632     /**
14633      * @cfg {Boolean} singleSelect Allow single selection
14634      */
14635     singleSelect:  false,
14636     
14637     /**
14638      * @cfg {Boolean} toggleSelect - selecting 
14639      */
14640     toggleSelect : false,
14641     
14642     /**
14643      * @cfg {Boolean} tickable - selecting 
14644      */
14645     tickable : false,
14646     
14647     /**
14648      * Returns the element this view is bound to.
14649      * @return {Roo.Element}
14650      */
14651     getEl : function(){
14652         return this.wrapEl;
14653     },
14654     
14655     
14656
14657     /**
14658      * Refreshes the view. - called by datachanged on the store. - do not call directly.
14659      */
14660     refresh : function(){
14661         //Roo.log('refresh');
14662         var t = this.tpl;
14663         
14664         // if we are using something like 'domtemplate', then
14665         // the what gets used is:
14666         // t.applySubtemplate(NAME, data, wrapping data..)
14667         // the outer template then get' applied with
14668         //     the store 'extra data'
14669         // and the body get's added to the
14670         //      roo-name="data" node?
14671         //      <span class='roo-tpl-{name}'></span> ?????
14672         
14673         
14674         
14675         this.clearSelections();
14676         this.el.update("");
14677         var html = [];
14678         var records = this.store.getRange();
14679         if(records.length < 1) {
14680             
14681             // is this valid??  = should it render a template??
14682             
14683             this.el.update(this.emptyText);
14684             return;
14685         }
14686         var el = this.el;
14687         if (this.dataName) {
14688             this.el.update(t.apply(this.store.meta)); //????
14689             el = this.el.child('.roo-tpl-' + this.dataName);
14690         }
14691         
14692         for(var i = 0, len = records.length; i < len; i++){
14693             var data = this.prepareData(records[i].data, i, records[i]);
14694             this.fireEvent("preparedata", this, data, i, records[i]);
14695             
14696             var d = Roo.apply({}, data);
14697             
14698             if(this.tickable){
14699                 Roo.apply(d, {'roo-id' : Roo.id()});
14700                 
14701                 var _this = this;
14702             
14703                 Roo.each(this.parent.item, function(item){
14704                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
14705                         return;
14706                     }
14707                     Roo.apply(d, {'roo-data-checked' : 'checked'});
14708                 });
14709             }
14710             
14711             html[html.length] = Roo.util.Format.trim(
14712                 this.dataName ?
14713                     t.applySubtemplate(this.dataName, d, this.store.meta) :
14714                     t.apply(d)
14715             );
14716         }
14717         
14718         
14719         
14720         el.update(html.join(""));
14721         this.nodes = el.dom.childNodes;
14722         this.updateIndexes(0);
14723     },
14724     
14725
14726     /**
14727      * Function to override to reformat the data that is sent to
14728      * the template for each node.
14729      * DEPRICATED - use the preparedata event handler.
14730      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
14731      * a JSON object for an UpdateManager bound view).
14732      */
14733     prepareData : function(data, index, record)
14734     {
14735         this.fireEvent("preparedata", this, data, index, record);
14736         return data;
14737     },
14738
14739     onUpdate : function(ds, record){
14740         // Roo.log('on update');   
14741         this.clearSelections();
14742         var index = this.store.indexOf(record);
14743         var n = this.nodes[index];
14744         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
14745         n.parentNode.removeChild(n);
14746         this.updateIndexes(index, index);
14747     },
14748
14749     
14750     
14751 // --------- FIXME     
14752     onAdd : function(ds, records, index)
14753     {
14754         //Roo.log(['on Add', ds, records, index] );        
14755         this.clearSelections();
14756         if(this.nodes.length == 0){
14757             this.refresh();
14758             return;
14759         }
14760         var n = this.nodes[index];
14761         for(var i = 0, len = records.length; i < len; i++){
14762             var d = this.prepareData(records[i].data, i, records[i]);
14763             if(n){
14764                 this.tpl.insertBefore(n, d);
14765             }else{
14766                 
14767                 this.tpl.append(this.el, d);
14768             }
14769         }
14770         this.updateIndexes(index);
14771     },
14772
14773     onRemove : function(ds, record, index){
14774        // Roo.log('onRemove');
14775         this.clearSelections();
14776         var el = this.dataName  ?
14777             this.el.child('.roo-tpl-' + this.dataName) :
14778             this.el; 
14779         
14780         el.dom.removeChild(this.nodes[index]);
14781         this.updateIndexes(index);
14782     },
14783
14784     /**
14785      * Refresh an individual node.
14786      * @param {Number} index
14787      */
14788     refreshNode : function(index){
14789         this.onUpdate(this.store, this.store.getAt(index));
14790     },
14791
14792     updateIndexes : function(startIndex, endIndex){
14793         var ns = this.nodes;
14794         startIndex = startIndex || 0;
14795         endIndex = endIndex || ns.length - 1;
14796         for(var i = startIndex; i <= endIndex; i++){
14797             ns[i].nodeIndex = i;
14798         }
14799     },
14800
14801     /**
14802      * Changes the data store this view uses and refresh the view.
14803      * @param {Store} store
14804      */
14805     setStore : function(store, initial){
14806         if(!initial && this.store){
14807             this.store.un("datachanged", this.refresh);
14808             this.store.un("add", this.onAdd);
14809             this.store.un("remove", this.onRemove);
14810             this.store.un("update", this.onUpdate);
14811             this.store.un("clear", this.refresh);
14812             this.store.un("beforeload", this.onBeforeLoad);
14813             this.store.un("load", this.onLoad);
14814             this.store.un("loadexception", this.onLoad);
14815         }
14816         if(store){
14817           
14818             store.on("datachanged", this.refresh, this);
14819             store.on("add", this.onAdd, this);
14820             store.on("remove", this.onRemove, this);
14821             store.on("update", this.onUpdate, this);
14822             store.on("clear", this.refresh, this);
14823             store.on("beforeload", this.onBeforeLoad, this);
14824             store.on("load", this.onLoad, this);
14825             store.on("loadexception", this.onLoad, this);
14826         }
14827         
14828         if(store){
14829             this.refresh();
14830         }
14831     },
14832     /**
14833      * onbeforeLoad - masks the loading area.
14834      *
14835      */
14836     onBeforeLoad : function(store,opts)
14837     {
14838          //Roo.log('onBeforeLoad');   
14839         if (!opts.add) {
14840             this.el.update("");
14841         }
14842         this.el.mask(this.mask ? this.mask : "Loading" ); 
14843     },
14844     onLoad : function ()
14845     {
14846         this.el.unmask();
14847     },
14848     
14849
14850     /**
14851      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
14852      * @param {HTMLElement} node
14853      * @return {HTMLElement} The template node
14854      */
14855     findItemFromChild : function(node){
14856         var el = this.dataName  ?
14857             this.el.child('.roo-tpl-' + this.dataName,true) :
14858             this.el.dom; 
14859         
14860         if(!node || node.parentNode == el){
14861                     return node;
14862             }
14863             var p = node.parentNode;
14864             while(p && p != el){
14865             if(p.parentNode == el){
14866                 return p;
14867             }
14868             p = p.parentNode;
14869         }
14870             return null;
14871     },
14872
14873     /** @ignore */
14874     onClick : function(e){
14875         var item = this.findItemFromChild(e.getTarget());
14876         if(item){
14877             var index = this.indexOf(item);
14878             if(this.onItemClick(item, index, e) !== false){
14879                 this.fireEvent("click", this, index, item, e);
14880             }
14881         }else{
14882             this.clearSelections();
14883         }
14884     },
14885
14886     /** @ignore */
14887     onContextMenu : function(e){
14888         var item = this.findItemFromChild(e.getTarget());
14889         if(item){
14890             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
14891         }
14892     },
14893
14894     /** @ignore */
14895     onDblClick : function(e){
14896         var item = this.findItemFromChild(e.getTarget());
14897         if(item){
14898             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
14899         }
14900     },
14901
14902     onItemClick : function(item, index, e)
14903     {
14904         if(this.fireEvent("beforeclick", this, index, item, e) === false){
14905             return false;
14906         }
14907         if (this.toggleSelect) {
14908             var m = this.isSelected(item) ? 'unselect' : 'select';
14909             //Roo.log(m);
14910             var _t = this;
14911             _t[m](item, true, false);
14912             return true;
14913         }
14914         if(this.multiSelect || this.singleSelect){
14915             if(this.multiSelect && e.shiftKey && this.lastSelection){
14916                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
14917             }else{
14918                 this.select(item, this.multiSelect && e.ctrlKey);
14919                 this.lastSelection = item;
14920             }
14921             
14922             if(!this.tickable){
14923                 e.preventDefault();
14924             }
14925             
14926         }
14927         return true;
14928     },
14929
14930     /**
14931      * Get the number of selected nodes.
14932      * @return {Number}
14933      */
14934     getSelectionCount : function(){
14935         return this.selections.length;
14936     },
14937
14938     /**
14939      * Get the currently selected nodes.
14940      * @return {Array} An array of HTMLElements
14941      */
14942     getSelectedNodes : function(){
14943         return this.selections;
14944     },
14945
14946     /**
14947      * Get the indexes of the selected nodes.
14948      * @return {Array}
14949      */
14950     getSelectedIndexes : function(){
14951         var indexes = [], s = this.selections;
14952         for(var i = 0, len = s.length; i < len; i++){
14953             indexes.push(s[i].nodeIndex);
14954         }
14955         return indexes;
14956     },
14957
14958     /**
14959      * Clear all selections
14960      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
14961      */
14962     clearSelections : function(suppressEvent){
14963         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
14964             this.cmp.elements = this.selections;
14965             this.cmp.removeClass(this.selectedClass);
14966             this.selections = [];
14967             if(!suppressEvent){
14968                 this.fireEvent("selectionchange", this, this.selections);
14969             }
14970         }
14971     },
14972
14973     /**
14974      * Returns true if the passed node is selected
14975      * @param {HTMLElement/Number} node The node or node index
14976      * @return {Boolean}
14977      */
14978     isSelected : function(node){
14979         var s = this.selections;
14980         if(s.length < 1){
14981             return false;
14982         }
14983         node = this.getNode(node);
14984         return s.indexOf(node) !== -1;
14985     },
14986
14987     /**
14988      * Selects nodes.
14989      * @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
14990      * @param {Boolean} keepExisting (optional) true to keep existing selections
14991      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
14992      */
14993     select : function(nodeInfo, keepExisting, suppressEvent){
14994         if(nodeInfo instanceof Array){
14995             if(!keepExisting){
14996                 this.clearSelections(true);
14997             }
14998             for(var i = 0, len = nodeInfo.length; i < len; i++){
14999                 this.select(nodeInfo[i], true, true);
15000             }
15001             return;
15002         } 
15003         var node = this.getNode(nodeInfo);
15004         if(!node || this.isSelected(node)){
15005             return; // already selected.
15006         }
15007         if(!keepExisting){
15008             this.clearSelections(true);
15009         }
15010         
15011         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15012             Roo.fly(node).addClass(this.selectedClass);
15013             this.selections.push(node);
15014             if(!suppressEvent){
15015                 this.fireEvent("selectionchange", this, this.selections);
15016             }
15017         }
15018         
15019         
15020     },
15021       /**
15022      * Unselects nodes.
15023      * @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
15024      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
15025      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15026      */
15027     unselect : function(nodeInfo, keepExisting, suppressEvent)
15028     {
15029         if(nodeInfo instanceof Array){
15030             Roo.each(this.selections, function(s) {
15031                 this.unselect(s, nodeInfo);
15032             }, this);
15033             return;
15034         }
15035         var node = this.getNode(nodeInfo);
15036         if(!node || !this.isSelected(node)){
15037             //Roo.log("not selected");
15038             return; // not selected.
15039         }
15040         // fireevent???
15041         var ns = [];
15042         Roo.each(this.selections, function(s) {
15043             if (s == node ) {
15044                 Roo.fly(node).removeClass(this.selectedClass);
15045
15046                 return;
15047             }
15048             ns.push(s);
15049         },this);
15050         
15051         this.selections= ns;
15052         this.fireEvent("selectionchange", this, this.selections);
15053     },
15054
15055     /**
15056      * Gets a template node.
15057      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15058      * @return {HTMLElement} The node or null if it wasn't found
15059      */
15060     getNode : function(nodeInfo){
15061         if(typeof nodeInfo == "string"){
15062             return document.getElementById(nodeInfo);
15063         }else if(typeof nodeInfo == "number"){
15064             return this.nodes[nodeInfo];
15065         }
15066         return nodeInfo;
15067     },
15068
15069     /**
15070      * Gets a range template nodes.
15071      * @param {Number} startIndex
15072      * @param {Number} endIndex
15073      * @return {Array} An array of nodes
15074      */
15075     getNodes : function(start, end){
15076         var ns = this.nodes;
15077         start = start || 0;
15078         end = typeof end == "undefined" ? ns.length - 1 : end;
15079         var nodes = [];
15080         if(start <= end){
15081             for(var i = start; i <= end; i++){
15082                 nodes.push(ns[i]);
15083             }
15084         } else{
15085             for(var i = start; i >= end; i--){
15086                 nodes.push(ns[i]);
15087             }
15088         }
15089         return nodes;
15090     },
15091
15092     /**
15093      * Finds the index of the passed node
15094      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
15095      * @return {Number} The index of the node or -1
15096      */
15097     indexOf : function(node){
15098         node = this.getNode(node);
15099         if(typeof node.nodeIndex == "number"){
15100             return node.nodeIndex;
15101         }
15102         var ns = this.nodes;
15103         for(var i = 0, len = ns.length; i < len; i++){
15104             if(ns[i] == node){
15105                 return i;
15106             }
15107         }
15108         return -1;
15109     }
15110 });
15111 /*
15112  * - LGPL
15113  *
15114  * based on jquery fullcalendar
15115  * 
15116  */
15117
15118 Roo.bootstrap = Roo.bootstrap || {};
15119 /**
15120  * @class Roo.bootstrap.Calendar
15121  * @extends Roo.bootstrap.Component
15122  * Bootstrap Calendar class
15123  * @cfg {Boolean} loadMask (true|false) default false
15124  * @cfg {Object} header generate the user specific header of the calendar, default false
15125
15126  * @constructor
15127  * Create a new Container
15128  * @param {Object} config The config object
15129  */
15130
15131
15132
15133 Roo.bootstrap.Calendar = function(config){
15134     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
15135      this.addEvents({
15136         /**
15137              * @event select
15138              * Fires when a date is selected
15139              * @param {DatePicker} this
15140              * @param {Date} date The selected date
15141              */
15142         'select': true,
15143         /**
15144              * @event monthchange
15145              * Fires when the displayed month changes 
15146              * @param {DatePicker} this
15147              * @param {Date} date The selected month
15148              */
15149         'monthchange': true,
15150         /**
15151              * @event evententer
15152              * Fires when mouse over an event
15153              * @param {Calendar} this
15154              * @param {event} Event
15155              */
15156         'evententer': true,
15157         /**
15158              * @event eventleave
15159              * Fires when the mouse leaves an
15160              * @param {Calendar} this
15161              * @param {event}
15162              */
15163         'eventleave': true,
15164         /**
15165              * @event eventclick
15166              * Fires when the mouse click an
15167              * @param {Calendar} this
15168              * @param {event}
15169              */
15170         'eventclick': true
15171         
15172     });
15173
15174 };
15175
15176 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
15177     
15178      /**
15179      * @cfg {Number} startDay
15180      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
15181      */
15182     startDay : 0,
15183     
15184     loadMask : false,
15185     
15186     header : false,
15187       
15188     getAutoCreate : function(){
15189         
15190         
15191         var fc_button = function(name, corner, style, content ) {
15192             return Roo.apply({},{
15193                 tag : 'span',
15194                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
15195                          (corner.length ?
15196                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
15197                             ''
15198                         ),
15199                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
15200                 unselectable: 'on'
15201             });
15202         };
15203         
15204         var header = {};
15205         
15206         if(!this.header){
15207             header = {
15208                 tag : 'table',
15209                 cls : 'fc-header',
15210                 style : 'width:100%',
15211                 cn : [
15212                     {
15213                         tag: 'tr',
15214                         cn : [
15215                             {
15216                                 tag : 'td',
15217                                 cls : 'fc-header-left',
15218                                 cn : [
15219                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
15220                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
15221                                     { tag: 'span', cls: 'fc-header-space' },
15222                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
15223
15224
15225                                 ]
15226                             },
15227
15228                             {
15229                                 tag : 'td',
15230                                 cls : 'fc-header-center',
15231                                 cn : [
15232                                     {
15233                                         tag: 'span',
15234                                         cls: 'fc-header-title',
15235                                         cn : {
15236                                             tag: 'H2',
15237                                             html : 'month / year'
15238                                         }
15239                                     }
15240
15241                                 ]
15242                             },
15243                             {
15244                                 tag : 'td',
15245                                 cls : 'fc-header-right',
15246                                 cn : [
15247                               /*      fc_button('month', 'left', '', 'month' ),
15248                                     fc_button('week', '', '', 'week' ),
15249                                     fc_button('day', 'right', '', 'day' )
15250                                 */    
15251
15252                                 ]
15253                             }
15254
15255                         ]
15256                     }
15257                 ]
15258             };
15259         }
15260         
15261         header = this.header;
15262         
15263        
15264         var cal_heads = function() {
15265             var ret = [];
15266             // fixme - handle this.
15267             
15268             for (var i =0; i < Date.dayNames.length; i++) {
15269                 var d = Date.dayNames[i];
15270                 ret.push({
15271                     tag: 'th',
15272                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
15273                     html : d.substring(0,3)
15274                 });
15275                 
15276             }
15277             ret[0].cls += ' fc-first';
15278             ret[6].cls += ' fc-last';
15279             return ret;
15280         };
15281         var cal_cell = function(n) {
15282             return  {
15283                 tag: 'td',
15284                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
15285                 cn : [
15286                     {
15287                         cn : [
15288                             {
15289                                 cls: 'fc-day-number',
15290                                 html: 'D'
15291                             },
15292                             {
15293                                 cls: 'fc-day-content',
15294                              
15295                                 cn : [
15296                                      {
15297                                         style: 'position: relative;' // height: 17px;
15298                                     }
15299                                 ]
15300                             }
15301                             
15302                             
15303                         ]
15304                     }
15305                 ]
15306                 
15307             }
15308         };
15309         var cal_rows = function() {
15310             
15311             var ret = [];
15312             for (var r = 0; r < 6; r++) {
15313                 var row= {
15314                     tag : 'tr',
15315                     cls : 'fc-week',
15316                     cn : []
15317                 };
15318                 
15319                 for (var i =0; i < Date.dayNames.length; i++) {
15320                     var d = Date.dayNames[i];
15321                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
15322
15323                 }
15324                 row.cn[0].cls+=' fc-first';
15325                 row.cn[0].cn[0].style = 'min-height:90px';
15326                 row.cn[6].cls+=' fc-last';
15327                 ret.push(row);
15328                 
15329             }
15330             ret[0].cls += ' fc-first';
15331             ret[4].cls += ' fc-prev-last';
15332             ret[5].cls += ' fc-last';
15333             return ret;
15334             
15335         };
15336         
15337         var cal_table = {
15338             tag: 'table',
15339             cls: 'fc-border-separate',
15340             style : 'width:100%',
15341             cellspacing  : 0,
15342             cn : [
15343                 { 
15344                     tag: 'thead',
15345                     cn : [
15346                         { 
15347                             tag: 'tr',
15348                             cls : 'fc-first fc-last',
15349                             cn : cal_heads()
15350                         }
15351                     ]
15352                 },
15353                 { 
15354                     tag: 'tbody',
15355                     cn : cal_rows()
15356                 }
15357                   
15358             ]
15359         };
15360          
15361          var cfg = {
15362             cls : 'fc fc-ltr',
15363             cn : [
15364                 header,
15365                 {
15366                     cls : 'fc-content',
15367                     style : "position: relative;",
15368                     cn : [
15369                         {
15370                             cls : 'fc-view fc-view-month fc-grid',
15371                             style : 'position: relative',
15372                             unselectable : 'on',
15373                             cn : [
15374                                 {
15375                                     cls : 'fc-event-container',
15376                                     style : 'position:absolute;z-index:8;top:0;left:0;'
15377                                 },
15378                                 cal_table
15379                             ]
15380                         }
15381                     ]
15382     
15383                 }
15384            ] 
15385             
15386         };
15387         
15388          
15389         
15390         return cfg;
15391     },
15392     
15393     
15394     initEvents : function()
15395     {
15396         if(!this.store){
15397             throw "can not find store for calendar";
15398         }
15399         
15400         var mark = {
15401             tag: "div",
15402             cls:"x-dlg-mask",
15403             style: "text-align:center",
15404             cn: [
15405                 {
15406                     tag: "div",
15407                     style: "background-color:white;width:50%;margin:250 auto",
15408                     cn: [
15409                         {
15410                             tag: "img",
15411                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
15412                         },
15413                         {
15414                             tag: "span",
15415                             html: "Loading"
15416                         }
15417                         
15418                     ]
15419                 }
15420             ]
15421         };
15422         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
15423         
15424         var size = this.el.select('.fc-content', true).first().getSize();
15425         this.maskEl.setSize(size.width, size.height);
15426         this.maskEl.enableDisplayMode("block");
15427         if(!this.loadMask){
15428             this.maskEl.hide();
15429         }
15430         
15431         this.store = Roo.factory(this.store, Roo.data);
15432         this.store.on('load', this.onLoad, this);
15433         this.store.on('beforeload', this.onBeforeLoad, this);
15434         
15435         this.resize();
15436         
15437         this.cells = this.el.select('.fc-day',true);
15438         //Roo.log(this.cells);
15439         this.textNodes = this.el.query('.fc-day-number');
15440         this.cells.addClassOnOver('fc-state-hover');
15441         
15442         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
15443         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
15444         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
15445         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
15446         
15447         this.on('monthchange', this.onMonthChange, this);
15448         
15449         this.update(new Date().clearTime());
15450     },
15451     
15452     resize : function() {
15453         var sz  = this.el.getSize();
15454         
15455         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
15456         this.el.select('.fc-day-content div',true).setHeight(34);
15457     },
15458     
15459     
15460     // private
15461     showPrevMonth : function(e){
15462         this.update(this.activeDate.add("mo", -1));
15463     },
15464     showToday : function(e){
15465         this.update(new Date().clearTime());
15466     },
15467     // private
15468     showNextMonth : function(e){
15469         this.update(this.activeDate.add("mo", 1));
15470     },
15471
15472     // private
15473     showPrevYear : function(){
15474         this.update(this.activeDate.add("y", -1));
15475     },
15476
15477     // private
15478     showNextYear : function(){
15479         this.update(this.activeDate.add("y", 1));
15480     },
15481
15482     
15483    // private
15484     update : function(date)
15485     {
15486         var vd = this.activeDate;
15487         this.activeDate = date;
15488 //        if(vd && this.el){
15489 //            var t = date.getTime();
15490 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
15491 //                Roo.log('using add remove');
15492 //                
15493 //                this.fireEvent('monthchange', this, date);
15494 //                
15495 //                this.cells.removeClass("fc-state-highlight");
15496 //                this.cells.each(function(c){
15497 //                   if(c.dateValue == t){
15498 //                       c.addClass("fc-state-highlight");
15499 //                       setTimeout(function(){
15500 //                            try{c.dom.firstChild.focus();}catch(e){}
15501 //                       }, 50);
15502 //                       return false;
15503 //                   }
15504 //                   return true;
15505 //                });
15506 //                return;
15507 //            }
15508 //        }
15509         
15510         var days = date.getDaysInMonth();
15511         
15512         var firstOfMonth = date.getFirstDateOfMonth();
15513         var startingPos = firstOfMonth.getDay()-this.startDay;
15514         
15515         if(startingPos < this.startDay){
15516             startingPos += 7;
15517         }
15518         
15519         var pm = date.add(Date.MONTH, -1);
15520         var prevStart = pm.getDaysInMonth()-startingPos;
15521 //        
15522         this.cells = this.el.select('.fc-day',true);
15523         this.textNodes = this.el.query('.fc-day-number');
15524         this.cells.addClassOnOver('fc-state-hover');
15525         
15526         var cells = this.cells.elements;
15527         var textEls = this.textNodes;
15528         
15529         Roo.each(cells, function(cell){
15530             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
15531         });
15532         
15533         days += startingPos;
15534
15535         // convert everything to numbers so it's fast
15536         var day = 86400000;
15537         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
15538         //Roo.log(d);
15539         //Roo.log(pm);
15540         //Roo.log(prevStart);
15541         
15542         var today = new Date().clearTime().getTime();
15543         var sel = date.clearTime().getTime();
15544         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
15545         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
15546         var ddMatch = this.disabledDatesRE;
15547         var ddText = this.disabledDatesText;
15548         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
15549         var ddaysText = this.disabledDaysText;
15550         var format = this.format;
15551         
15552         var setCellClass = function(cal, cell){
15553             cell.row = 0;
15554             cell.events = [];
15555             cell.more = [];
15556             //Roo.log('set Cell Class');
15557             cell.title = "";
15558             var t = d.getTime();
15559             
15560             //Roo.log(d);
15561             
15562             cell.dateValue = t;
15563             if(t == today){
15564                 cell.className += " fc-today";
15565                 cell.className += " fc-state-highlight";
15566                 cell.title = cal.todayText;
15567             }
15568             if(t == sel){
15569                 // disable highlight in other month..
15570                 //cell.className += " fc-state-highlight";
15571                 
15572             }
15573             // disabling
15574             if(t < min) {
15575                 cell.className = " fc-state-disabled";
15576                 cell.title = cal.minText;
15577                 return;
15578             }
15579             if(t > max) {
15580                 cell.className = " fc-state-disabled";
15581                 cell.title = cal.maxText;
15582                 return;
15583             }
15584             if(ddays){
15585                 if(ddays.indexOf(d.getDay()) != -1){
15586                     cell.title = ddaysText;
15587                     cell.className = " fc-state-disabled";
15588                 }
15589             }
15590             if(ddMatch && format){
15591                 var fvalue = d.dateFormat(format);
15592                 if(ddMatch.test(fvalue)){
15593                     cell.title = ddText.replace("%0", fvalue);
15594                     cell.className = " fc-state-disabled";
15595                 }
15596             }
15597             
15598             if (!cell.initialClassName) {
15599                 cell.initialClassName = cell.dom.className;
15600             }
15601             
15602             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
15603         };
15604
15605         var i = 0;
15606         
15607         for(; i < startingPos; i++) {
15608             textEls[i].innerHTML = (++prevStart);
15609             d.setDate(d.getDate()+1);
15610             
15611             cells[i].className = "fc-past fc-other-month";
15612             setCellClass(this, cells[i]);
15613         }
15614         
15615         var intDay = 0;
15616         
15617         for(; i < days; i++){
15618             intDay = i - startingPos + 1;
15619             textEls[i].innerHTML = (intDay);
15620             d.setDate(d.getDate()+1);
15621             
15622             cells[i].className = ''; // "x-date-active";
15623             setCellClass(this, cells[i]);
15624         }
15625         var extraDays = 0;
15626         
15627         for(; i < 42; i++) {
15628             textEls[i].innerHTML = (++extraDays);
15629             d.setDate(d.getDate()+1);
15630             
15631             cells[i].className = "fc-future fc-other-month";
15632             setCellClass(this, cells[i]);
15633         }
15634         
15635         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
15636         
15637         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
15638         
15639         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
15640         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
15641         
15642         if(totalRows != 6){
15643             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
15644             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
15645         }
15646         
15647         this.fireEvent('monthchange', this, date);
15648         
15649         
15650         /*
15651         if(!this.internalRender){
15652             var main = this.el.dom.firstChild;
15653             var w = main.offsetWidth;
15654             this.el.setWidth(w + this.el.getBorderWidth("lr"));
15655             Roo.fly(main).setWidth(w);
15656             this.internalRender = true;
15657             // opera does not respect the auto grow header center column
15658             // then, after it gets a width opera refuses to recalculate
15659             // without a second pass
15660             if(Roo.isOpera && !this.secondPass){
15661                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
15662                 this.secondPass = true;
15663                 this.update.defer(10, this, [date]);
15664             }
15665         }
15666         */
15667         
15668     },
15669     
15670     findCell : function(dt) {
15671         dt = dt.clearTime().getTime();
15672         var ret = false;
15673         this.cells.each(function(c){
15674             //Roo.log("check " +c.dateValue + '?=' + dt);
15675             if(c.dateValue == dt){
15676                 ret = c;
15677                 return false;
15678             }
15679             return true;
15680         });
15681         
15682         return ret;
15683     },
15684     
15685     findCells : function(ev) {
15686         var s = ev.start.clone().clearTime().getTime();
15687        // Roo.log(s);
15688         var e= ev.end.clone().clearTime().getTime();
15689        // Roo.log(e);
15690         var ret = [];
15691         this.cells.each(function(c){
15692              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
15693             
15694             if(c.dateValue > e){
15695                 return ;
15696             }
15697             if(c.dateValue < s){
15698                 return ;
15699             }
15700             ret.push(c);
15701         });
15702         
15703         return ret;    
15704     },
15705     
15706 //    findBestRow: function(cells)
15707 //    {
15708 //        var ret = 0;
15709 //        
15710 //        for (var i =0 ; i < cells.length;i++) {
15711 //            ret  = Math.max(cells[i].rows || 0,ret);
15712 //        }
15713 //        return ret;
15714 //        
15715 //    },
15716     
15717     
15718     addItem : function(ev)
15719     {
15720         // look for vertical location slot in
15721         var cells = this.findCells(ev);
15722         
15723 //        ev.row = this.findBestRow(cells);
15724         
15725         // work out the location.
15726         
15727         var crow = false;
15728         var rows = [];
15729         for(var i =0; i < cells.length; i++) {
15730             
15731             cells[i].row = cells[0].row;
15732             
15733             if(i == 0){
15734                 cells[i].row = cells[i].row + 1;
15735             }
15736             
15737             if (!crow) {
15738                 crow = {
15739                     start : cells[i],
15740                     end :  cells[i]
15741                 };
15742                 continue;
15743             }
15744             if (crow.start.getY() == cells[i].getY()) {
15745                 // on same row.
15746                 crow.end = cells[i];
15747                 continue;
15748             }
15749             // different row.
15750             rows.push(crow);
15751             crow = {
15752                 start: cells[i],
15753                 end : cells[i]
15754             };
15755             
15756         }
15757         
15758         rows.push(crow);
15759         ev.els = [];
15760         ev.rows = rows;
15761         ev.cells = cells;
15762         
15763         cells[0].events.push(ev);
15764         
15765         this.calevents.push(ev);
15766     },
15767     
15768     clearEvents: function() {
15769         
15770         if(!this.calevents){
15771             return;
15772         }
15773         
15774         Roo.each(this.cells.elements, function(c){
15775             c.row = 0;
15776             c.events = [];
15777             c.more = [];
15778         });
15779         
15780         Roo.each(this.calevents, function(e) {
15781             Roo.each(e.els, function(el) {
15782                 el.un('mouseenter' ,this.onEventEnter, this);
15783                 el.un('mouseleave' ,this.onEventLeave, this);
15784                 el.remove();
15785             },this);
15786         },this);
15787         
15788         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
15789             e.remove();
15790         });
15791         
15792     },
15793     
15794     renderEvents: function()
15795     {   
15796         var _this = this;
15797         
15798         this.cells.each(function(c) {
15799             
15800             if(c.row < 5){
15801                 return;
15802             }
15803             
15804             var ev = c.events;
15805             
15806             var r = 4;
15807             if(c.row != c.events.length){
15808                 r = 4 - (4 - (c.row - c.events.length));
15809             }
15810             
15811             c.events = ev.slice(0, r);
15812             c.more = ev.slice(r);
15813             
15814             if(c.more.length && c.more.length == 1){
15815                 c.events.push(c.more.pop());
15816             }
15817             
15818             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
15819             
15820         });
15821             
15822         this.cells.each(function(c) {
15823             
15824             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
15825             
15826             
15827             for (var e = 0; e < c.events.length; e++){
15828                 var ev = c.events[e];
15829                 var rows = ev.rows;
15830                 
15831                 for(var i = 0; i < rows.length; i++) {
15832                 
15833                     // how many rows should it span..
15834
15835                     var  cfg = {
15836                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
15837                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
15838
15839                         unselectable : "on",
15840                         cn : [
15841                             {
15842                                 cls: 'fc-event-inner',
15843                                 cn : [
15844     //                                {
15845     //                                  tag:'span',
15846     //                                  cls: 'fc-event-time',
15847     //                                  html : cells.length > 1 ? '' : ev.time
15848     //                                },
15849                                     {
15850                                       tag:'span',
15851                                       cls: 'fc-event-title',
15852                                       html : String.format('{0}', ev.title)
15853                                     }
15854
15855
15856                                 ]
15857                             },
15858                             {
15859                                 cls: 'ui-resizable-handle ui-resizable-e',
15860                                 html : '&nbsp;&nbsp;&nbsp'
15861                             }
15862
15863                         ]
15864                     };
15865
15866                     if (i == 0) {
15867                         cfg.cls += ' fc-event-start';
15868                     }
15869                     if ((i+1) == rows.length) {
15870                         cfg.cls += ' fc-event-end';
15871                     }
15872
15873                     var ctr = _this.el.select('.fc-event-container',true).first();
15874                     var cg = ctr.createChild(cfg);
15875
15876                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
15877                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
15878
15879                     var r = (c.more.length) ? 1 : 0;
15880                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
15881                     cg.setWidth(ebox.right - sbox.x -2);
15882
15883                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
15884                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
15885                     cg.on('click', _this.onEventClick, _this, ev);
15886
15887                     ev.els.push(cg);
15888                     
15889                 }
15890                 
15891             }
15892             
15893             
15894             if(c.more.length){
15895                 var  cfg = {
15896                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
15897                     style : 'position: absolute',
15898                     unselectable : "on",
15899                     cn : [
15900                         {
15901                             cls: 'fc-event-inner',
15902                             cn : [
15903                                 {
15904                                   tag:'span',
15905                                   cls: 'fc-event-title',
15906                                   html : 'More'
15907                                 }
15908
15909
15910                             ]
15911                         },
15912                         {
15913                             cls: 'ui-resizable-handle ui-resizable-e',
15914                             html : '&nbsp;&nbsp;&nbsp'
15915                         }
15916
15917                     ]
15918                 };
15919
15920                 var ctr = _this.el.select('.fc-event-container',true).first();
15921                 var cg = ctr.createChild(cfg);
15922
15923                 var sbox = c.select('.fc-day-content',true).first().getBox();
15924                 var ebox = c.select('.fc-day-content',true).first().getBox();
15925                 //Roo.log(cg);
15926                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
15927                 cg.setWidth(ebox.right - sbox.x -2);
15928
15929                 cg.on('click', _this.onMoreEventClick, _this, c.more);
15930                 
15931             }
15932             
15933         });
15934         
15935         
15936         
15937     },
15938     
15939     onEventEnter: function (e, el,event,d) {
15940         this.fireEvent('evententer', this, el, event);
15941     },
15942     
15943     onEventLeave: function (e, el,event,d) {
15944         this.fireEvent('eventleave', this, el, event);
15945     },
15946     
15947     onEventClick: function (e, el,event,d) {
15948         this.fireEvent('eventclick', this, el, event);
15949     },
15950     
15951     onMonthChange: function () {
15952         this.store.load();
15953     },
15954     
15955     onMoreEventClick: function(e, el, more)
15956     {
15957         var _this = this;
15958         
15959         this.calpopover.placement = 'right';
15960         this.calpopover.setTitle('More');
15961         
15962         this.calpopover.setContent('');
15963         
15964         var ctr = this.calpopover.el.select('.popover-content', true).first();
15965         
15966         Roo.each(more, function(m){
15967             var cfg = {
15968                 cls : 'fc-event-hori fc-event-draggable',
15969                 html : m.title
15970             };
15971             var cg = ctr.createChild(cfg);
15972             
15973             cg.on('click', _this.onEventClick, _this, m);
15974         });
15975         
15976         this.calpopover.show(el);
15977         
15978         
15979     },
15980     
15981     onLoad: function () 
15982     {   
15983         this.calevents = [];
15984         var cal = this;
15985         
15986         if(this.store.getCount() > 0){
15987             this.store.data.each(function(d){
15988                cal.addItem({
15989                     id : d.data.id,
15990                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
15991                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
15992                     time : d.data.start_time,
15993                     title : d.data.title,
15994                     description : d.data.description,
15995                     venue : d.data.venue
15996                 });
15997             });
15998         }
15999         
16000         this.renderEvents();
16001         
16002         if(this.calevents.length && this.loadMask){
16003             this.maskEl.hide();
16004         }
16005     },
16006     
16007     onBeforeLoad: function()
16008     {
16009         this.clearEvents();
16010         if(this.loadMask){
16011             this.maskEl.show();
16012         }
16013     }
16014 });
16015
16016  
16017  /*
16018  * - LGPL
16019  *
16020  * element
16021  * 
16022  */
16023
16024 /**
16025  * @class Roo.bootstrap.Popover
16026  * @extends Roo.bootstrap.Component
16027  * Bootstrap Popover class
16028  * @cfg {String} html contents of the popover   (or false to use children..)
16029  * @cfg {String} title of popover (or false to hide)
16030  * @cfg {String} placement how it is placed
16031  * @cfg {String} trigger click || hover (or false to trigger manually)
16032  * @cfg {String} over what (parent or false to trigger manually.)
16033  * @cfg {Number} delay - delay before showing
16034  
16035  * @constructor
16036  * Create a new Popover
16037  * @param {Object} config The config object
16038  */
16039
16040 Roo.bootstrap.Popover = function(config){
16041     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
16042     
16043     this.addEvents({
16044         // raw events
16045          /**
16046          * @event show
16047          * After the popover show
16048          * 
16049          * @param {Roo.bootstrap.Popover} this
16050          */
16051         "show" : true,
16052         /**
16053          * @event hide
16054          * After the popover hide
16055          * 
16056          * @param {Roo.bootstrap.Popover} this
16057          */
16058         "hide" : true
16059     });
16060 };
16061
16062 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
16063     
16064     title: 'Fill in a title',
16065     html: false,
16066     
16067     placement : 'right',
16068     trigger : 'hover', // hover
16069     
16070     delay : 0,
16071     
16072     over: 'parent',
16073     
16074     can_build_overlaid : false,
16075     
16076     getChildContainer : function()
16077     {
16078         return this.el.select('.popover-content',true).first();
16079     },
16080     
16081     getAutoCreate : function(){
16082          
16083         var cfg = {
16084            cls : 'popover roo-dynamic',
16085            style: 'display:block',
16086            cn : [
16087                 {
16088                     cls : 'arrow'
16089                 },
16090                 {
16091                     cls : 'popover-inner',
16092                     cn : [
16093                         {
16094                             tag: 'h3',
16095                             cls: 'popover-title',
16096                             html : this.title
16097                         },
16098                         {
16099                             cls : 'popover-content',
16100                             html : this.html
16101                         }
16102                     ]
16103                     
16104                 }
16105            ]
16106         };
16107         
16108         return cfg;
16109     },
16110     setTitle: function(str)
16111     {
16112         this.title = str;
16113         this.el.select('.popover-title',true).first().dom.innerHTML = str;
16114     },
16115     setContent: function(str)
16116     {
16117         this.html = str;
16118         this.el.select('.popover-content',true).first().dom.innerHTML = str;
16119     },
16120     // as it get's added to the bottom of the page.
16121     onRender : function(ct, position)
16122     {
16123         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
16124         if(!this.el){
16125             var cfg = Roo.apply({},  this.getAutoCreate());
16126             cfg.id = Roo.id();
16127             
16128             if (this.cls) {
16129                 cfg.cls += ' ' + this.cls;
16130             }
16131             if (this.style) {
16132                 cfg.style = this.style;
16133             }
16134             //Roo.log("adding to ");
16135             this.el = Roo.get(document.body).createChild(cfg, position);
16136 //            Roo.log(this.el);
16137         }
16138         this.initEvents();
16139     },
16140     
16141     initEvents : function()
16142     {
16143         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
16144         this.el.enableDisplayMode('block');
16145         this.el.hide();
16146         if (this.over === false) {
16147             return; 
16148         }
16149         if (this.triggers === false) {
16150             return;
16151         }
16152         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16153         var triggers = this.trigger ? this.trigger.split(' ') : [];
16154         Roo.each(triggers, function(trigger) {
16155         
16156             if (trigger == 'click') {
16157                 on_el.on('click', this.toggle, this);
16158             } else if (trigger != 'manual') {
16159                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
16160                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
16161       
16162                 on_el.on(eventIn  ,this.enter, this);
16163                 on_el.on(eventOut, this.leave, this);
16164             }
16165         }, this);
16166         
16167     },
16168     
16169     
16170     // private
16171     timeout : null,
16172     hoverState : null,
16173     
16174     toggle : function () {
16175         this.hoverState == 'in' ? this.leave() : this.enter();
16176     },
16177     
16178     enter : function () {
16179         
16180         clearTimeout(this.timeout);
16181     
16182         this.hoverState = 'in';
16183     
16184         if (!this.delay || !this.delay.show) {
16185             this.show();
16186             return;
16187         }
16188         var _t = this;
16189         this.timeout = setTimeout(function () {
16190             if (_t.hoverState == 'in') {
16191                 _t.show();
16192             }
16193         }, this.delay.show)
16194     },
16195     
16196     leave : function() {
16197         clearTimeout(this.timeout);
16198     
16199         this.hoverState = 'out';
16200     
16201         if (!this.delay || !this.delay.hide) {
16202             this.hide();
16203             return;
16204         }
16205         var _t = this;
16206         this.timeout = setTimeout(function () {
16207             if (_t.hoverState == 'out') {
16208                 _t.hide();
16209             }
16210         }, this.delay.hide)
16211     },
16212     
16213     show : function (on_el)
16214     {
16215         if (!on_el) {
16216             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
16217         }
16218         
16219         // set content.
16220         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
16221         if (this.html !== false) {
16222             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
16223         }
16224         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
16225         if (!this.title.length) {
16226             this.el.select('.popover-title',true).hide();
16227         }
16228         
16229         var placement = typeof this.placement == 'function' ?
16230             this.placement.call(this, this.el, on_el) :
16231             this.placement;
16232             
16233         var autoToken = /\s?auto?\s?/i;
16234         var autoPlace = autoToken.test(placement);
16235         if (autoPlace) {
16236             placement = placement.replace(autoToken, '') || 'top';
16237         }
16238         
16239         //this.el.detach()
16240         //this.el.setXY([0,0]);
16241         this.el.show();
16242         this.el.dom.style.display='block';
16243         this.el.addClass(placement);
16244         
16245         //this.el.appendTo(on_el);
16246         
16247         var p = this.getPosition();
16248         var box = this.el.getBox();
16249         
16250         if (autoPlace) {
16251             // fixme..
16252         }
16253         var align = Roo.bootstrap.Popover.alignment[placement];
16254         this.el.alignTo(on_el, align[0],align[1]);
16255         //var arrow = this.el.select('.arrow',true).first();
16256         //arrow.set(align[2], 
16257         
16258         this.el.addClass('in');
16259         
16260         
16261         if (this.el.hasClass('fade')) {
16262             // fade it?
16263         }
16264         
16265         this.hoverState = 'in';
16266         
16267         this.fireEvent('show', this);
16268         
16269     },
16270     hide : function()
16271     {
16272         this.el.setXY([0,0]);
16273         this.el.removeClass('in');
16274         this.el.hide();
16275         this.hoverState = null;
16276         
16277         this.fireEvent('hide', this);
16278     }
16279     
16280 });
16281
16282 Roo.bootstrap.Popover.alignment = {
16283     'left' : ['r-l', [-10,0], 'right'],
16284     'right' : ['l-r', [10,0], 'left'],
16285     'bottom' : ['t-b', [0,10], 'top'],
16286     'top' : [ 'b-t', [0,-10], 'bottom']
16287 };
16288
16289  /*
16290  * - LGPL
16291  *
16292  * Progress
16293  * 
16294  */
16295
16296 /**
16297  * @class Roo.bootstrap.Progress
16298  * @extends Roo.bootstrap.Component
16299  * Bootstrap Progress class
16300  * @cfg {Boolean} striped striped of the progress bar
16301  * @cfg {Boolean} active animated of the progress bar
16302  * 
16303  * 
16304  * @constructor
16305  * Create a new Progress
16306  * @param {Object} config The config object
16307  */
16308
16309 Roo.bootstrap.Progress = function(config){
16310     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
16311 };
16312
16313 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
16314     
16315     striped : false,
16316     active: false,
16317     
16318     getAutoCreate : function(){
16319         var cfg = {
16320             tag: 'div',
16321             cls: 'progress'
16322         };
16323         
16324         
16325         if(this.striped){
16326             cfg.cls += ' progress-striped';
16327         }
16328       
16329         if(this.active){
16330             cfg.cls += ' active';
16331         }
16332         
16333         
16334         return cfg;
16335     }
16336    
16337 });
16338
16339  
16340
16341  /*
16342  * - LGPL
16343  *
16344  * ProgressBar
16345  * 
16346  */
16347
16348 /**
16349  * @class Roo.bootstrap.ProgressBar
16350  * @extends Roo.bootstrap.Component
16351  * Bootstrap ProgressBar class
16352  * @cfg {Number} aria_valuenow aria-value now
16353  * @cfg {Number} aria_valuemin aria-value min
16354  * @cfg {Number} aria_valuemax aria-value max
16355  * @cfg {String} label label for the progress bar
16356  * @cfg {String} panel (success | info | warning | danger )
16357  * @cfg {String} role role of the progress bar
16358  * @cfg {String} sr_only text
16359  * 
16360  * 
16361  * @constructor
16362  * Create a new ProgressBar
16363  * @param {Object} config The config object
16364  */
16365
16366 Roo.bootstrap.ProgressBar = function(config){
16367     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
16368 };
16369
16370 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
16371     
16372     aria_valuenow : 0,
16373     aria_valuemin : 0,
16374     aria_valuemax : 100,
16375     label : false,
16376     panel : false,
16377     role : false,
16378     sr_only: false,
16379     
16380     getAutoCreate : function()
16381     {
16382         
16383         var cfg = {
16384             tag: 'div',
16385             cls: 'progress-bar',
16386             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
16387         };
16388         
16389         if(this.sr_only){
16390             cfg.cn = {
16391                 tag: 'span',
16392                 cls: 'sr-only',
16393                 html: this.sr_only
16394             }
16395         }
16396         
16397         if(this.role){
16398             cfg.role = this.role;
16399         }
16400         
16401         if(this.aria_valuenow){
16402             cfg['aria-valuenow'] = this.aria_valuenow;
16403         }
16404         
16405         if(this.aria_valuemin){
16406             cfg['aria-valuemin'] = this.aria_valuemin;
16407         }
16408         
16409         if(this.aria_valuemax){
16410             cfg['aria-valuemax'] = this.aria_valuemax;
16411         }
16412         
16413         if(this.label && !this.sr_only){
16414             cfg.html = this.label;
16415         }
16416         
16417         if(this.panel){
16418             cfg.cls += ' progress-bar-' + this.panel;
16419         }
16420         
16421         return cfg;
16422     },
16423     
16424     update : function(aria_valuenow)
16425     {
16426         this.aria_valuenow = aria_valuenow;
16427         
16428         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
16429     }
16430    
16431 });
16432
16433  
16434
16435  /*
16436  * - LGPL
16437  *
16438  * column
16439  * 
16440  */
16441
16442 /**
16443  * @class Roo.bootstrap.TabGroup
16444  * @extends Roo.bootstrap.Column
16445  * Bootstrap Column class
16446  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
16447  * @cfg {Boolean} carousel true to make the group behave like a carousel
16448  * @cfg {Boolean} bullets show bullets for the panels
16449  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
16450  * @cfg {Boolean} slideOnTouch (true|false) slide on touch .. default false
16451  * @cfg {Number} timer auto slide timer .. default 0 millisecond
16452  * 
16453  * @constructor
16454  * Create a new TabGroup
16455  * @param {Object} config The config object
16456  */
16457
16458 Roo.bootstrap.TabGroup = function(config){
16459     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
16460     if (!this.navId) {
16461         this.navId = Roo.id();
16462     }
16463     this.tabs = [];
16464     Roo.bootstrap.TabGroup.register(this);
16465     
16466 };
16467
16468 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
16469     
16470     carousel : false,
16471     transition : false,
16472     bullets : 0,
16473     timer : 0,
16474     autoslide : false,
16475     slideFn : false,
16476     slideOnTouch : false,
16477     
16478     getAutoCreate : function()
16479     {
16480         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
16481         
16482         cfg.cls += ' tab-content';
16483         
16484         if (this.carousel) {
16485             cfg.cls += ' carousel slide';
16486             
16487             cfg.cn = [{
16488                cls : 'carousel-inner'
16489             }];
16490         
16491             if(this.bullets  && !Roo.isTouch){
16492                 
16493                 var bullets = {
16494                     cls : 'carousel-bullets',
16495                     cn : []
16496                 };
16497                
16498                 if(this.bullets_cls){
16499                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
16500                 }
16501                  /*
16502                 for (var i = 0; i < this.bullets; i++){
16503                     bullets.cn.push({
16504                         cls : 'bullet bullet-' + i
16505                     });
16506                 }
16507                 */
16508                 bullets.cn.push({
16509                     cls : 'clear'
16510                 });
16511                 
16512                 cfg.cn[0].cn = bullets;
16513             }
16514         }
16515         
16516         return cfg;
16517     },
16518     
16519     initEvents:  function()
16520     {
16521         if(Roo.isTouch && this.slideOnTouch){
16522             this.el.on("touchstart", this.onTouchStart, this);
16523         }
16524         
16525         if(this.autoslide){
16526             var _this = this;
16527             
16528             this.slideFn = window.setInterval(function() {
16529                 _this.showPanelNext();
16530             }, this.timer);
16531         }
16532         
16533     },
16534     
16535     onTouchStart : function(e, el, o)
16536     {
16537         if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
16538             return;
16539         }
16540         
16541         this.showPanelNext();
16542     },
16543     
16544     getChildContainer : function()
16545     {
16546         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
16547     },
16548     
16549     /**
16550     * register a Navigation item
16551     * @param {Roo.bootstrap.NavItem} the navitem to add
16552     */
16553     register : function(item)
16554     {
16555         this.tabs.push( item);
16556         item.navId = this.navId; // not really needed..
16557         this.addBullet();
16558     
16559     },
16560     
16561     getActivePanel : function()
16562     {
16563         var r = false;
16564         Roo.each(this.tabs, function(t) {
16565             if (t.active) {
16566                 r = t;
16567                 return false;
16568             }
16569             return null;
16570         });
16571         return r;
16572         
16573     },
16574     getPanelByName : function(n)
16575     {
16576         var r = false;
16577         Roo.each(this.tabs, function(t) {
16578             if (t.tabId == n) {
16579                 r = t;
16580                 return false;
16581             }
16582             return null;
16583         });
16584         return r;
16585     },
16586     indexOfPanel : function(p)
16587     {
16588         var r = false;
16589         Roo.each(this.tabs, function(t,i) {
16590             if (t.tabId == p.tabId) {
16591                 r = i;
16592                 return false;
16593             }
16594             return null;
16595         });
16596         return r;
16597     },
16598     /**
16599      * show a specific panel
16600      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
16601      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
16602      */
16603     showPanel : function (pan)
16604     {
16605         if(this.transition || typeof(pan) == 'undefined'){
16606             Roo.log("waiting for the transitionend");
16607             return;
16608         }
16609         
16610         if (typeof(pan) == 'number') {
16611             pan = this.tabs[pan];
16612         }
16613         
16614         if (typeof(pan) == 'string') {
16615             pan = this.getPanelByName(pan);
16616         }
16617         
16618         var cur = this.getActivePanel();
16619         
16620         if(!pan || !cur){
16621             Roo.log('pan or acitve pan is undefined');
16622             return false;
16623         }
16624         
16625         if (pan.tabId == this.getActivePanel().tabId) {
16626             return true;
16627         }
16628         
16629         if (false === cur.fireEvent('beforedeactivate')) {
16630             return false;
16631         }
16632         
16633         if(this.bullets > 0 && !Roo.isTouch){
16634             this.setActiveBullet(this.indexOfPanel(pan));
16635         }
16636         
16637         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
16638             
16639             this.transition = true;
16640             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
16641             var lr = dir == 'next' ? 'left' : 'right';
16642             pan.el.addClass(dir); // or prev
16643             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
16644             cur.el.addClass(lr); // or right
16645             pan.el.addClass(lr);
16646             
16647             var _this = this;
16648             cur.el.on('transitionend', function() {
16649                 Roo.log("trans end?");
16650                 
16651                 pan.el.removeClass([lr,dir]);
16652                 pan.setActive(true);
16653                 
16654                 cur.el.removeClass([lr]);
16655                 cur.setActive(false);
16656                 
16657                 _this.transition = false;
16658                 
16659             }, this, { single:  true } );
16660             
16661             return true;
16662         }
16663         
16664         cur.setActive(false);
16665         pan.setActive(true);
16666         
16667         return true;
16668         
16669     },
16670     showPanelNext : function()
16671     {
16672         var i = this.indexOfPanel(this.getActivePanel());
16673         
16674         if (i >= this.tabs.length - 1 && !this.autoslide) {
16675             return;
16676         }
16677         
16678         if (i >= this.tabs.length - 1 && this.autoslide) {
16679             i = -1;
16680         }
16681         
16682         this.showPanel(this.tabs[i+1]);
16683     },
16684     
16685     showPanelPrev : function()
16686     {
16687         var i = this.indexOfPanel(this.getActivePanel());
16688         
16689         if (i  < 1 && !this.autoslide) {
16690             return;
16691         }
16692         
16693         if (i < 1 && this.autoslide) {
16694             i = this.tabs.length;
16695         }
16696         
16697         this.showPanel(this.tabs[i-1]);
16698     },
16699     
16700     
16701     addBullet: function()
16702     {
16703         if(!this.bullets || Roo.isTouch){
16704             return;
16705         }
16706         var ctr = this.el.select('.carousel-bullets',true).first();
16707         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
16708         var bullet = ctr.createChild({
16709             cls : 'bullet bullet-' + i
16710         },ctr.dom.lastChild);
16711         
16712         
16713         var _this = this;
16714         
16715         bullet.on('click', (function(e, el, o, ii, t){
16716
16717             e.preventDefault();
16718
16719             this.showPanel(ii);
16720
16721             if(this.autoslide && this.slideFn){
16722                 clearInterval(this.slideFn);
16723                 this.slideFn = window.setInterval(function() {
16724                     _this.showPanelNext();
16725                 }, this.timer);
16726             }
16727
16728         }).createDelegate(this, [i, bullet], true));
16729                 
16730         
16731     },
16732      
16733     setActiveBullet : function(i)
16734     {
16735         if(Roo.isTouch){
16736             return;
16737         }
16738         
16739         Roo.each(this.el.select('.bullet', true).elements, function(el){
16740             el.removeClass('selected');
16741         });
16742
16743         var bullet = this.el.select('.bullet-' + i, true).first();
16744         
16745         if(!bullet){
16746             return;
16747         }
16748         
16749         bullet.addClass('selected');
16750     }
16751     
16752     
16753   
16754 });
16755
16756  
16757
16758  
16759  
16760 Roo.apply(Roo.bootstrap.TabGroup, {
16761     
16762     groups: {},
16763      /**
16764     * register a Navigation Group
16765     * @param {Roo.bootstrap.NavGroup} the navgroup to add
16766     */
16767     register : function(navgrp)
16768     {
16769         this.groups[navgrp.navId] = navgrp;
16770         
16771     },
16772     /**
16773     * fetch a Navigation Group based on the navigation ID
16774     * if one does not exist , it will get created.
16775     * @param {string} the navgroup to add
16776     * @returns {Roo.bootstrap.NavGroup} the navgroup 
16777     */
16778     get: function(navId) {
16779         if (typeof(this.groups[navId]) == 'undefined') {
16780             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
16781         }
16782         return this.groups[navId] ;
16783     }
16784     
16785     
16786     
16787 });
16788
16789  /*
16790  * - LGPL
16791  *
16792  * TabPanel
16793  * 
16794  */
16795
16796 /**
16797  * @class Roo.bootstrap.TabPanel
16798  * @extends Roo.bootstrap.Component
16799  * Bootstrap TabPanel class
16800  * @cfg {Boolean} active panel active
16801  * @cfg {String} html panel content
16802  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
16803  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
16804  * 
16805  * 
16806  * @constructor
16807  * Create a new TabPanel
16808  * @param {Object} config The config object
16809  */
16810
16811 Roo.bootstrap.TabPanel = function(config){
16812     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
16813     this.addEvents({
16814         /**
16815              * @event changed
16816              * Fires when the active status changes
16817              * @param {Roo.bootstrap.TabPanel} this
16818              * @param {Boolean} state the new state
16819             
16820          */
16821         'changed': true,
16822         /**
16823              * @event beforedeactivate
16824              * Fires before a tab is de-activated - can be used to do validation on a form.
16825              * @param {Roo.bootstrap.TabPanel} this
16826              * @return {Boolean} false if there is an error
16827             
16828          */
16829         'beforedeactivate': true
16830      });
16831     
16832     this.tabId = this.tabId || Roo.id();
16833   
16834 };
16835
16836 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
16837     
16838     active: false,
16839     html: false,
16840     tabId: false,
16841     navId : false,
16842     
16843     getAutoCreate : function(){
16844         var cfg = {
16845             tag: 'div',
16846             // item is needed for carousel - not sure if it has any effect otherwise
16847             cls: 'tab-pane item',
16848             html: this.html || ''
16849         };
16850         
16851         if(this.active){
16852             cfg.cls += ' active';
16853         }
16854         
16855         if(this.tabId){
16856             cfg.tabId = this.tabId;
16857         }
16858         
16859         
16860         return cfg;
16861     },
16862     
16863     initEvents:  function()
16864     {
16865         var p = this.parent();
16866         this.navId = this.navId || p.navId;
16867         
16868         if (typeof(this.navId) != 'undefined') {
16869             // not really needed.. but just in case.. parent should be a NavGroup.
16870             var tg = Roo.bootstrap.TabGroup.get(this.navId);
16871             
16872             tg.register(this);
16873             
16874             var i = tg.tabs.length - 1;
16875             
16876             if(this.active && tg.bullets > 0 && i < tg.bullets){
16877                 tg.setActiveBullet(i);
16878             }
16879         }
16880         
16881     },
16882     
16883     
16884     onRender : function(ct, position)
16885     {
16886        // Roo.log("Call onRender: " + this.xtype);
16887         
16888         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
16889         
16890         
16891         
16892         
16893         
16894     },
16895     
16896     setActive: function(state)
16897     {
16898         Roo.log("panel - set active " + this.tabId + "=" + state);
16899         
16900         this.active = state;
16901         if (!state) {
16902             this.el.removeClass('active');
16903             
16904         } else  if (!this.el.hasClass('active')) {
16905             this.el.addClass('active');
16906         }
16907         
16908         this.fireEvent('changed', this, state);
16909     }
16910     
16911     
16912 });
16913  
16914
16915  
16916
16917  /*
16918  * - LGPL
16919  *
16920  * DateField
16921  * 
16922  */
16923
16924 /**
16925  * @class Roo.bootstrap.DateField
16926  * @extends Roo.bootstrap.Input
16927  * Bootstrap DateField class
16928  * @cfg {Number} weekStart default 0
16929  * @cfg {String} viewMode default empty, (months|years)
16930  * @cfg {String} minViewMode default empty, (months|years)
16931  * @cfg {Number} startDate default -Infinity
16932  * @cfg {Number} endDate default Infinity
16933  * @cfg {Boolean} todayHighlight default false
16934  * @cfg {Boolean} todayBtn default false
16935  * @cfg {Boolean} calendarWeeks default false
16936  * @cfg {Object} daysOfWeekDisabled default empty
16937  * @cfg {Boolean} singleMode default false (true | false)
16938  * 
16939  * @cfg {Boolean} keyboardNavigation default true
16940  * @cfg {String} language default en
16941  * 
16942  * @constructor
16943  * Create a new DateField
16944  * @param {Object} config The config object
16945  */
16946
16947 Roo.bootstrap.DateField = function(config){
16948     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
16949      this.addEvents({
16950             /**
16951              * @event show
16952              * Fires when this field show.
16953              * @param {Roo.bootstrap.DateField} this
16954              * @param {Mixed} date The date value
16955              */
16956             show : true,
16957             /**
16958              * @event show
16959              * Fires when this field hide.
16960              * @param {Roo.bootstrap.DateField} this
16961              * @param {Mixed} date The date value
16962              */
16963             hide : true,
16964             /**
16965              * @event select
16966              * Fires when select a date.
16967              * @param {Roo.bootstrap.DateField} this
16968              * @param {Mixed} date The date value
16969              */
16970             select : true,
16971             /**
16972              * @event beforeselect
16973              * Fires when before select a date.
16974              * @param {Roo.bootstrap.DateField} this
16975              * @param {Mixed} date The date value
16976              */
16977             beforeselect : true
16978         });
16979 };
16980
16981 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
16982     
16983     /**
16984      * @cfg {String} format
16985      * The default date format string which can be overriden for localization support.  The format must be
16986      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
16987      */
16988     format : "m/d/y",
16989     /**
16990      * @cfg {String} altFormats
16991      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
16992      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
16993      */
16994     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
16995     
16996     weekStart : 0,
16997     
16998     viewMode : '',
16999     
17000     minViewMode : '',
17001     
17002     todayHighlight : false,
17003     
17004     todayBtn: false,
17005     
17006     language: 'en',
17007     
17008     keyboardNavigation: true,
17009     
17010     calendarWeeks: false,
17011     
17012     startDate: -Infinity,
17013     
17014     endDate: Infinity,
17015     
17016     daysOfWeekDisabled: [],
17017     
17018     _events: [],
17019     
17020     singleMode : false,
17021     
17022     UTCDate: function()
17023     {
17024         return new Date(Date.UTC.apply(Date, arguments));
17025     },
17026     
17027     UTCToday: function()
17028     {
17029         var today = new Date();
17030         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
17031     },
17032     
17033     getDate: function() {
17034             var d = this.getUTCDate();
17035             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
17036     },
17037     
17038     getUTCDate: function() {
17039             return this.date;
17040     },
17041     
17042     setDate: function(d) {
17043             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
17044     },
17045     
17046     setUTCDate: function(d) {
17047             this.date = d;
17048             this.setValue(this.formatDate(this.date));
17049     },
17050         
17051     onRender: function(ct, position)
17052     {
17053         
17054         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
17055         
17056         this.language = this.language || 'en';
17057         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
17058         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
17059         
17060         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
17061         this.format = this.format || 'm/d/y';
17062         this.isInline = false;
17063         this.isInput = true;
17064         this.component = this.el.select('.add-on', true).first() || false;
17065         this.component = (this.component && this.component.length === 0) ? false : this.component;
17066         this.hasInput = this.component && this.inputEL().length;
17067         
17068         if (typeof(this.minViewMode === 'string')) {
17069             switch (this.minViewMode) {
17070                 case 'months':
17071                     this.minViewMode = 1;
17072                     break;
17073                 case 'years':
17074                     this.minViewMode = 2;
17075                     break;
17076                 default:
17077                     this.minViewMode = 0;
17078                     break;
17079             }
17080         }
17081         
17082         if (typeof(this.viewMode === 'string')) {
17083             switch (this.viewMode) {
17084                 case 'months':
17085                     this.viewMode = 1;
17086                     break;
17087                 case 'years':
17088                     this.viewMode = 2;
17089                     break;
17090                 default:
17091                     this.viewMode = 0;
17092                     break;
17093             }
17094         }
17095                 
17096         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
17097         
17098 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
17099         
17100         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17101         
17102         this.picker().on('mousedown', this.onMousedown, this);
17103         this.picker().on('click', this.onClick, this);
17104         
17105         this.picker().addClass('datepicker-dropdown');
17106         
17107         this.startViewMode = this.viewMode;
17108         
17109         if(this.singleMode){
17110             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
17111                 v.setVisibilityMode(Roo.Element.DISPLAY);
17112                 v.hide();
17113             });
17114             
17115             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
17116                 v.setStyle('width', '189px');
17117             });
17118         }
17119         
17120         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
17121             if(!this.calendarWeeks){
17122                 v.remove();
17123                 return;
17124             }
17125             
17126             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17127             v.attr('colspan', function(i, val){
17128                 return parseInt(val) + 1;
17129             });
17130         });
17131                         
17132         
17133         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
17134         
17135         this.setStartDate(this.startDate);
17136         this.setEndDate(this.endDate);
17137         
17138         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
17139         
17140         this.fillDow();
17141         this.fillMonths();
17142         this.update();
17143         this.showMode();
17144         
17145         if(this.isInline) {
17146             this.show();
17147         }
17148     },
17149     
17150     picker : function()
17151     {
17152         return this.pickerEl;
17153 //        return this.el.select('.datepicker', true).first();
17154     },
17155     
17156     fillDow: function()
17157     {
17158         var dowCnt = this.weekStart;
17159         
17160         var dow = {
17161             tag: 'tr',
17162             cn: [
17163                 
17164             ]
17165         };
17166         
17167         if(this.calendarWeeks){
17168             dow.cn.push({
17169                 tag: 'th',
17170                 cls: 'cw',
17171                 html: '&nbsp;'
17172             })
17173         }
17174         
17175         while (dowCnt < this.weekStart + 7) {
17176             dow.cn.push({
17177                 tag: 'th',
17178                 cls: 'dow',
17179                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
17180             });
17181         }
17182         
17183         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
17184     },
17185     
17186     fillMonths: function()
17187     {    
17188         var i = 0;
17189         var months = this.picker().select('>.datepicker-months td', true).first();
17190         
17191         months.dom.innerHTML = '';
17192         
17193         while (i < 12) {
17194             var month = {
17195                 tag: 'span',
17196                 cls: 'month',
17197                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
17198             };
17199             
17200             months.createChild(month);
17201         }
17202         
17203     },
17204     
17205     update: function()
17206     {
17207         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;
17208         
17209         if (this.date < this.startDate) {
17210             this.viewDate = new Date(this.startDate);
17211         } else if (this.date > this.endDate) {
17212             this.viewDate = new Date(this.endDate);
17213         } else {
17214             this.viewDate = new Date(this.date);
17215         }
17216         
17217         this.fill();
17218     },
17219     
17220     fill: function() 
17221     {
17222         var d = new Date(this.viewDate),
17223                 year = d.getUTCFullYear(),
17224                 month = d.getUTCMonth(),
17225                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
17226                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
17227                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
17228                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
17229                 currentDate = this.date && this.date.valueOf(),
17230                 today = this.UTCToday();
17231         
17232         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
17233         
17234 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
17235         
17236 //        this.picker.select('>tfoot th.today').
17237 //                                              .text(dates[this.language].today)
17238 //                                              .toggle(this.todayBtn !== false);
17239     
17240         this.updateNavArrows();
17241         this.fillMonths();
17242                                                 
17243         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
17244         
17245         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
17246          
17247         prevMonth.setUTCDate(day);
17248         
17249         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
17250         
17251         var nextMonth = new Date(prevMonth);
17252         
17253         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
17254         
17255         nextMonth = nextMonth.valueOf();
17256         
17257         var fillMonths = false;
17258         
17259         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
17260         
17261         while(prevMonth.valueOf() < nextMonth) {
17262             var clsName = '';
17263             
17264             if (prevMonth.getUTCDay() === this.weekStart) {
17265                 if(fillMonths){
17266                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
17267                 }
17268                     
17269                 fillMonths = {
17270                     tag: 'tr',
17271                     cn: []
17272                 };
17273                 
17274                 if(this.calendarWeeks){
17275                     // ISO 8601: First week contains first thursday.
17276                     // ISO also states week starts on Monday, but we can be more abstract here.
17277                     var
17278                     // Start of current week: based on weekstart/current date
17279                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
17280                     // Thursday of this week
17281                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
17282                     // First Thursday of year, year from thursday
17283                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
17284                     // Calendar week: ms between thursdays, div ms per day, div 7 days
17285                     calWeek =  (th - yth) / 864e5 / 7 + 1;
17286                     
17287                     fillMonths.cn.push({
17288                         tag: 'td',
17289                         cls: 'cw',
17290                         html: calWeek
17291                     });
17292                 }
17293             }
17294             
17295             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
17296                 clsName += ' old';
17297             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
17298                 clsName += ' new';
17299             }
17300             if (this.todayHighlight &&
17301                 prevMonth.getUTCFullYear() == today.getFullYear() &&
17302                 prevMonth.getUTCMonth() == today.getMonth() &&
17303                 prevMonth.getUTCDate() == today.getDate()) {
17304                 clsName += ' today';
17305             }
17306             
17307             if (currentDate && prevMonth.valueOf() === currentDate) {
17308                 clsName += ' active';
17309             }
17310             
17311             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
17312                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
17313                     clsName += ' disabled';
17314             }
17315             
17316             fillMonths.cn.push({
17317                 tag: 'td',
17318                 cls: 'day ' + clsName,
17319                 html: prevMonth.getDate()
17320             });
17321             
17322             prevMonth.setDate(prevMonth.getDate()+1);
17323         }
17324           
17325         var currentYear = this.date && this.date.getUTCFullYear();
17326         var currentMonth = this.date && this.date.getUTCMonth();
17327         
17328         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
17329         
17330         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
17331             v.removeClass('active');
17332             
17333             if(currentYear === year && k === currentMonth){
17334                 v.addClass('active');
17335             }
17336             
17337             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
17338                 v.addClass('disabled');
17339             }
17340             
17341         });
17342         
17343         
17344         year = parseInt(year/10, 10) * 10;
17345         
17346         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
17347         
17348         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
17349         
17350         year -= 1;
17351         for (var i = -1; i < 11; i++) {
17352             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
17353                 tag: 'span',
17354                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
17355                 html: year
17356             });
17357             
17358             year += 1;
17359         }
17360     },
17361     
17362     showMode: function(dir) 
17363     {
17364         if (dir) {
17365             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
17366         }
17367         
17368         Roo.each(this.picker().select('>div',true).elements, function(v){
17369             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17370             v.hide();
17371         });
17372         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
17373     },
17374     
17375     place: function()
17376     {
17377         if(this.isInline) {
17378             return;
17379         }
17380         
17381         this.picker().removeClass(['bottom', 'top']);
17382         
17383         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
17384             /*
17385              * place to the top of element!
17386              *
17387              */
17388             
17389             this.picker().addClass('top');
17390             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
17391             
17392             return;
17393         }
17394         
17395         this.picker().addClass('bottom');
17396         
17397         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
17398     },
17399     
17400     parseDate : function(value)
17401     {
17402         if(!value || value instanceof Date){
17403             return value;
17404         }
17405         var v = Date.parseDate(value, this.format);
17406         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
17407             v = Date.parseDate(value, 'Y-m-d');
17408         }
17409         if(!v && this.altFormats){
17410             if(!this.altFormatsArray){
17411                 this.altFormatsArray = this.altFormats.split("|");
17412             }
17413             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
17414                 v = Date.parseDate(value, this.altFormatsArray[i]);
17415             }
17416         }
17417         return v;
17418     },
17419     
17420     formatDate : function(date, fmt)
17421     {   
17422         return (!date || !(date instanceof Date)) ?
17423         date : date.dateFormat(fmt || this.format);
17424     },
17425     
17426     onFocus : function()
17427     {
17428         Roo.bootstrap.DateField.superclass.onFocus.call(this);
17429         this.show();
17430     },
17431     
17432     onBlur : function()
17433     {
17434         Roo.bootstrap.DateField.superclass.onBlur.call(this);
17435         
17436         var d = this.inputEl().getValue();
17437         
17438         this.setValue(d);
17439                 
17440         this.hide();
17441     },
17442     
17443     show : function()
17444     {
17445         this.picker().show();
17446         this.update();
17447         this.place();
17448         
17449         this.fireEvent('show', this, this.date);
17450     },
17451     
17452     hide : function()
17453     {
17454         if(this.isInline) {
17455             return;
17456         }
17457         this.picker().hide();
17458         this.viewMode = this.startViewMode;
17459         this.showMode();
17460         
17461         this.fireEvent('hide', this, this.date);
17462         
17463     },
17464     
17465     onMousedown: function(e)
17466     {
17467         e.stopPropagation();
17468         e.preventDefault();
17469     },
17470     
17471     keyup: function(e)
17472     {
17473         Roo.bootstrap.DateField.superclass.keyup.call(this);
17474         this.update();
17475     },
17476
17477     setValue: function(v)
17478     {
17479         if(this.fireEvent('beforeselect', this, v) !== false){
17480             var d = new Date(this.parseDate(v) ).clearTime();
17481         
17482             if(isNaN(d.getTime())){
17483                 this.date = this.viewDate = '';
17484                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
17485                 return;
17486             }
17487
17488             v = this.formatDate(d);
17489
17490             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
17491
17492             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
17493
17494             this.update();
17495
17496             this.fireEvent('select', this, this.date);
17497         }
17498     },
17499     
17500     getValue: function()
17501     {
17502         return this.formatDate(this.date);
17503     },
17504     
17505     fireKey: function(e)
17506     {
17507         if (!this.picker().isVisible()){
17508             if (e.keyCode == 27) { // allow escape to hide and re-show picker
17509                 this.show();
17510             }
17511             return;
17512         }
17513         
17514         var dateChanged = false,
17515         dir, day, month,
17516         newDate, newViewDate;
17517         
17518         switch(e.keyCode){
17519             case 27: // escape
17520                 this.hide();
17521                 e.preventDefault();
17522                 break;
17523             case 37: // left
17524             case 39: // right
17525                 if (!this.keyboardNavigation) {
17526                     break;
17527                 }
17528                 dir = e.keyCode == 37 ? -1 : 1;
17529                 
17530                 if (e.ctrlKey){
17531                     newDate = this.moveYear(this.date, dir);
17532                     newViewDate = this.moveYear(this.viewDate, dir);
17533                 } else if (e.shiftKey){
17534                     newDate = this.moveMonth(this.date, dir);
17535                     newViewDate = this.moveMonth(this.viewDate, dir);
17536                 } else {
17537                     newDate = new Date(this.date);
17538                     newDate.setUTCDate(this.date.getUTCDate() + dir);
17539                     newViewDate = new Date(this.viewDate);
17540                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
17541                 }
17542                 if (this.dateWithinRange(newDate)){
17543                     this.date = newDate;
17544                     this.viewDate = newViewDate;
17545                     this.setValue(this.formatDate(this.date));
17546 //                    this.update();
17547                     e.preventDefault();
17548                     dateChanged = true;
17549                 }
17550                 break;
17551             case 38: // up
17552             case 40: // down
17553                 if (!this.keyboardNavigation) {
17554                     break;
17555                 }
17556                 dir = e.keyCode == 38 ? -1 : 1;
17557                 if (e.ctrlKey){
17558                     newDate = this.moveYear(this.date, dir);
17559                     newViewDate = this.moveYear(this.viewDate, dir);
17560                 } else if (e.shiftKey){
17561                     newDate = this.moveMonth(this.date, dir);
17562                     newViewDate = this.moveMonth(this.viewDate, dir);
17563                 } else {
17564                     newDate = new Date(this.date);
17565                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
17566                     newViewDate = new Date(this.viewDate);
17567                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
17568                 }
17569                 if (this.dateWithinRange(newDate)){
17570                     this.date = newDate;
17571                     this.viewDate = newViewDate;
17572                     this.setValue(this.formatDate(this.date));
17573 //                    this.update();
17574                     e.preventDefault();
17575                     dateChanged = true;
17576                 }
17577                 break;
17578             case 13: // enter
17579                 this.setValue(this.formatDate(this.date));
17580                 this.hide();
17581                 e.preventDefault();
17582                 break;
17583             case 9: // tab
17584                 this.setValue(this.formatDate(this.date));
17585                 this.hide();
17586                 break;
17587             case 16: // shift
17588             case 17: // ctrl
17589             case 18: // alt
17590                 break;
17591             default :
17592                 this.hide();
17593                 
17594         }
17595     },
17596     
17597     
17598     onClick: function(e) 
17599     {
17600         e.stopPropagation();
17601         e.preventDefault();
17602         
17603         var target = e.getTarget();
17604         
17605         if(target.nodeName.toLowerCase() === 'i'){
17606             target = Roo.get(target).dom.parentNode;
17607         }
17608         
17609         var nodeName = target.nodeName;
17610         var className = target.className;
17611         var html = target.innerHTML;
17612         //Roo.log(nodeName);
17613         
17614         switch(nodeName.toLowerCase()) {
17615             case 'th':
17616                 switch(className) {
17617                     case 'switch':
17618                         this.showMode(1);
17619                         break;
17620                     case 'prev':
17621                     case 'next':
17622                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
17623                         switch(this.viewMode){
17624                                 case 0:
17625                                         this.viewDate = this.moveMonth(this.viewDate, dir);
17626                                         break;
17627                                 case 1:
17628                                 case 2:
17629                                         this.viewDate = this.moveYear(this.viewDate, dir);
17630                                         break;
17631                         }
17632                         this.fill();
17633                         break;
17634                     case 'today':
17635                         var date = new Date();
17636                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
17637 //                        this.fill()
17638                         this.setValue(this.formatDate(this.date));
17639                         
17640                         this.hide();
17641                         break;
17642                 }
17643                 break;
17644             case 'span':
17645                 if (className.indexOf('disabled') < 0) {
17646                     this.viewDate.setUTCDate(1);
17647                     if (className.indexOf('month') > -1) {
17648                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
17649                     } else {
17650                         var year = parseInt(html, 10) || 0;
17651                         this.viewDate.setUTCFullYear(year);
17652                         
17653                     }
17654                     
17655                     if(this.singleMode){
17656                         this.setValue(this.formatDate(this.viewDate));
17657                         this.hide();
17658                         return;
17659                     }
17660                     
17661                     this.showMode(-1);
17662                     this.fill();
17663                 }
17664                 break;
17665                 
17666             case 'td':
17667                 //Roo.log(className);
17668                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
17669                     var day = parseInt(html, 10) || 1;
17670                     var year = this.viewDate.getUTCFullYear(),
17671                         month = this.viewDate.getUTCMonth();
17672
17673                     if (className.indexOf('old') > -1) {
17674                         if(month === 0 ){
17675                             month = 11;
17676                             year -= 1;
17677                         }else{
17678                             month -= 1;
17679                         }
17680                     } else if (className.indexOf('new') > -1) {
17681                         if (month == 11) {
17682                             month = 0;
17683                             year += 1;
17684                         } else {
17685                             month += 1;
17686                         }
17687                     }
17688                     //Roo.log([year,month,day]);
17689                     this.date = this.UTCDate(year, month, day,0,0,0,0);
17690                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
17691 //                    this.fill();
17692                     //Roo.log(this.formatDate(this.date));
17693                     this.setValue(this.formatDate(this.date));
17694                     this.hide();
17695                 }
17696                 break;
17697         }
17698     },
17699     
17700     setStartDate: function(startDate)
17701     {
17702         this.startDate = startDate || -Infinity;
17703         if (this.startDate !== -Infinity) {
17704             this.startDate = this.parseDate(this.startDate);
17705         }
17706         this.update();
17707         this.updateNavArrows();
17708     },
17709
17710     setEndDate: function(endDate)
17711     {
17712         this.endDate = endDate || Infinity;
17713         if (this.endDate !== Infinity) {
17714             this.endDate = this.parseDate(this.endDate);
17715         }
17716         this.update();
17717         this.updateNavArrows();
17718     },
17719     
17720     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
17721     {
17722         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
17723         if (typeof(this.daysOfWeekDisabled) !== 'object') {
17724             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
17725         }
17726         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
17727             return parseInt(d, 10);
17728         });
17729         this.update();
17730         this.updateNavArrows();
17731     },
17732     
17733     updateNavArrows: function() 
17734     {
17735         if(this.singleMode){
17736             return;
17737         }
17738         
17739         var d = new Date(this.viewDate),
17740         year = d.getUTCFullYear(),
17741         month = d.getUTCMonth();
17742         
17743         Roo.each(this.picker().select('.prev', true).elements, function(v){
17744             v.show();
17745             switch (this.viewMode) {
17746                 case 0:
17747
17748                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
17749                         v.hide();
17750                     }
17751                     break;
17752                 case 1:
17753                 case 2:
17754                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
17755                         v.hide();
17756                     }
17757                     break;
17758             }
17759         });
17760         
17761         Roo.each(this.picker().select('.next', true).elements, function(v){
17762             v.show();
17763             switch (this.viewMode) {
17764                 case 0:
17765
17766                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
17767                         v.hide();
17768                     }
17769                     break;
17770                 case 1:
17771                 case 2:
17772                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
17773                         v.hide();
17774                     }
17775                     break;
17776             }
17777         })
17778     },
17779     
17780     moveMonth: function(date, dir)
17781     {
17782         if (!dir) {
17783             return date;
17784         }
17785         var new_date = new Date(date.valueOf()),
17786         day = new_date.getUTCDate(),
17787         month = new_date.getUTCMonth(),
17788         mag = Math.abs(dir),
17789         new_month, test;
17790         dir = dir > 0 ? 1 : -1;
17791         if (mag == 1){
17792             test = dir == -1
17793             // If going back one month, make sure month is not current month
17794             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
17795             ? function(){
17796                 return new_date.getUTCMonth() == month;
17797             }
17798             // If going forward one month, make sure month is as expected
17799             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
17800             : function(){
17801                 return new_date.getUTCMonth() != new_month;
17802             };
17803             new_month = month + dir;
17804             new_date.setUTCMonth(new_month);
17805             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
17806             if (new_month < 0 || new_month > 11) {
17807                 new_month = (new_month + 12) % 12;
17808             }
17809         } else {
17810             // For magnitudes >1, move one month at a time...
17811             for (var i=0; i<mag; i++) {
17812                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
17813                 new_date = this.moveMonth(new_date, dir);
17814             }
17815             // ...then reset the day, keeping it in the new month
17816             new_month = new_date.getUTCMonth();
17817             new_date.setUTCDate(day);
17818             test = function(){
17819                 return new_month != new_date.getUTCMonth();
17820             };
17821         }
17822         // Common date-resetting loop -- if date is beyond end of month, make it
17823         // end of month
17824         while (test()){
17825             new_date.setUTCDate(--day);
17826             new_date.setUTCMonth(new_month);
17827         }
17828         return new_date;
17829     },
17830
17831     moveYear: function(date, dir)
17832     {
17833         return this.moveMonth(date, dir*12);
17834     },
17835
17836     dateWithinRange: function(date)
17837     {
17838         return date >= this.startDate && date <= this.endDate;
17839     },
17840
17841     
17842     remove: function() 
17843     {
17844         this.picker().remove();
17845     }
17846    
17847 });
17848
17849 Roo.apply(Roo.bootstrap.DateField,  {
17850     
17851     head : {
17852         tag: 'thead',
17853         cn: [
17854         {
17855             tag: 'tr',
17856             cn: [
17857             {
17858                 tag: 'th',
17859                 cls: 'prev',
17860                 html: '<i class="fa fa-arrow-left"/>'
17861             },
17862             {
17863                 tag: 'th',
17864                 cls: 'switch',
17865                 colspan: '5'
17866             },
17867             {
17868                 tag: 'th',
17869                 cls: 'next',
17870                 html: '<i class="fa fa-arrow-right"/>'
17871             }
17872
17873             ]
17874         }
17875         ]
17876     },
17877     
17878     content : {
17879         tag: 'tbody',
17880         cn: [
17881         {
17882             tag: 'tr',
17883             cn: [
17884             {
17885                 tag: 'td',
17886                 colspan: '7'
17887             }
17888             ]
17889         }
17890         ]
17891     },
17892     
17893     footer : {
17894         tag: 'tfoot',
17895         cn: [
17896         {
17897             tag: 'tr',
17898             cn: [
17899             {
17900                 tag: 'th',
17901                 colspan: '7',
17902                 cls: 'today'
17903             }
17904                     
17905             ]
17906         }
17907         ]
17908     },
17909     
17910     dates:{
17911         en: {
17912             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
17913             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
17914             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
17915             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
17916             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
17917             today: "Today"
17918         }
17919     },
17920     
17921     modes: [
17922     {
17923         clsName: 'days',
17924         navFnc: 'Month',
17925         navStep: 1
17926     },
17927     {
17928         clsName: 'months',
17929         navFnc: 'FullYear',
17930         navStep: 1
17931     },
17932     {
17933         clsName: 'years',
17934         navFnc: 'FullYear',
17935         navStep: 10
17936     }]
17937 });
17938
17939 Roo.apply(Roo.bootstrap.DateField,  {
17940   
17941     template : {
17942         tag: 'div',
17943         cls: 'datepicker dropdown-menu roo-dynamic',
17944         cn: [
17945         {
17946             tag: 'div',
17947             cls: 'datepicker-days',
17948             cn: [
17949             {
17950                 tag: 'table',
17951                 cls: 'table-condensed',
17952                 cn:[
17953                 Roo.bootstrap.DateField.head,
17954                 {
17955                     tag: 'tbody'
17956                 },
17957                 Roo.bootstrap.DateField.footer
17958                 ]
17959             }
17960             ]
17961         },
17962         {
17963             tag: 'div',
17964             cls: 'datepicker-months',
17965             cn: [
17966             {
17967                 tag: 'table',
17968                 cls: 'table-condensed',
17969                 cn:[
17970                 Roo.bootstrap.DateField.head,
17971                 Roo.bootstrap.DateField.content,
17972                 Roo.bootstrap.DateField.footer
17973                 ]
17974             }
17975             ]
17976         },
17977         {
17978             tag: 'div',
17979             cls: 'datepicker-years',
17980             cn: [
17981             {
17982                 tag: 'table',
17983                 cls: 'table-condensed',
17984                 cn:[
17985                 Roo.bootstrap.DateField.head,
17986                 Roo.bootstrap.DateField.content,
17987                 Roo.bootstrap.DateField.footer
17988                 ]
17989             }
17990             ]
17991         }
17992         ]
17993     }
17994 });
17995
17996  
17997
17998  /*
17999  * - LGPL
18000  *
18001  * TimeField
18002  * 
18003  */
18004
18005 /**
18006  * @class Roo.bootstrap.TimeField
18007  * @extends Roo.bootstrap.Input
18008  * Bootstrap DateField class
18009  * 
18010  * 
18011  * @constructor
18012  * Create a new TimeField
18013  * @param {Object} config The config object
18014  */
18015
18016 Roo.bootstrap.TimeField = function(config){
18017     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
18018     this.addEvents({
18019             /**
18020              * @event show
18021              * Fires when this field show.
18022              * @param {Roo.bootstrap.DateField} thisthis
18023              * @param {Mixed} date The date value
18024              */
18025             show : true,
18026             /**
18027              * @event show
18028              * Fires when this field hide.
18029              * @param {Roo.bootstrap.DateField} this
18030              * @param {Mixed} date The date value
18031              */
18032             hide : true,
18033             /**
18034              * @event select
18035              * Fires when select a date.
18036              * @param {Roo.bootstrap.DateField} this
18037              * @param {Mixed} date The date value
18038              */
18039             select : true
18040         });
18041 };
18042
18043 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
18044     
18045     /**
18046      * @cfg {String} format
18047      * The default time format string which can be overriden for localization support.  The format must be
18048      * valid according to {@link Date#parseDate} (defaults to 'H:i').
18049      */
18050     format : "H:i",
18051        
18052     onRender: function(ct, position)
18053     {
18054         
18055         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
18056                 
18057         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
18058         
18059         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18060         
18061         this.pop = this.picker().select('>.datepicker-time',true).first();
18062         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18063         
18064         this.picker().on('mousedown', this.onMousedown, this);
18065         this.picker().on('click', this.onClick, this);
18066         
18067         this.picker().addClass('datepicker-dropdown');
18068     
18069         this.fillTime();
18070         this.update();
18071             
18072         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
18073         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
18074         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
18075         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
18076         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
18077         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
18078
18079     },
18080     
18081     fireKey: function(e){
18082         if (!this.picker().isVisible()){
18083             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18084                 this.show();
18085             }
18086             return;
18087         }
18088
18089         e.preventDefault();
18090         
18091         switch(e.keyCode){
18092             case 27: // escape
18093                 this.hide();
18094                 break;
18095             case 37: // left
18096             case 39: // right
18097                 this.onTogglePeriod();
18098                 break;
18099             case 38: // up
18100                 this.onIncrementMinutes();
18101                 break;
18102             case 40: // down
18103                 this.onDecrementMinutes();
18104                 break;
18105             case 13: // enter
18106             case 9: // tab
18107                 this.setTime();
18108                 break;
18109         }
18110     },
18111     
18112     onClick: function(e) {
18113         e.stopPropagation();
18114         e.preventDefault();
18115     },
18116     
18117     picker : function()
18118     {
18119         return this.el.select('.datepicker', true).first();
18120     },
18121     
18122     fillTime: function()
18123     {    
18124         var time = this.pop.select('tbody', true).first();
18125         
18126         time.dom.innerHTML = '';
18127         
18128         time.createChild({
18129             tag: 'tr',
18130             cn: [
18131                 {
18132                     tag: 'td',
18133                     cn: [
18134                         {
18135                             tag: 'a',
18136                             href: '#',
18137                             cls: 'btn',
18138                             cn: [
18139                                 {
18140                                     tag: 'span',
18141                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
18142                                 }
18143                             ]
18144                         } 
18145                     ]
18146                 },
18147                 {
18148                     tag: 'td',
18149                     cls: 'separator'
18150                 },
18151                 {
18152                     tag: 'td',
18153                     cn: [
18154                         {
18155                             tag: 'a',
18156                             href: '#',
18157                             cls: 'btn',
18158                             cn: [
18159                                 {
18160                                     tag: 'span',
18161                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
18162                                 }
18163                             ]
18164                         }
18165                     ]
18166                 },
18167                 {
18168                     tag: 'td',
18169                     cls: 'separator'
18170                 }
18171             ]
18172         });
18173         
18174         time.createChild({
18175             tag: 'tr',
18176             cn: [
18177                 {
18178                     tag: 'td',
18179                     cn: [
18180                         {
18181                             tag: 'span',
18182                             cls: 'timepicker-hour',
18183                             html: '00'
18184                         }  
18185                     ]
18186                 },
18187                 {
18188                     tag: 'td',
18189                     cls: 'separator',
18190                     html: ':'
18191                 },
18192                 {
18193                     tag: 'td',
18194                     cn: [
18195                         {
18196                             tag: 'span',
18197                             cls: 'timepicker-minute',
18198                             html: '00'
18199                         }  
18200                     ]
18201                 },
18202                 {
18203                     tag: 'td',
18204                     cls: 'separator'
18205                 },
18206                 {
18207                     tag: 'td',
18208                     cn: [
18209                         {
18210                             tag: 'button',
18211                             type: 'button',
18212                             cls: 'btn btn-primary period',
18213                             html: 'AM'
18214                             
18215                         }
18216                     ]
18217                 }
18218             ]
18219         });
18220         
18221         time.createChild({
18222             tag: 'tr',
18223             cn: [
18224                 {
18225                     tag: 'td',
18226                     cn: [
18227                         {
18228                             tag: 'a',
18229                             href: '#',
18230                             cls: 'btn',
18231                             cn: [
18232                                 {
18233                                     tag: 'span',
18234                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
18235                                 }
18236                             ]
18237                         }
18238                     ]
18239                 },
18240                 {
18241                     tag: 'td',
18242                     cls: 'separator'
18243                 },
18244                 {
18245                     tag: 'td',
18246                     cn: [
18247                         {
18248                             tag: 'a',
18249                             href: '#',
18250                             cls: 'btn',
18251                             cn: [
18252                                 {
18253                                     tag: 'span',
18254                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
18255                                 }
18256                             ]
18257                         }
18258                     ]
18259                 },
18260                 {
18261                     tag: 'td',
18262                     cls: 'separator'
18263                 }
18264             ]
18265         });
18266         
18267     },
18268     
18269     update: function()
18270     {
18271         
18272         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
18273         
18274         this.fill();
18275     },
18276     
18277     fill: function() 
18278     {
18279         var hours = this.time.getHours();
18280         var minutes = this.time.getMinutes();
18281         var period = 'AM';
18282         
18283         if(hours > 11){
18284             period = 'PM';
18285         }
18286         
18287         if(hours == 0){
18288             hours = 12;
18289         }
18290         
18291         
18292         if(hours > 12){
18293             hours = hours - 12;
18294         }
18295         
18296         if(hours < 10){
18297             hours = '0' + hours;
18298         }
18299         
18300         if(minutes < 10){
18301             minutes = '0' + minutes;
18302         }
18303         
18304         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
18305         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
18306         this.pop.select('button', true).first().dom.innerHTML = period;
18307         
18308     },
18309     
18310     place: function()
18311     {   
18312         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
18313         
18314         var cls = ['bottom'];
18315         
18316         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
18317             cls.pop();
18318             cls.push('top');
18319         }
18320         
18321         cls.push('right');
18322         
18323         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
18324             cls.pop();
18325             cls.push('left');
18326         }
18327         
18328         this.picker().addClass(cls.join('-'));
18329         
18330         var _this = this;
18331         
18332         Roo.each(cls, function(c){
18333             if(c == 'bottom'){
18334                 _this.picker().setTop(_this.inputEl().getHeight());
18335                 return;
18336             }
18337             if(c == 'top'){
18338                 _this.picker().setTop(0 - _this.picker().getHeight());
18339                 return;
18340             }
18341             
18342             if(c == 'left'){
18343                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
18344                 return;
18345             }
18346             if(c == 'right'){
18347                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
18348                 return;
18349             }
18350         });
18351         
18352     },
18353   
18354     onFocus : function()
18355     {
18356         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
18357         this.show();
18358     },
18359     
18360     onBlur : function()
18361     {
18362         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
18363         this.hide();
18364     },
18365     
18366     show : function()
18367     {
18368         this.picker().show();
18369         this.pop.show();
18370         this.update();
18371         this.place();
18372         
18373         this.fireEvent('show', this, this.date);
18374     },
18375     
18376     hide : function()
18377     {
18378         this.picker().hide();
18379         this.pop.hide();
18380         
18381         this.fireEvent('hide', this, this.date);
18382     },
18383     
18384     setTime : function()
18385     {
18386         this.hide();
18387         this.setValue(this.time.format(this.format));
18388         
18389         this.fireEvent('select', this, this.date);
18390         
18391         
18392     },
18393     
18394     onMousedown: function(e){
18395         e.stopPropagation();
18396         e.preventDefault();
18397     },
18398     
18399     onIncrementHours: function()
18400     {
18401         Roo.log('onIncrementHours');
18402         this.time = this.time.add(Date.HOUR, 1);
18403         this.update();
18404         
18405     },
18406     
18407     onDecrementHours: function()
18408     {
18409         Roo.log('onDecrementHours');
18410         this.time = this.time.add(Date.HOUR, -1);
18411         this.update();
18412     },
18413     
18414     onIncrementMinutes: function()
18415     {
18416         Roo.log('onIncrementMinutes');
18417         this.time = this.time.add(Date.MINUTE, 1);
18418         this.update();
18419     },
18420     
18421     onDecrementMinutes: function()
18422     {
18423         Roo.log('onDecrementMinutes');
18424         this.time = this.time.add(Date.MINUTE, -1);
18425         this.update();
18426     },
18427     
18428     onTogglePeriod: function()
18429     {
18430         Roo.log('onTogglePeriod');
18431         this.time = this.time.add(Date.HOUR, 12);
18432         this.update();
18433     }
18434     
18435    
18436 });
18437
18438 Roo.apply(Roo.bootstrap.TimeField,  {
18439     
18440     content : {
18441         tag: 'tbody',
18442         cn: [
18443             {
18444                 tag: 'tr',
18445                 cn: [
18446                 {
18447                     tag: 'td',
18448                     colspan: '7'
18449                 }
18450                 ]
18451             }
18452         ]
18453     },
18454     
18455     footer : {
18456         tag: 'tfoot',
18457         cn: [
18458             {
18459                 tag: 'tr',
18460                 cn: [
18461                 {
18462                     tag: 'th',
18463                     colspan: '7',
18464                     cls: '',
18465                     cn: [
18466                         {
18467                             tag: 'button',
18468                             cls: 'btn btn-info ok',
18469                             html: 'OK'
18470                         }
18471                     ]
18472                 }
18473
18474                 ]
18475             }
18476         ]
18477     }
18478 });
18479
18480 Roo.apply(Roo.bootstrap.TimeField,  {
18481   
18482     template : {
18483         tag: 'div',
18484         cls: 'datepicker dropdown-menu',
18485         cn: [
18486             {
18487                 tag: 'div',
18488                 cls: 'datepicker-time',
18489                 cn: [
18490                 {
18491                     tag: 'table',
18492                     cls: 'table-condensed',
18493                     cn:[
18494                     Roo.bootstrap.TimeField.content,
18495                     Roo.bootstrap.TimeField.footer
18496                     ]
18497                 }
18498                 ]
18499             }
18500         ]
18501     }
18502 });
18503
18504  
18505
18506  /*
18507  * - LGPL
18508  *
18509  * MonthField
18510  * 
18511  */
18512
18513 /**
18514  * @class Roo.bootstrap.MonthField
18515  * @extends Roo.bootstrap.Input
18516  * Bootstrap MonthField class
18517  * 
18518  * @cfg {String} language default en
18519  * 
18520  * @constructor
18521  * Create a new MonthField
18522  * @param {Object} config The config object
18523  */
18524
18525 Roo.bootstrap.MonthField = function(config){
18526     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
18527     
18528     this.addEvents({
18529         /**
18530          * @event show
18531          * Fires when this field show.
18532          * @param {Roo.bootstrap.MonthField} this
18533          * @param {Mixed} date The date value
18534          */
18535         show : true,
18536         /**
18537          * @event show
18538          * Fires when this field hide.
18539          * @param {Roo.bootstrap.MonthField} this
18540          * @param {Mixed} date The date value
18541          */
18542         hide : true,
18543         /**
18544          * @event select
18545          * Fires when select a date.
18546          * @param {Roo.bootstrap.MonthField} this
18547          * @param {String} oldvalue The old value
18548          * @param {String} newvalue The new value
18549          */
18550         select : true
18551     });
18552 };
18553
18554 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
18555     
18556     onRender: function(ct, position)
18557     {
18558         
18559         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
18560         
18561         this.language = this.language || 'en';
18562         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
18563         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
18564         
18565         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
18566         this.isInline = false;
18567         this.isInput = true;
18568         this.component = this.el.select('.add-on', true).first() || false;
18569         this.component = (this.component && this.component.length === 0) ? false : this.component;
18570         this.hasInput = this.component && this.inputEL().length;
18571         
18572         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
18573         
18574         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18575         
18576         this.picker().on('mousedown', this.onMousedown, this);
18577         this.picker().on('click', this.onClick, this);
18578         
18579         this.picker().addClass('datepicker-dropdown');
18580         
18581         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18582             v.setStyle('width', '189px');
18583         });
18584         
18585         this.fillMonths();
18586         
18587         this.update();
18588         
18589         if(this.isInline) {
18590             this.show();
18591         }
18592         
18593     },
18594     
18595     setValue: function(v, suppressEvent)
18596     {   
18597         var o = this.getValue();
18598         
18599         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
18600         
18601         this.update();
18602
18603         if(suppressEvent !== true){
18604             this.fireEvent('select', this, o, v);
18605         }
18606         
18607     },
18608     
18609     getValue: function()
18610     {
18611         return this.value;
18612     },
18613     
18614     onClick: function(e) 
18615     {
18616         e.stopPropagation();
18617         e.preventDefault();
18618         
18619         var target = e.getTarget();
18620         
18621         if(target.nodeName.toLowerCase() === 'i'){
18622             target = Roo.get(target).dom.parentNode;
18623         }
18624         
18625         var nodeName = target.nodeName;
18626         var className = target.className;
18627         var html = target.innerHTML;
18628         
18629         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
18630             return;
18631         }
18632         
18633         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
18634         
18635         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18636         
18637         this.hide();
18638                         
18639     },
18640     
18641     picker : function()
18642     {
18643         return this.pickerEl;
18644     },
18645     
18646     fillMonths: function()
18647     {    
18648         var i = 0;
18649         var months = this.picker().select('>.datepicker-months td', true).first();
18650         
18651         months.dom.innerHTML = '';
18652         
18653         while (i < 12) {
18654             var month = {
18655                 tag: 'span',
18656                 cls: 'month',
18657                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
18658             };
18659             
18660             months.createChild(month);
18661         }
18662         
18663     },
18664     
18665     update: function()
18666     {
18667         var _this = this;
18668         
18669         if(typeof(this.vIndex) == 'undefined' && this.value.length){
18670             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
18671         }
18672         
18673         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
18674             e.removeClass('active');
18675             
18676             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
18677                 e.addClass('active');
18678             }
18679         })
18680     },
18681     
18682     place: function()
18683     {
18684         if(this.isInline) {
18685             return;
18686         }
18687         
18688         this.picker().removeClass(['bottom', 'top']);
18689         
18690         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18691             /*
18692              * place to the top of element!
18693              *
18694              */
18695             
18696             this.picker().addClass('top');
18697             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18698             
18699             return;
18700         }
18701         
18702         this.picker().addClass('bottom');
18703         
18704         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18705     },
18706     
18707     onFocus : function()
18708     {
18709         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
18710         this.show();
18711     },
18712     
18713     onBlur : function()
18714     {
18715         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
18716         
18717         var d = this.inputEl().getValue();
18718         
18719         this.setValue(d);
18720                 
18721         this.hide();
18722     },
18723     
18724     show : function()
18725     {
18726         this.picker().show();
18727         this.picker().select('>.datepicker-months', true).first().show();
18728         this.update();
18729         this.place();
18730         
18731         this.fireEvent('show', this, this.date);
18732     },
18733     
18734     hide : function()
18735     {
18736         if(this.isInline) {
18737             return;
18738         }
18739         this.picker().hide();
18740         this.fireEvent('hide', this, this.date);
18741         
18742     },
18743     
18744     onMousedown: function(e)
18745     {
18746         e.stopPropagation();
18747         e.preventDefault();
18748     },
18749     
18750     keyup: function(e)
18751     {
18752         Roo.bootstrap.MonthField.superclass.keyup.call(this);
18753         this.update();
18754     },
18755
18756     fireKey: function(e)
18757     {
18758         if (!this.picker().isVisible()){
18759             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
18760                 this.show();
18761             }
18762             return;
18763         }
18764         
18765         var dir;
18766         
18767         switch(e.keyCode){
18768             case 27: // escape
18769                 this.hide();
18770                 e.preventDefault();
18771                 break;
18772             case 37: // left
18773             case 39: // right
18774                 dir = e.keyCode == 37 ? -1 : 1;
18775                 
18776                 this.vIndex = this.vIndex + dir;
18777                 
18778                 if(this.vIndex < 0){
18779                     this.vIndex = 0;
18780                 }
18781                 
18782                 if(this.vIndex > 11){
18783                     this.vIndex = 11;
18784                 }
18785                 
18786                 if(isNaN(this.vIndex)){
18787                     this.vIndex = 0;
18788                 }
18789                 
18790                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18791                 
18792                 break;
18793             case 38: // up
18794             case 40: // down
18795                 
18796                 dir = e.keyCode == 38 ? -1 : 1;
18797                 
18798                 this.vIndex = this.vIndex + dir * 4;
18799                 
18800                 if(this.vIndex < 0){
18801                     this.vIndex = 0;
18802                 }
18803                 
18804                 if(this.vIndex > 11){
18805                     this.vIndex = 11;
18806                 }
18807                 
18808                 if(isNaN(this.vIndex)){
18809                     this.vIndex = 0;
18810                 }
18811                 
18812                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18813                 break;
18814                 
18815             case 13: // enter
18816                 
18817                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18818                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18819                 }
18820                 
18821                 this.hide();
18822                 e.preventDefault();
18823                 break;
18824             case 9: // tab
18825                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
18826                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
18827                 }
18828                 this.hide();
18829                 break;
18830             case 16: // shift
18831             case 17: // ctrl
18832             case 18: // alt
18833                 break;
18834             default :
18835                 this.hide();
18836                 
18837         }
18838     },
18839     
18840     remove: function() 
18841     {
18842         this.picker().remove();
18843     }
18844    
18845 });
18846
18847 Roo.apply(Roo.bootstrap.MonthField,  {
18848     
18849     content : {
18850         tag: 'tbody',
18851         cn: [
18852         {
18853             tag: 'tr',
18854             cn: [
18855             {
18856                 tag: 'td',
18857                 colspan: '7'
18858             }
18859             ]
18860         }
18861         ]
18862     },
18863     
18864     dates:{
18865         en: {
18866             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
18867             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
18868         }
18869     }
18870 });
18871
18872 Roo.apply(Roo.bootstrap.MonthField,  {
18873   
18874     template : {
18875         tag: 'div',
18876         cls: 'datepicker dropdown-menu roo-dynamic',
18877         cn: [
18878             {
18879                 tag: 'div',
18880                 cls: 'datepicker-months',
18881                 cn: [
18882                 {
18883                     tag: 'table',
18884                     cls: 'table-condensed',
18885                     cn:[
18886                         Roo.bootstrap.DateField.content
18887                     ]
18888                 }
18889                 ]
18890             }
18891         ]
18892     }
18893 });
18894
18895  
18896
18897  
18898  /*
18899  * - LGPL
18900  *
18901  * CheckBox
18902  * 
18903  */
18904
18905 /**
18906  * @class Roo.bootstrap.CheckBox
18907  * @extends Roo.bootstrap.Input
18908  * Bootstrap CheckBox class
18909  * 
18910  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
18911  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
18912  * @cfg {String} boxLabel The text that appears beside the checkbox
18913  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
18914  * @cfg {Boolean} checked initnal the element
18915  * @cfg {Boolean} inline inline the element (default false)
18916  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
18917  * 
18918  * @constructor
18919  * Create a new CheckBox
18920  * @param {Object} config The config object
18921  */
18922
18923 Roo.bootstrap.CheckBox = function(config){
18924     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
18925    
18926     this.addEvents({
18927         /**
18928         * @event check
18929         * Fires when the element is checked or unchecked.
18930         * @param {Roo.bootstrap.CheckBox} this This input
18931         * @param {Boolean} checked The new checked value
18932         */
18933        check : true
18934     });
18935     
18936 };
18937
18938 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
18939   
18940     inputType: 'checkbox',
18941     inputValue: 1,
18942     valueOff: 0,
18943     boxLabel: false,
18944     checked: false,
18945     weight : false,
18946     inline: false,
18947     
18948     getAutoCreate : function()
18949     {
18950         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
18951         
18952         var id = Roo.id();
18953         
18954         var cfg = {};
18955         
18956         cfg.cls = 'form-group ' + this.inputType; //input-group
18957         
18958         if(this.inline){
18959             cfg.cls += ' ' + this.inputType + '-inline';
18960         }
18961         
18962         var input =  {
18963             tag: 'input',
18964             id : id,
18965             type : this.inputType,
18966             value : this.inputType == 'radio' ? this.inputValue : ((!this.checked) ? this.valueOff : this.inputValue),
18967             cls : 'roo-' + this.inputType, //'form-box',
18968             placeholder : this.placeholder || ''
18969             
18970         };
18971         
18972         if (this.weight) { // Validity check?
18973             cfg.cls += " " + this.inputType + "-" + this.weight;
18974         }
18975         
18976         if (this.disabled) {
18977             input.disabled=true;
18978         }
18979         
18980         if(this.checked){
18981             input.checked = this.checked;
18982         }
18983         
18984         if (this.name) {
18985             input.name = this.name;
18986         }
18987         
18988         if (this.size) {
18989             input.cls += ' input-' + this.size;
18990         }
18991         
18992         var settings=this;
18993         
18994         ['xs','sm','md','lg'].map(function(size){
18995             if (settings[size]) {
18996                 cfg.cls += ' col-' + size + '-' + settings[size];
18997             }
18998         });
18999         
19000         var inputblock = input;
19001          
19002         if (this.before || this.after) {
19003             
19004             inputblock = {
19005                 cls : 'input-group',
19006                 cn :  [] 
19007             };
19008             
19009             if (this.before) {
19010                 inputblock.cn.push({
19011                     tag :'span',
19012                     cls : 'input-group-addon',
19013                     html : this.before
19014                 });
19015             }
19016             
19017             inputblock.cn.push(input);
19018             
19019             if (this.after) {
19020                 inputblock.cn.push({
19021                     tag :'span',
19022                     cls : 'input-group-addon',
19023                     html : this.after
19024                 });
19025             }
19026             
19027         }
19028         
19029         if (align ==='left' && this.fieldLabel.length) {
19030 //                Roo.log("left and has label");
19031                 cfg.cn = [
19032                     
19033                     {
19034                         tag: 'label',
19035                         'for' :  id,
19036                         cls : 'control-label col-md-' + this.labelWidth,
19037                         html : this.fieldLabel
19038                         
19039                     },
19040                     {
19041                         cls : "col-md-" + (12 - this.labelWidth), 
19042                         cn: [
19043                             inputblock
19044                         ]
19045                     }
19046                     
19047                 ];
19048         } else if ( this.fieldLabel.length) {
19049 //                Roo.log(" label");
19050                 cfg.cn = [
19051                    
19052                     {
19053                         tag: this.boxLabel ? 'span' : 'label',
19054                         'for': id,
19055                         cls: 'control-label box-input-label',
19056                         //cls : 'input-group-addon',
19057                         html : this.fieldLabel
19058                         
19059                     },
19060                     
19061                     inputblock
19062                     
19063                 ];
19064
19065         } else {
19066             
19067 //                Roo.log(" no label && no align");
19068                 cfg.cn = [  inputblock ] ;
19069                 
19070                 
19071         }
19072         
19073         if(this.boxLabel){
19074              var boxLabelCfg = {
19075                 tag: 'label',
19076                 //'for': id, // box label is handled by onclick - so no for...
19077                 cls: 'box-label',
19078                 html: this.boxLabel
19079             };
19080             
19081             if(this.tooltip){
19082                 boxLabelCfg.tooltip = this.tooltip;
19083             }
19084              
19085             cfg.cn.push(boxLabelCfg);
19086         }
19087         
19088         
19089        
19090         return cfg;
19091         
19092     },
19093     
19094     /**
19095      * return the real input element.
19096      */
19097     inputEl: function ()
19098     {
19099         return this.el.select('input.roo-' + this.inputType,true).first();
19100     },
19101     
19102     labelEl: function()
19103     {
19104         return this.el.select('label.control-label',true).first();
19105     },
19106     /* depricated... */
19107     
19108     label: function()
19109     {
19110         return this.labelEl();
19111     },
19112     
19113     boxLabelEl: function()
19114     {
19115         return this.el.select('label.box-label',true).first();
19116     },
19117     
19118     initEvents : function()
19119     {
19120 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19121         
19122         this.inputEl().on('click', this.onClick,  this);
19123         
19124         if (this.boxLabel) { 
19125             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
19126         }
19127         
19128         this.startValue = this.getValue();
19129         
19130         if(this.groupId){
19131             Roo.bootstrap.CheckBox.register(this);
19132         }
19133     },
19134     
19135     onClick : function()
19136     {   
19137         this.setChecked(!this.checked);
19138     },
19139     
19140     setChecked : function(state,suppressEvent)
19141     {
19142         this.startValue = this.getValue();
19143         
19144         if(this.inputType == 'radio'){
19145             
19146             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19147                 e.dom.checked = false;
19148             });
19149             
19150             this.inputEl().dom.checked = true;
19151             
19152             this.inputEl().dom.value = this.inputValue;
19153             
19154             if(suppressEvent !== true){
19155                 this.fireEvent('check', this, true);
19156             }
19157             
19158             this.validate();
19159             
19160             return;
19161         }
19162         
19163         this.checked = state;
19164         
19165         this.inputEl().dom.checked = state;
19166         
19167         this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19168         
19169         if(suppressEvent !== true){
19170             this.fireEvent('check', this, state);
19171         }
19172         
19173         this.validate();
19174     },
19175     
19176     getValue : function()
19177     {
19178         if(this.inputType == 'radio'){
19179             return this.getGroupValue();
19180         }
19181         
19182         return this.inputEl().getValue();
19183         
19184     },
19185     
19186     getGroupValue : function()
19187     {
19188         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
19189             return '';
19190         }
19191         
19192         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
19193     },
19194     
19195     setValue : function(v,suppressEvent)
19196     {
19197         if(this.inputType == 'radio'){
19198             this.setGroupValue(v, suppressEvent);
19199             return;
19200         }
19201         
19202         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
19203         
19204         this.validate();
19205     },
19206     
19207     setGroupValue : function(v, suppressEvent)
19208     {
19209         this.startValue = this.getValue();
19210         
19211         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19212             e.dom.checked = false;
19213             
19214             if(e.dom.value == v){
19215                 e.dom.checked = true;
19216             }
19217         });
19218         
19219         if(suppressEvent !== true){
19220             this.fireEvent('check', this, true);
19221         }
19222
19223         this.validate();
19224         
19225         return;
19226     },
19227     
19228     validate : function()
19229     {
19230         if(
19231                 this.disabled || 
19232                 (this.inputType == 'radio' && this.validateRadio()) ||
19233                 (this.inputType == 'checkbox' && this.validateCheckbox())
19234         ){
19235             this.markValid();
19236             return true;
19237         }
19238         
19239         this.markInvalid();
19240         return false;
19241     },
19242     
19243     validateRadio : function()
19244     {
19245         var valid = false;
19246         
19247         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19248             if(!e.dom.checked){
19249                 return;
19250             }
19251             
19252             valid = true;
19253             
19254             return false;
19255         });
19256         
19257         return valid;
19258     },
19259     
19260     validateCheckbox : function()
19261     {
19262         if(!this.groupId){
19263             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
19264         }
19265         
19266         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19267         
19268         if(!group){
19269             return false;
19270         }
19271         
19272         var r = false;
19273         
19274         for(var i in group){
19275             if(r){
19276                 break;
19277             }
19278             
19279             r = (group[i].getValue() == group[i].inputValue) ? true : false;
19280         }
19281         
19282         return r;
19283     },
19284     
19285     /**
19286      * Mark this field as valid
19287      */
19288     markValid : function()
19289     {
19290         if(this.allowBlank){
19291             return;
19292         }
19293         
19294         var _this = this;
19295         
19296         this.fireEvent('valid', this);
19297         
19298         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19299         
19300         if(this.groupId){
19301             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19302         }
19303         
19304         if(label){
19305             label.markValid();
19306         }
19307         
19308         if(this.inputType == 'radio'){
19309             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19310                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19311                 e.findParent('.form-group', false, true).addClass(_this.validClass);
19312             });
19313             
19314             return;
19315         }
19316         
19317         if(!this.groupId){
19318             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19319             this.el.findParent('.form-group', false, true).addClass(this.validClass);
19320             return;
19321         }
19322         
19323         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19324             
19325         if(!group){
19326             return;
19327         }
19328         
19329         for(var i in group){
19330             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19331             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
19332         }
19333     },
19334     
19335      /**
19336      * Mark this field as invalid
19337      * @param {String} msg The validation message
19338      */
19339     markInvalid : function(msg)
19340     {
19341         if(this.allowBlank){
19342             return;
19343         }
19344         
19345         var _this = this;
19346         
19347         this.fireEvent('invalid', this, msg);
19348         
19349         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
19350         
19351         if(this.groupId){
19352             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
19353         }
19354         
19355         if(label){
19356             label.markInvalid();
19357         }
19358             
19359         if(this.inputType == 'radio'){
19360             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
19361                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
19362                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
19363             });
19364             
19365             return;
19366         }
19367         
19368         if(!this.groupId){
19369             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19370             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
19371             return;
19372         }
19373         
19374         var group = Roo.bootstrap.CheckBox.get(this.groupId);
19375         
19376         if(!group){
19377             return;
19378         }
19379         
19380         for(var i in group){
19381             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
19382             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
19383         }
19384         
19385     }
19386     
19387 });
19388
19389 Roo.apply(Roo.bootstrap.CheckBox, {
19390     
19391     groups: {},
19392     
19393      /**
19394     * register a CheckBox Group
19395     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
19396     */
19397     register : function(checkbox)
19398     {
19399         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
19400             this.groups[checkbox.groupId] = {};
19401         }
19402         
19403         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
19404             return;
19405         }
19406         
19407         this.groups[checkbox.groupId][checkbox.name] = checkbox;
19408         
19409     },
19410     /**
19411     * fetch a CheckBox Group based on the group ID
19412     * @param {string} the group ID
19413     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
19414     */
19415     get: function(groupId) {
19416         if (typeof(this.groups[groupId]) == 'undefined') {
19417             return false;
19418         }
19419         
19420         return this.groups[groupId] ;
19421     }
19422     
19423     
19424 });
19425 /*
19426  * - LGPL
19427  *
19428  * Radio
19429  *
19430  *
19431  * not inline
19432  *<div class="radio">
19433   <label>
19434     <input type="radio" name="optionsRadios" id="optionsRadios1" value="option1" checked>
19435     Option one is this and that&mdash;be sure to include why it's great
19436   </label>
19437 </div>
19438  *
19439  *
19440  *inline
19441  *<span>
19442  *<label class="radio-inline">fieldLabel</label>
19443  *<label class="radio-inline">
19444   <input type="radio" name="inlineRadioOptions" id="inlineRadio1" value="option1"> 1
19445 </label>
19446 <span>
19447  * 
19448  * 
19449  */
19450
19451 /**
19452  * @class Roo.bootstrap.Radio
19453  * @extends Roo.bootstrap.CheckBox
19454  * Bootstrap Radio class
19455
19456  * @constructor
19457  * Create a new Radio
19458  * @param {Object} config The config object
19459  */
19460
19461 Roo.bootstrap.Radio = function(config){
19462     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
19463    
19464 };
19465
19466 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.CheckBox,  {
19467     
19468     inputType: 'radio',
19469     inputValue: '',
19470     valueOff: '',
19471     
19472     getAutoCreate : function()
19473     {
19474         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
19475         align = align || 'left'; // default...
19476         
19477         
19478         
19479         var id = Roo.id();
19480         
19481         var cfg = {
19482                 tag : this.inline ? 'span' : 'div',
19483                 cls : '',
19484                 cn : []
19485         };
19486         
19487         var inline = this.inline ? ' radio-inline' : '';
19488         
19489         var lbl = {
19490                 tag: 'label' ,
19491                 // does not need for, as we wrap the input with it..
19492                 'for' : id,
19493                 cls : 'control-label box-label' + inline,
19494                 cn : []
19495         };
19496         var labelWidth = this.labelWidth ? this.labelWidth *1 : 100;
19497         
19498         var fieldLabel = {
19499             tag: 'label' ,
19500             //cls : 'control-label' + inline,
19501             html : this.fieldLabel,
19502             style : 'width:' +  labelWidth  + 'px;line-height:1;vertical-align:bottom;cursor:default;' // should be css really.
19503         };
19504         
19505  
19506         
19507         
19508         var input =  {
19509             tag: 'input',
19510             id : id,
19511             type : this.inputType,
19512             //value : (!this.checked) ? this.valueOff : this.inputValue,
19513             value : this.inputValue,
19514             cls : 'roo-radio',
19515             placeholder : this.placeholder || '' // ?? needed????
19516             
19517         };
19518         if (this.weight) { // Validity check?
19519             input.cls += " radio-" + this.weight;
19520         }
19521         if (this.disabled) {
19522             input.disabled=true;
19523         }
19524         
19525         if(this.checked){
19526             input.checked = this.checked;
19527         }
19528         
19529         if (this.name) {
19530             input.name = this.name;
19531         }
19532         
19533         if (this.size) {
19534             input.cls += ' input-' + this.size;
19535         }
19536         
19537         //?? can span's inline have a width??
19538         
19539         var settings=this;
19540         ['xs','sm','md','lg'].map(function(size){
19541             if (settings[size]) {
19542                 cfg.cls += ' col-' + size + '-' + settings[size];
19543             }
19544         });
19545         
19546         var inputblock = input;
19547         
19548         if (this.before || this.after) {
19549             
19550             inputblock = {
19551                 cls : 'input-group',
19552                 tag : 'span',
19553                 cn :  [] 
19554             };
19555             if (this.before) {
19556                 inputblock.cn.push({
19557                     tag :'span',
19558                     cls : 'input-group-addon',
19559                     html : this.before
19560                 });
19561             }
19562             inputblock.cn.push(input);
19563             if (this.after) {
19564                 inputblock.cn.push({
19565                     tag :'span',
19566                     cls : 'input-group-addon',
19567                     html : this.after
19568                 });
19569             }
19570             
19571         };
19572         
19573         
19574         if (this.fieldLabel && this.fieldLabel.length) {
19575             cfg.cn.push(fieldLabel);
19576         }
19577        
19578         // normal bootstrap puts the input inside the label.
19579         // however with our styled version - it has to go after the input.
19580        
19581         //lbl.cn.push(inputblock);
19582         
19583         var lblwrap =  {
19584             tag: 'span',
19585             cls: 'radio' + inline,
19586             cn: [
19587                 inputblock,
19588                 lbl
19589             ]
19590         };
19591         
19592         cfg.cn.push( lblwrap);
19593         
19594         if(this.boxLabel){
19595             lbl.cn.push({
19596                 tag: 'span',
19597                 html: this.boxLabel
19598             })
19599         }
19600          
19601         
19602         return cfg;
19603         
19604     },
19605     
19606     initEvents : function()
19607     {
19608 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
19609         
19610         this.inputEl().on('click', this.onClick,  this);
19611         if (this.boxLabel) {
19612             //Roo.log('find label');
19613             this.el.select('span.radio label span',true).first().on('click', this.onClick,  this);
19614         }
19615         
19616     },
19617     
19618     inputEl: function ()
19619     {
19620         return this.el.select('input.roo-radio',true).first();
19621     },
19622     onClick : function()
19623     {   
19624         Roo.log("click");
19625         this.setChecked(true);
19626     },
19627     
19628     setChecked : function(state,suppressEvent)
19629     {
19630         if(state){
19631             Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19632                 v.dom.checked = false;
19633             });
19634         }
19635         Roo.log(this.inputEl().dom);
19636         this.checked = state;
19637         this.inputEl().dom.checked = state;
19638         
19639         if(suppressEvent !== true){
19640             this.fireEvent('check', this, state);
19641         }
19642         
19643         //this.inputEl().dom.value = state ? this.inputValue : this.valueOff;
19644         
19645     },
19646     
19647     getGroupValue : function()
19648     {
19649         var value = '';
19650         Roo.each(this.inputEl().up('form').select('input[name='+this.inputEl().dom.name+']', true).elements, function(v){
19651             if(v.dom.checked == true){
19652                 value = v.dom.value;
19653             }
19654         });
19655         
19656         return value;
19657     },
19658     
19659     /**
19660      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
19661      * @return {Mixed} value The field value
19662      */
19663     getValue : function(){
19664         return this.getGroupValue();
19665     }
19666     
19667 });
19668
19669  
19670 //<script type="text/javascript">
19671
19672 /*
19673  * Based  Ext JS Library 1.1.1
19674  * Copyright(c) 2006-2007, Ext JS, LLC.
19675  * LGPL
19676  *
19677  */
19678  
19679 /**
19680  * @class Roo.HtmlEditorCore
19681  * @extends Roo.Component
19682  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
19683  *
19684  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
19685  */
19686
19687 Roo.HtmlEditorCore = function(config){
19688     
19689     
19690     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
19691     
19692     
19693     this.addEvents({
19694         /**
19695          * @event initialize
19696          * Fires when the editor is fully initialized (including the iframe)
19697          * @param {Roo.HtmlEditorCore} this
19698          */
19699         initialize: true,
19700         /**
19701          * @event activate
19702          * Fires when the editor is first receives the focus. Any insertion must wait
19703          * until after this event.
19704          * @param {Roo.HtmlEditorCore} this
19705          */
19706         activate: true,
19707          /**
19708          * @event beforesync
19709          * Fires before the textarea is updated with content from the editor iframe. Return false
19710          * to cancel the sync.
19711          * @param {Roo.HtmlEditorCore} this
19712          * @param {String} html
19713          */
19714         beforesync: true,
19715          /**
19716          * @event beforepush
19717          * Fires before the iframe editor is updated with content from the textarea. Return false
19718          * to cancel the push.
19719          * @param {Roo.HtmlEditorCore} this
19720          * @param {String} html
19721          */
19722         beforepush: true,
19723          /**
19724          * @event sync
19725          * Fires when the textarea is updated with content from the editor iframe.
19726          * @param {Roo.HtmlEditorCore} this
19727          * @param {String} html
19728          */
19729         sync: true,
19730          /**
19731          * @event push
19732          * Fires when the iframe editor is updated with content from the textarea.
19733          * @param {Roo.HtmlEditorCore} this
19734          * @param {String} html
19735          */
19736         push: true,
19737         
19738         /**
19739          * @event editorevent
19740          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
19741          * @param {Roo.HtmlEditorCore} this
19742          */
19743         editorevent: true
19744         
19745     });
19746     
19747     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
19748     
19749     // defaults : white / black...
19750     this.applyBlacklists();
19751     
19752     
19753     
19754 };
19755
19756
19757 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
19758
19759
19760      /**
19761      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
19762      */
19763     
19764     owner : false,
19765     
19766      /**
19767      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
19768      *                        Roo.resizable.
19769      */
19770     resizable : false,
19771      /**
19772      * @cfg {Number} height (in pixels)
19773      */   
19774     height: 300,
19775    /**
19776      * @cfg {Number} width (in pixels)
19777      */   
19778     width: 500,
19779     
19780     /**
19781      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
19782      * 
19783      */
19784     stylesheets: false,
19785     
19786     // id of frame..
19787     frameId: false,
19788     
19789     // private properties
19790     validationEvent : false,
19791     deferHeight: true,
19792     initialized : false,
19793     activated : false,
19794     sourceEditMode : false,
19795     onFocus : Roo.emptyFn,
19796     iframePad:3,
19797     hideMode:'offsets',
19798     
19799     clearUp: true,
19800     
19801     // blacklist + whitelisted elements..
19802     black: false,
19803     white: false,
19804      
19805     
19806
19807     /**
19808      * Protected method that will not generally be called directly. It
19809      * is called when the editor initializes the iframe with HTML contents. Override this method if you
19810      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
19811      */
19812     getDocMarkup : function(){
19813         // body styles..
19814         var st = '';
19815         
19816         // inherit styels from page...?? 
19817         if (this.stylesheets === false) {
19818             
19819             Roo.get(document.head).select('style').each(function(node) {
19820                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19821             });
19822             
19823             Roo.get(document.head).select('link').each(function(node) { 
19824                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
19825             });
19826             
19827         } else if (!this.stylesheets.length) {
19828                 // simple..
19829                 st = '<style type="text/css">' +
19830                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19831                    '</style>';
19832         } else { 
19833             
19834         }
19835         
19836         st +=  '<style type="text/css">' +
19837             'IMG { cursor: pointer } ' +
19838         '</style>';
19839
19840         
19841         return '<html><head>' + st  +
19842             //<style type="text/css">' +
19843             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
19844             //'</style>' +
19845             ' </head><body class="roo-htmleditor-body"></body></html>';
19846     },
19847
19848     // private
19849     onRender : function(ct, position)
19850     {
19851         var _t = this;
19852         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
19853         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
19854         
19855         
19856         this.el.dom.style.border = '0 none';
19857         this.el.dom.setAttribute('tabIndex', -1);
19858         this.el.addClass('x-hidden hide');
19859         
19860         
19861         
19862         if(Roo.isIE){ // fix IE 1px bogus margin
19863             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
19864         }
19865        
19866         
19867         this.frameId = Roo.id();
19868         
19869          
19870         
19871         var iframe = this.owner.wrap.createChild({
19872             tag: 'iframe',
19873             cls: 'form-control', // bootstrap..
19874             id: this.frameId,
19875             name: this.frameId,
19876             frameBorder : 'no',
19877             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
19878         }, this.el
19879         );
19880         
19881         
19882         this.iframe = iframe.dom;
19883
19884          this.assignDocWin();
19885         
19886         this.doc.designMode = 'on';
19887        
19888         this.doc.open();
19889         this.doc.write(this.getDocMarkup());
19890         this.doc.close();
19891
19892         
19893         var task = { // must defer to wait for browser to be ready
19894             run : function(){
19895                 //console.log("run task?" + this.doc.readyState);
19896                 this.assignDocWin();
19897                 if(this.doc.body || this.doc.readyState == 'complete'){
19898                     try {
19899                         this.doc.designMode="on";
19900                     } catch (e) {
19901                         return;
19902                     }
19903                     Roo.TaskMgr.stop(task);
19904                     this.initEditor.defer(10, this);
19905                 }
19906             },
19907             interval : 10,
19908             duration: 10000,
19909             scope: this
19910         };
19911         Roo.TaskMgr.start(task);
19912
19913     },
19914
19915     // private
19916     onResize : function(w, h)
19917     {
19918          Roo.log('resize: ' +w + ',' + h );
19919         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
19920         if(!this.iframe){
19921             return;
19922         }
19923         if(typeof w == 'number'){
19924             
19925             this.iframe.style.width = w + 'px';
19926         }
19927         if(typeof h == 'number'){
19928             
19929             this.iframe.style.height = h + 'px';
19930             if(this.doc){
19931                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
19932             }
19933         }
19934         
19935     },
19936
19937     /**
19938      * Toggles the editor between standard and source edit mode.
19939      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
19940      */
19941     toggleSourceEdit : function(sourceEditMode){
19942         
19943         this.sourceEditMode = sourceEditMode === true;
19944         
19945         if(this.sourceEditMode){
19946  
19947             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
19948             
19949         }else{
19950             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
19951             //this.iframe.className = '';
19952             this.deferFocus();
19953         }
19954         //this.setSize(this.owner.wrap.getSize());
19955         //this.fireEvent('editmodechange', this, this.sourceEditMode);
19956     },
19957
19958     
19959   
19960
19961     /**
19962      * Protected method that will not generally be called directly. If you need/want
19963      * custom HTML cleanup, this is the method you should override.
19964      * @param {String} html The HTML to be cleaned
19965      * return {String} The cleaned HTML
19966      */
19967     cleanHtml : function(html){
19968         html = String(html);
19969         if(html.length > 5){
19970             if(Roo.isSafari){ // strip safari nonsense
19971                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
19972             }
19973         }
19974         if(html == '&nbsp;'){
19975             html = '';
19976         }
19977         return html;
19978     },
19979
19980     /**
19981      * HTML Editor -> Textarea
19982      * Protected method that will not generally be called directly. Syncs the contents
19983      * of the editor iframe with the textarea.
19984      */
19985     syncValue : function(){
19986         if(this.initialized){
19987             var bd = (this.doc.body || this.doc.documentElement);
19988             //this.cleanUpPaste(); -- this is done else where and causes havoc..
19989             var html = bd.innerHTML;
19990             if(Roo.isSafari){
19991                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
19992                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
19993                 if(m && m[1]){
19994                     html = '<div style="'+m[0]+'">' + html + '</div>';
19995                 }
19996             }
19997             html = this.cleanHtml(html);
19998             // fix up the special chars.. normaly like back quotes in word...
19999             // however we do not want to do this with chinese..
20000             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
20001                 var cc = b.charCodeAt();
20002                 if (
20003                     (cc >= 0x4E00 && cc < 0xA000 ) ||
20004                     (cc >= 0x3400 && cc < 0x4E00 ) ||
20005                     (cc >= 0xf900 && cc < 0xfb00 )
20006                 ) {
20007                         return b;
20008                 }
20009                 return "&#"+cc+";" 
20010             });
20011             if(this.owner.fireEvent('beforesync', this, html) !== false){
20012                 this.el.dom.value = html;
20013                 this.owner.fireEvent('sync', this, html);
20014             }
20015         }
20016     },
20017
20018     /**
20019      * Protected method that will not generally be called directly. Pushes the value of the textarea
20020      * into the iframe editor.
20021      */
20022     pushValue : function(){
20023         if(this.initialized){
20024             var v = this.el.dom.value.trim();
20025             
20026 //            if(v.length < 1){
20027 //                v = '&#160;';
20028 //            }
20029             
20030             if(this.owner.fireEvent('beforepush', this, v) !== false){
20031                 var d = (this.doc.body || this.doc.documentElement);
20032                 d.innerHTML = v;
20033                 this.cleanUpPaste();
20034                 this.el.dom.value = d.innerHTML;
20035                 this.owner.fireEvent('push', this, v);
20036             }
20037         }
20038     },
20039
20040     // private
20041     deferFocus : function(){
20042         this.focus.defer(10, this);
20043     },
20044
20045     // doc'ed in Field
20046     focus : function(){
20047         if(this.win && !this.sourceEditMode){
20048             this.win.focus();
20049         }else{
20050             this.el.focus();
20051         }
20052     },
20053     
20054     assignDocWin: function()
20055     {
20056         var iframe = this.iframe;
20057         
20058          if(Roo.isIE){
20059             this.doc = iframe.contentWindow.document;
20060             this.win = iframe.contentWindow;
20061         } else {
20062 //            if (!Roo.get(this.frameId)) {
20063 //                return;
20064 //            }
20065 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20066 //            this.win = Roo.get(this.frameId).dom.contentWindow;
20067             
20068             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
20069                 return;
20070             }
20071             
20072             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
20073             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
20074         }
20075     },
20076     
20077     // private
20078     initEditor : function(){
20079         //console.log("INIT EDITOR");
20080         this.assignDocWin();
20081         
20082         
20083         
20084         this.doc.designMode="on";
20085         this.doc.open();
20086         this.doc.write(this.getDocMarkup());
20087         this.doc.close();
20088         
20089         var dbody = (this.doc.body || this.doc.documentElement);
20090         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
20091         // this copies styles from the containing element into thsi one..
20092         // not sure why we need all of this..
20093         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
20094         
20095         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
20096         //ss['background-attachment'] = 'fixed'; // w3c
20097         dbody.bgProperties = 'fixed'; // ie
20098         //Roo.DomHelper.applyStyles(dbody, ss);
20099         Roo.EventManager.on(this.doc, {
20100             //'mousedown': this.onEditorEvent,
20101             'mouseup': this.onEditorEvent,
20102             'dblclick': this.onEditorEvent,
20103             'click': this.onEditorEvent,
20104             'keyup': this.onEditorEvent,
20105             buffer:100,
20106             scope: this
20107         });
20108         if(Roo.isGecko){
20109             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
20110         }
20111         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
20112             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
20113         }
20114         this.initialized = true;
20115
20116         this.owner.fireEvent('initialize', this);
20117         this.pushValue();
20118     },
20119
20120     // private
20121     onDestroy : function(){
20122         
20123         
20124         
20125         if(this.rendered){
20126             
20127             //for (var i =0; i < this.toolbars.length;i++) {
20128             //    // fixme - ask toolbars for heights?
20129             //    this.toolbars[i].onDestroy();
20130            // }
20131             
20132             //this.wrap.dom.innerHTML = '';
20133             //this.wrap.remove();
20134         }
20135     },
20136
20137     // private
20138     onFirstFocus : function(){
20139         
20140         this.assignDocWin();
20141         
20142         
20143         this.activated = true;
20144          
20145     
20146         if(Roo.isGecko){ // prevent silly gecko errors
20147             this.win.focus();
20148             var s = this.win.getSelection();
20149             if(!s.focusNode || s.focusNode.nodeType != 3){
20150                 var r = s.getRangeAt(0);
20151                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
20152                 r.collapse(true);
20153                 this.deferFocus();
20154             }
20155             try{
20156                 this.execCmd('useCSS', true);
20157                 this.execCmd('styleWithCSS', false);
20158             }catch(e){}
20159         }
20160         this.owner.fireEvent('activate', this);
20161     },
20162
20163     // private
20164     adjustFont: function(btn){
20165         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
20166         //if(Roo.isSafari){ // safari
20167         //    adjust *= 2;
20168        // }
20169         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
20170         if(Roo.isSafari){ // safari
20171             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
20172             v =  (v < 10) ? 10 : v;
20173             v =  (v > 48) ? 48 : v;
20174             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
20175             
20176         }
20177         
20178         
20179         v = Math.max(1, v+adjust);
20180         
20181         this.execCmd('FontSize', v  );
20182     },
20183
20184     onEditorEvent : function(e)
20185     {
20186         this.owner.fireEvent('editorevent', this, e);
20187       //  this.updateToolbar();
20188         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
20189     },
20190
20191     insertTag : function(tg)
20192     {
20193         // could be a bit smarter... -> wrap the current selected tRoo..
20194         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
20195             
20196             range = this.createRange(this.getSelection());
20197             var wrappingNode = this.doc.createElement(tg.toLowerCase());
20198             wrappingNode.appendChild(range.extractContents());
20199             range.insertNode(wrappingNode);
20200
20201             return;
20202             
20203             
20204             
20205         }
20206         this.execCmd("formatblock",   tg);
20207         
20208     },
20209     
20210     insertText : function(txt)
20211     {
20212         
20213         
20214         var range = this.createRange();
20215         range.deleteContents();
20216                //alert(Sender.getAttribute('label'));
20217                
20218         range.insertNode(this.doc.createTextNode(txt));
20219     } ,
20220     
20221      
20222
20223     /**
20224      * Executes a Midas editor command on the editor document and performs necessary focus and
20225      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
20226      * @param {String} cmd The Midas command
20227      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20228      */
20229     relayCmd : function(cmd, value){
20230         this.win.focus();
20231         this.execCmd(cmd, value);
20232         this.owner.fireEvent('editorevent', this);
20233         //this.updateToolbar();
20234         this.owner.deferFocus();
20235     },
20236
20237     /**
20238      * Executes a Midas editor command directly on the editor document.
20239      * For visual commands, you should use {@link #relayCmd} instead.
20240      * <b>This should only be called after the editor is initialized.</b>
20241      * @param {String} cmd The Midas command
20242      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
20243      */
20244     execCmd : function(cmd, value){
20245         this.doc.execCommand(cmd, false, value === undefined ? null : value);
20246         this.syncValue();
20247     },
20248  
20249  
20250    
20251     /**
20252      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
20253      * to insert tRoo.
20254      * @param {String} text | dom node.. 
20255      */
20256     insertAtCursor : function(text)
20257     {
20258         
20259         
20260         
20261         if(!this.activated){
20262             return;
20263         }
20264         /*
20265         if(Roo.isIE){
20266             this.win.focus();
20267             var r = this.doc.selection.createRange();
20268             if(r){
20269                 r.collapse(true);
20270                 r.pasteHTML(text);
20271                 this.syncValue();
20272                 this.deferFocus();
20273             
20274             }
20275             return;
20276         }
20277         */
20278         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
20279             this.win.focus();
20280             
20281             
20282             // from jquery ui (MIT licenced)
20283             var range, node;
20284             var win = this.win;
20285             
20286             if (win.getSelection && win.getSelection().getRangeAt) {
20287                 range = win.getSelection().getRangeAt(0);
20288                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
20289                 range.insertNode(node);
20290             } else if (win.document.selection && win.document.selection.createRange) {
20291                 // no firefox support
20292                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20293                 win.document.selection.createRange().pasteHTML(txt);
20294             } else {
20295                 // no firefox support
20296                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
20297                 this.execCmd('InsertHTML', txt);
20298             } 
20299             
20300             this.syncValue();
20301             
20302             this.deferFocus();
20303         }
20304     },
20305  // private
20306     mozKeyPress : function(e){
20307         if(e.ctrlKey){
20308             var c = e.getCharCode(), cmd;
20309           
20310             if(c > 0){
20311                 c = String.fromCharCode(c).toLowerCase();
20312                 switch(c){
20313                     case 'b':
20314                         cmd = 'bold';
20315                         break;
20316                     case 'i':
20317                         cmd = 'italic';
20318                         break;
20319                     
20320                     case 'u':
20321                         cmd = 'underline';
20322                         break;
20323                     
20324                     case 'v':
20325                         this.cleanUpPaste.defer(100, this);
20326                         return;
20327                         
20328                 }
20329                 if(cmd){
20330                     this.win.focus();
20331                     this.execCmd(cmd);
20332                     this.deferFocus();
20333                     e.preventDefault();
20334                 }
20335                 
20336             }
20337         }
20338     },
20339
20340     // private
20341     fixKeys : function(){ // load time branching for fastest keydown performance
20342         if(Roo.isIE){
20343             return function(e){
20344                 var k = e.getKey(), r;
20345                 if(k == e.TAB){
20346                     e.stopEvent();
20347                     r = this.doc.selection.createRange();
20348                     if(r){
20349                         r.collapse(true);
20350                         r.pasteHTML('&#160;&#160;&#160;&#160;');
20351                         this.deferFocus();
20352                     }
20353                     return;
20354                 }
20355                 
20356                 if(k == e.ENTER){
20357                     r = this.doc.selection.createRange();
20358                     if(r){
20359                         var target = r.parentElement();
20360                         if(!target || target.tagName.toLowerCase() != 'li'){
20361                             e.stopEvent();
20362                             r.pasteHTML('<br />');
20363                             r.collapse(false);
20364                             r.select();
20365                         }
20366                     }
20367                 }
20368                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20369                     this.cleanUpPaste.defer(100, this);
20370                     return;
20371                 }
20372                 
20373                 
20374             };
20375         }else if(Roo.isOpera){
20376             return function(e){
20377                 var k = e.getKey();
20378                 if(k == e.TAB){
20379                     e.stopEvent();
20380                     this.win.focus();
20381                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
20382                     this.deferFocus();
20383                 }
20384                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20385                     this.cleanUpPaste.defer(100, this);
20386                     return;
20387                 }
20388                 
20389             };
20390         }else if(Roo.isSafari){
20391             return function(e){
20392                 var k = e.getKey();
20393                 
20394                 if(k == e.TAB){
20395                     e.stopEvent();
20396                     this.execCmd('InsertText','\t');
20397                     this.deferFocus();
20398                     return;
20399                 }
20400                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
20401                     this.cleanUpPaste.defer(100, this);
20402                     return;
20403                 }
20404                 
20405              };
20406         }
20407     }(),
20408     
20409     getAllAncestors: function()
20410     {
20411         var p = this.getSelectedNode();
20412         var a = [];
20413         if (!p) {
20414             a.push(p); // push blank onto stack..
20415             p = this.getParentElement();
20416         }
20417         
20418         
20419         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
20420             a.push(p);
20421             p = p.parentNode;
20422         }
20423         a.push(this.doc.body);
20424         return a;
20425     },
20426     lastSel : false,
20427     lastSelNode : false,
20428     
20429     
20430     getSelection : function() 
20431     {
20432         this.assignDocWin();
20433         return Roo.isIE ? this.doc.selection : this.win.getSelection();
20434     },
20435     
20436     getSelectedNode: function() 
20437     {
20438         // this may only work on Gecko!!!
20439         
20440         // should we cache this!!!!
20441         
20442         
20443         
20444          
20445         var range = this.createRange(this.getSelection()).cloneRange();
20446         
20447         if (Roo.isIE) {
20448             var parent = range.parentElement();
20449             while (true) {
20450                 var testRange = range.duplicate();
20451                 testRange.moveToElementText(parent);
20452                 if (testRange.inRange(range)) {
20453                     break;
20454                 }
20455                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
20456                     break;
20457                 }
20458                 parent = parent.parentElement;
20459             }
20460             return parent;
20461         }
20462         
20463         // is ancestor a text element.
20464         var ac =  range.commonAncestorContainer;
20465         if (ac.nodeType == 3) {
20466             ac = ac.parentNode;
20467         }
20468         
20469         var ar = ac.childNodes;
20470          
20471         var nodes = [];
20472         var other_nodes = [];
20473         var has_other_nodes = false;
20474         for (var i=0;i<ar.length;i++) {
20475             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
20476                 continue;
20477             }
20478             // fullly contained node.
20479             
20480             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
20481                 nodes.push(ar[i]);
20482                 continue;
20483             }
20484             
20485             // probably selected..
20486             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
20487                 other_nodes.push(ar[i]);
20488                 continue;
20489             }
20490             // outer..
20491             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
20492                 continue;
20493             }
20494             
20495             
20496             has_other_nodes = true;
20497         }
20498         if (!nodes.length && other_nodes.length) {
20499             nodes= other_nodes;
20500         }
20501         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
20502             return false;
20503         }
20504         
20505         return nodes[0];
20506     },
20507     createRange: function(sel)
20508     {
20509         // this has strange effects when using with 
20510         // top toolbar - not sure if it's a great idea.
20511         //this.editor.contentWindow.focus();
20512         if (typeof sel != "undefined") {
20513             try {
20514                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
20515             } catch(e) {
20516                 return this.doc.createRange();
20517             }
20518         } else {
20519             return this.doc.createRange();
20520         }
20521     },
20522     getParentElement: function()
20523     {
20524         
20525         this.assignDocWin();
20526         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
20527         
20528         var range = this.createRange(sel);
20529          
20530         try {
20531             var p = range.commonAncestorContainer;
20532             while (p.nodeType == 3) { // text node
20533                 p = p.parentNode;
20534             }
20535             return p;
20536         } catch (e) {
20537             return null;
20538         }
20539     
20540     },
20541     /***
20542      *
20543      * Range intersection.. the hard stuff...
20544      *  '-1' = before
20545      *  '0' = hits..
20546      *  '1' = after.
20547      *         [ -- selected range --- ]
20548      *   [fail]                        [fail]
20549      *
20550      *    basically..
20551      *      if end is before start or  hits it. fail.
20552      *      if start is after end or hits it fail.
20553      *
20554      *   if either hits (but other is outside. - then it's not 
20555      *   
20556      *    
20557      **/
20558     
20559     
20560     // @see http://www.thismuchiknow.co.uk/?p=64.
20561     rangeIntersectsNode : function(range, node)
20562     {
20563         var nodeRange = node.ownerDocument.createRange();
20564         try {
20565             nodeRange.selectNode(node);
20566         } catch (e) {
20567             nodeRange.selectNodeContents(node);
20568         }
20569     
20570         var rangeStartRange = range.cloneRange();
20571         rangeStartRange.collapse(true);
20572     
20573         var rangeEndRange = range.cloneRange();
20574         rangeEndRange.collapse(false);
20575     
20576         var nodeStartRange = nodeRange.cloneRange();
20577         nodeStartRange.collapse(true);
20578     
20579         var nodeEndRange = nodeRange.cloneRange();
20580         nodeEndRange.collapse(false);
20581     
20582         return rangeStartRange.compareBoundaryPoints(
20583                  Range.START_TO_START, nodeEndRange) == -1 &&
20584                rangeEndRange.compareBoundaryPoints(
20585                  Range.START_TO_START, nodeStartRange) == 1;
20586         
20587          
20588     },
20589     rangeCompareNode : function(range, node)
20590     {
20591         var nodeRange = node.ownerDocument.createRange();
20592         try {
20593             nodeRange.selectNode(node);
20594         } catch (e) {
20595             nodeRange.selectNodeContents(node);
20596         }
20597         
20598         
20599         range.collapse(true);
20600     
20601         nodeRange.collapse(true);
20602      
20603         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
20604         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
20605          
20606         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
20607         
20608         var nodeIsBefore   =  ss == 1;
20609         var nodeIsAfter    = ee == -1;
20610         
20611         if (nodeIsBefore && nodeIsAfter) {
20612             return 0; // outer
20613         }
20614         if (!nodeIsBefore && nodeIsAfter) {
20615             return 1; //right trailed.
20616         }
20617         
20618         if (nodeIsBefore && !nodeIsAfter) {
20619             return 2;  // left trailed.
20620         }
20621         // fully contined.
20622         return 3;
20623     },
20624
20625     // private? - in a new class?
20626     cleanUpPaste :  function()
20627     {
20628         // cleans up the whole document..
20629         Roo.log('cleanuppaste');
20630         
20631         this.cleanUpChildren(this.doc.body);
20632         var clean = this.cleanWordChars(this.doc.body.innerHTML);
20633         if (clean != this.doc.body.innerHTML) {
20634             this.doc.body.innerHTML = clean;
20635         }
20636         
20637     },
20638     
20639     cleanWordChars : function(input) {// change the chars to hex code
20640         var he = Roo.HtmlEditorCore;
20641         
20642         var output = input;
20643         Roo.each(he.swapCodes, function(sw) { 
20644             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
20645             
20646             output = output.replace(swapper, sw[1]);
20647         });
20648         
20649         return output;
20650     },
20651     
20652     
20653     cleanUpChildren : function (n)
20654     {
20655         if (!n.childNodes.length) {
20656             return;
20657         }
20658         for (var i = n.childNodes.length-1; i > -1 ; i--) {
20659            this.cleanUpChild(n.childNodes[i]);
20660         }
20661     },
20662     
20663     
20664         
20665     
20666     cleanUpChild : function (node)
20667     {
20668         var ed = this;
20669         //console.log(node);
20670         if (node.nodeName == "#text") {
20671             // clean up silly Windows -- stuff?
20672             return; 
20673         }
20674         if (node.nodeName == "#comment") {
20675             node.parentNode.removeChild(node);
20676             // clean up silly Windows -- stuff?
20677             return; 
20678         }
20679         var lcname = node.tagName.toLowerCase();
20680         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
20681         // whitelist of tags..
20682         
20683         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
20684             // remove node.
20685             node.parentNode.removeChild(node);
20686             return;
20687             
20688         }
20689         
20690         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
20691         
20692         // remove <a name=....> as rendering on yahoo mailer is borked with this.
20693         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
20694         
20695         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
20696         //    remove_keep_children = true;
20697         //}
20698         
20699         if (remove_keep_children) {
20700             this.cleanUpChildren(node);
20701             // inserts everything just before this node...
20702             while (node.childNodes.length) {
20703                 var cn = node.childNodes[0];
20704                 node.removeChild(cn);
20705                 node.parentNode.insertBefore(cn, node);
20706             }
20707             node.parentNode.removeChild(node);
20708             return;
20709         }
20710         
20711         if (!node.attributes || !node.attributes.length) {
20712             this.cleanUpChildren(node);
20713             return;
20714         }
20715         
20716         function cleanAttr(n,v)
20717         {
20718             
20719             if (v.match(/^\./) || v.match(/^\//)) {
20720                 return;
20721             }
20722             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
20723                 return;
20724             }
20725             if (v.match(/^#/)) {
20726                 return;
20727             }
20728 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
20729             node.removeAttribute(n);
20730             
20731         }
20732         
20733         var cwhite = this.cwhite;
20734         var cblack = this.cblack;
20735             
20736         function cleanStyle(n,v)
20737         {
20738             if (v.match(/expression/)) { //XSS?? should we even bother..
20739                 node.removeAttribute(n);
20740                 return;
20741             }
20742             
20743             var parts = v.split(/;/);
20744             var clean = [];
20745             
20746             Roo.each(parts, function(p) {
20747                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
20748                 if (!p.length) {
20749                     return true;
20750                 }
20751                 var l = p.split(':').shift().replace(/\s+/g,'');
20752                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
20753                 
20754                 if ( cwhite.length && cblack.indexOf(l) > -1) {
20755 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20756                     //node.removeAttribute(n);
20757                     return true;
20758                 }
20759                 //Roo.log()
20760                 // only allow 'c whitelisted system attributes'
20761                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
20762 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
20763                     //node.removeAttribute(n);
20764                     return true;
20765                 }
20766                 
20767                 
20768                  
20769                 
20770                 clean.push(p);
20771                 return true;
20772             });
20773             if (clean.length) { 
20774                 node.setAttribute(n, clean.join(';'));
20775             } else {
20776                 node.removeAttribute(n);
20777             }
20778             
20779         }
20780         
20781         
20782         for (var i = node.attributes.length-1; i > -1 ; i--) {
20783             var a = node.attributes[i];
20784             //console.log(a);
20785             
20786             if (a.name.toLowerCase().substr(0,2)=='on')  {
20787                 node.removeAttribute(a.name);
20788                 continue;
20789             }
20790             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
20791                 node.removeAttribute(a.name);
20792                 continue;
20793             }
20794             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
20795                 cleanAttr(a.name,a.value); // fixme..
20796                 continue;
20797             }
20798             if (a.name == 'style') {
20799                 cleanStyle(a.name,a.value);
20800                 continue;
20801             }
20802             /// clean up MS crap..
20803             // tecnically this should be a list of valid class'es..
20804             
20805             
20806             if (a.name == 'class') {
20807                 if (a.value.match(/^Mso/)) {
20808                     node.className = '';
20809                 }
20810                 
20811                 if (a.value.match(/body/)) {
20812                     node.className = '';
20813                 }
20814                 continue;
20815             }
20816             
20817             // style cleanup!?
20818             // class cleanup?
20819             
20820         }
20821         
20822         
20823         this.cleanUpChildren(node);
20824         
20825         
20826     },
20827     
20828     /**
20829      * Clean up MS wordisms...
20830      */
20831     cleanWord : function(node)
20832     {
20833         
20834         
20835         if (!node) {
20836             this.cleanWord(this.doc.body);
20837             return;
20838         }
20839         if (node.nodeName == "#text") {
20840             // clean up silly Windows -- stuff?
20841             return; 
20842         }
20843         if (node.nodeName == "#comment") {
20844             node.parentNode.removeChild(node);
20845             // clean up silly Windows -- stuff?
20846             return; 
20847         }
20848         
20849         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
20850             node.parentNode.removeChild(node);
20851             return;
20852         }
20853         
20854         // remove - but keep children..
20855         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
20856             while (node.childNodes.length) {
20857                 var cn = node.childNodes[0];
20858                 node.removeChild(cn);
20859                 node.parentNode.insertBefore(cn, node);
20860             }
20861             node.parentNode.removeChild(node);
20862             this.iterateChildren(node, this.cleanWord);
20863             return;
20864         }
20865         // clean styles
20866         if (node.className.length) {
20867             
20868             var cn = node.className.split(/\W+/);
20869             var cna = [];
20870             Roo.each(cn, function(cls) {
20871                 if (cls.match(/Mso[a-zA-Z]+/)) {
20872                     return;
20873                 }
20874                 cna.push(cls);
20875             });
20876             node.className = cna.length ? cna.join(' ') : '';
20877             if (!cna.length) {
20878                 node.removeAttribute("class");
20879             }
20880         }
20881         
20882         if (node.hasAttribute("lang")) {
20883             node.removeAttribute("lang");
20884         }
20885         
20886         if (node.hasAttribute("style")) {
20887             
20888             var styles = node.getAttribute("style").split(";");
20889             var nstyle = [];
20890             Roo.each(styles, function(s) {
20891                 if (!s.match(/:/)) {
20892                     return;
20893                 }
20894                 var kv = s.split(":");
20895                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
20896                     return;
20897                 }
20898                 // what ever is left... we allow.
20899                 nstyle.push(s);
20900             });
20901             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20902             if (!nstyle.length) {
20903                 node.removeAttribute('style');
20904             }
20905         }
20906         this.iterateChildren(node, this.cleanWord);
20907         
20908         
20909         
20910     },
20911     /**
20912      * iterateChildren of a Node, calling fn each time, using this as the scole..
20913      * @param {DomNode} node node to iterate children of.
20914      * @param {Function} fn method of this class to call on each item.
20915      */
20916     iterateChildren : function(node, fn)
20917     {
20918         if (!node.childNodes.length) {
20919                 return;
20920         }
20921         for (var i = node.childNodes.length-1; i > -1 ; i--) {
20922            fn.call(this, node.childNodes[i])
20923         }
20924     },
20925     
20926     
20927     /**
20928      * cleanTableWidths.
20929      *
20930      * Quite often pasting from word etc.. results in tables with column and widths.
20931      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
20932      *
20933      */
20934     cleanTableWidths : function(node)
20935     {
20936          
20937          
20938         if (!node) {
20939             this.cleanTableWidths(this.doc.body);
20940             return;
20941         }
20942         
20943         // ignore list...
20944         if (node.nodeName == "#text" || node.nodeName == "#comment") {
20945             return; 
20946         }
20947         Roo.log(node.tagName);
20948         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
20949             this.iterateChildren(node, this.cleanTableWidths);
20950             return;
20951         }
20952         if (node.hasAttribute('width')) {
20953             node.removeAttribute('width');
20954         }
20955         
20956          
20957         if (node.hasAttribute("style")) {
20958             // pretty basic...
20959             
20960             var styles = node.getAttribute("style").split(";");
20961             var nstyle = [];
20962             Roo.each(styles, function(s) {
20963                 if (!s.match(/:/)) {
20964                     return;
20965                 }
20966                 var kv = s.split(":");
20967                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
20968                     return;
20969                 }
20970                 // what ever is left... we allow.
20971                 nstyle.push(s);
20972             });
20973             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
20974             if (!nstyle.length) {
20975                 node.removeAttribute('style');
20976             }
20977         }
20978         
20979         this.iterateChildren(node, this.cleanTableWidths);
20980         
20981         
20982     },
20983     
20984     
20985     
20986     
20987     domToHTML : function(currentElement, depth, nopadtext) {
20988         
20989         depth = depth || 0;
20990         nopadtext = nopadtext || false;
20991     
20992         if (!currentElement) {
20993             return this.domToHTML(this.doc.body);
20994         }
20995         
20996         //Roo.log(currentElement);
20997         var j;
20998         var allText = false;
20999         var nodeName = currentElement.nodeName;
21000         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
21001         
21002         if  (nodeName == '#text') {
21003             
21004             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
21005         }
21006         
21007         
21008         var ret = '';
21009         if (nodeName != 'BODY') {
21010              
21011             var i = 0;
21012             // Prints the node tagName, such as <A>, <IMG>, etc
21013             if (tagName) {
21014                 var attr = [];
21015                 for(i = 0; i < currentElement.attributes.length;i++) {
21016                     // quoting?
21017                     var aname = currentElement.attributes.item(i).name;
21018                     if (!currentElement.attributes.item(i).value.length) {
21019                         continue;
21020                     }
21021                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
21022                 }
21023                 
21024                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
21025             } 
21026             else {
21027                 
21028                 // eack
21029             }
21030         } else {
21031             tagName = false;
21032         }
21033         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
21034             return ret;
21035         }
21036         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
21037             nopadtext = true;
21038         }
21039         
21040         
21041         // Traverse the tree
21042         i = 0;
21043         var currentElementChild = currentElement.childNodes.item(i);
21044         var allText = true;
21045         var innerHTML  = '';
21046         lastnode = '';
21047         while (currentElementChild) {
21048             // Formatting code (indent the tree so it looks nice on the screen)
21049             var nopad = nopadtext;
21050             if (lastnode == 'SPAN') {
21051                 nopad  = true;
21052             }
21053             // text
21054             if  (currentElementChild.nodeName == '#text') {
21055                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
21056                 toadd = nopadtext ? toadd : toadd.trim();
21057                 if (!nopad && toadd.length > 80) {
21058                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
21059                 }
21060                 innerHTML  += toadd;
21061                 
21062                 i++;
21063                 currentElementChild = currentElement.childNodes.item(i);
21064                 lastNode = '';
21065                 continue;
21066             }
21067             allText = false;
21068             
21069             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
21070                 
21071             // Recursively traverse the tree structure of the child node
21072             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
21073             lastnode = currentElementChild.nodeName;
21074             i++;
21075             currentElementChild=currentElement.childNodes.item(i);
21076         }
21077         
21078         ret += innerHTML;
21079         
21080         if (!allText) {
21081                 // The remaining code is mostly for formatting the tree
21082             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
21083         }
21084         
21085         
21086         if (tagName) {
21087             ret+= "</"+tagName+">";
21088         }
21089         return ret;
21090         
21091     },
21092         
21093     applyBlacklists : function()
21094     {
21095         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
21096         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
21097         
21098         this.white = [];
21099         this.black = [];
21100         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
21101             if (b.indexOf(tag) > -1) {
21102                 return;
21103             }
21104             this.white.push(tag);
21105             
21106         }, this);
21107         
21108         Roo.each(w, function(tag) {
21109             if (b.indexOf(tag) > -1) {
21110                 return;
21111             }
21112             if (this.white.indexOf(tag) > -1) {
21113                 return;
21114             }
21115             this.white.push(tag);
21116             
21117         }, this);
21118         
21119         
21120         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
21121             if (w.indexOf(tag) > -1) {
21122                 return;
21123             }
21124             this.black.push(tag);
21125             
21126         }, this);
21127         
21128         Roo.each(b, function(tag) {
21129             if (w.indexOf(tag) > -1) {
21130                 return;
21131             }
21132             if (this.black.indexOf(tag) > -1) {
21133                 return;
21134             }
21135             this.black.push(tag);
21136             
21137         }, this);
21138         
21139         
21140         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
21141         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
21142         
21143         this.cwhite = [];
21144         this.cblack = [];
21145         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
21146             if (b.indexOf(tag) > -1) {
21147                 return;
21148             }
21149             this.cwhite.push(tag);
21150             
21151         }, this);
21152         
21153         Roo.each(w, function(tag) {
21154             if (b.indexOf(tag) > -1) {
21155                 return;
21156             }
21157             if (this.cwhite.indexOf(tag) > -1) {
21158                 return;
21159             }
21160             this.cwhite.push(tag);
21161             
21162         }, this);
21163         
21164         
21165         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
21166             if (w.indexOf(tag) > -1) {
21167                 return;
21168             }
21169             this.cblack.push(tag);
21170             
21171         }, this);
21172         
21173         Roo.each(b, function(tag) {
21174             if (w.indexOf(tag) > -1) {
21175                 return;
21176             }
21177             if (this.cblack.indexOf(tag) > -1) {
21178                 return;
21179             }
21180             this.cblack.push(tag);
21181             
21182         }, this);
21183     },
21184     
21185     setStylesheets : function(stylesheets)
21186     {
21187         if(typeof(stylesheets) == 'string'){
21188             Roo.get(this.iframe.contentDocument.head).createChild({
21189                 tag : 'link',
21190                 rel : 'stylesheet',
21191                 type : 'text/css',
21192                 href : stylesheets
21193             });
21194             
21195             return;
21196         }
21197         var _this = this;
21198      
21199         Roo.each(stylesheets, function(s) {
21200             if(!s.length){
21201                 return;
21202             }
21203             
21204             Roo.get(_this.iframe.contentDocument.head).createChild({
21205                 tag : 'link',
21206                 rel : 'stylesheet',
21207                 type : 'text/css',
21208                 href : s
21209             });
21210         });
21211
21212         
21213     },
21214     
21215     removeStylesheets : function()
21216     {
21217         var _this = this;
21218         
21219         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
21220             s.remove();
21221         });
21222     }
21223     
21224     // hide stuff that is not compatible
21225     /**
21226      * @event blur
21227      * @hide
21228      */
21229     /**
21230      * @event change
21231      * @hide
21232      */
21233     /**
21234      * @event focus
21235      * @hide
21236      */
21237     /**
21238      * @event specialkey
21239      * @hide
21240      */
21241     /**
21242      * @cfg {String} fieldClass @hide
21243      */
21244     /**
21245      * @cfg {String} focusClass @hide
21246      */
21247     /**
21248      * @cfg {String} autoCreate @hide
21249      */
21250     /**
21251      * @cfg {String} inputType @hide
21252      */
21253     /**
21254      * @cfg {String} invalidClass @hide
21255      */
21256     /**
21257      * @cfg {String} invalidText @hide
21258      */
21259     /**
21260      * @cfg {String} msgFx @hide
21261      */
21262     /**
21263      * @cfg {String} validateOnBlur @hide
21264      */
21265 });
21266
21267 Roo.HtmlEditorCore.white = [
21268         'area', 'br', 'img', 'input', 'hr', 'wbr',
21269         
21270        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
21271        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
21272        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
21273        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
21274        'table',   'ul',         'xmp', 
21275        
21276        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
21277       'thead',   'tr', 
21278      
21279       'dir', 'menu', 'ol', 'ul', 'dl',
21280        
21281       'embed',  'object'
21282 ];
21283
21284
21285 Roo.HtmlEditorCore.black = [
21286     //    'embed',  'object', // enable - backend responsiblity to clean thiese
21287         'applet', // 
21288         'base',   'basefont', 'bgsound', 'blink',  'body', 
21289         'frame',  'frameset', 'head',    'html',   'ilayer', 
21290         'iframe', 'layer',  'link',     'meta',    'object',   
21291         'script', 'style' ,'title',  'xml' // clean later..
21292 ];
21293 Roo.HtmlEditorCore.clean = [
21294     'script', 'style', 'title', 'xml'
21295 ];
21296 Roo.HtmlEditorCore.remove = [
21297     'font'
21298 ];
21299 // attributes..
21300
21301 Roo.HtmlEditorCore.ablack = [
21302     'on'
21303 ];
21304     
21305 Roo.HtmlEditorCore.aclean = [ 
21306     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
21307 ];
21308
21309 // protocols..
21310 Roo.HtmlEditorCore.pwhite= [
21311         'http',  'https',  'mailto'
21312 ];
21313
21314 // white listed style attributes.
21315 Roo.HtmlEditorCore.cwhite= [
21316       //  'text-align', /// default is to allow most things..
21317       
21318          
21319 //        'font-size'//??
21320 ];
21321
21322 // black listed style attributes.
21323 Roo.HtmlEditorCore.cblack= [
21324       //  'font-size' -- this can be set by the project 
21325 ];
21326
21327
21328 Roo.HtmlEditorCore.swapCodes   =[ 
21329     [    8211, "--" ], 
21330     [    8212, "--" ], 
21331     [    8216,  "'" ],  
21332     [    8217, "'" ],  
21333     [    8220, '"' ],  
21334     [    8221, '"' ],  
21335     [    8226, "*" ],  
21336     [    8230, "..." ]
21337 ]; 
21338
21339     /*
21340  * - LGPL
21341  *
21342  * HtmlEditor
21343  * 
21344  */
21345
21346 /**
21347  * @class Roo.bootstrap.HtmlEditor
21348  * @extends Roo.bootstrap.TextArea
21349  * Bootstrap HtmlEditor class
21350
21351  * @constructor
21352  * Create a new HtmlEditor
21353  * @param {Object} config The config object
21354  */
21355
21356 Roo.bootstrap.HtmlEditor = function(config){
21357     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
21358     if (!this.toolbars) {
21359         this.toolbars = [];
21360     }
21361     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
21362     this.addEvents({
21363             /**
21364              * @event initialize
21365              * Fires when the editor is fully initialized (including the iframe)
21366              * @param {HtmlEditor} this
21367              */
21368             initialize: true,
21369             /**
21370              * @event activate
21371              * Fires when the editor is first receives the focus. Any insertion must wait
21372              * until after this event.
21373              * @param {HtmlEditor} this
21374              */
21375             activate: true,
21376              /**
21377              * @event beforesync
21378              * Fires before the textarea is updated with content from the editor iframe. Return false
21379              * to cancel the sync.
21380              * @param {HtmlEditor} this
21381              * @param {String} html
21382              */
21383             beforesync: true,
21384              /**
21385              * @event beforepush
21386              * Fires before the iframe editor is updated with content from the textarea. Return false
21387              * to cancel the push.
21388              * @param {HtmlEditor} this
21389              * @param {String} html
21390              */
21391             beforepush: true,
21392              /**
21393              * @event sync
21394              * Fires when the textarea is updated with content from the editor iframe.
21395              * @param {HtmlEditor} this
21396              * @param {String} html
21397              */
21398             sync: true,
21399              /**
21400              * @event push
21401              * Fires when the iframe editor is updated with content from the textarea.
21402              * @param {HtmlEditor} this
21403              * @param {String} html
21404              */
21405             push: true,
21406              /**
21407              * @event editmodechange
21408              * Fires when the editor switches edit modes
21409              * @param {HtmlEditor} this
21410              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
21411              */
21412             editmodechange: true,
21413             /**
21414              * @event editorevent
21415              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21416              * @param {HtmlEditor} this
21417              */
21418             editorevent: true,
21419             /**
21420              * @event firstfocus
21421              * Fires when on first focus - needed by toolbars..
21422              * @param {HtmlEditor} this
21423              */
21424             firstfocus: true,
21425             /**
21426              * @event autosave
21427              * Auto save the htmlEditor value as a file into Events
21428              * @param {HtmlEditor} this
21429              */
21430             autosave: true,
21431             /**
21432              * @event savedpreview
21433              * preview the saved version of htmlEditor
21434              * @param {HtmlEditor} this
21435              */
21436             savedpreview: true
21437         });
21438 };
21439
21440
21441 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
21442     
21443     
21444       /**
21445      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
21446      */
21447     toolbars : false,
21448    
21449      /**
21450      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21451      *                        Roo.resizable.
21452      */
21453     resizable : false,
21454      /**
21455      * @cfg {Number} height (in pixels)
21456      */   
21457     height: 300,
21458    /**
21459      * @cfg {Number} width (in pixels)
21460      */   
21461     width: false,
21462     
21463     /**
21464      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21465      * 
21466      */
21467     stylesheets: false,
21468     
21469     // id of frame..
21470     frameId: false,
21471     
21472     // private properties
21473     validationEvent : false,
21474     deferHeight: true,
21475     initialized : false,
21476     activated : false,
21477     
21478     onFocus : Roo.emptyFn,
21479     iframePad:3,
21480     hideMode:'offsets',
21481     
21482     
21483     tbContainer : false,
21484     
21485     toolbarContainer :function() {
21486         return this.wrap.select('.x-html-editor-tb',true).first();
21487     },
21488
21489     /**
21490      * Protected method that will not generally be called directly. It
21491      * is called when the editor creates its toolbar. Override this method if you need to
21492      * add custom toolbar buttons.
21493      * @param {HtmlEditor} editor
21494      */
21495     createToolbar : function(){
21496         
21497         Roo.log("create toolbars");
21498         
21499         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
21500         this.toolbars[0].render(this.toolbarContainer());
21501         
21502         return;
21503         
21504 //        if (!editor.toolbars || !editor.toolbars.length) {
21505 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
21506 //        }
21507 //        
21508 //        for (var i =0 ; i < editor.toolbars.length;i++) {
21509 //            editor.toolbars[i] = Roo.factory(
21510 //                    typeof(editor.toolbars[i]) == 'string' ?
21511 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
21512 //                Roo.bootstrap.HtmlEditor);
21513 //            editor.toolbars[i].init(editor);
21514 //        }
21515     },
21516
21517      
21518     // private
21519     onRender : function(ct, position)
21520     {
21521        // Roo.log("Call onRender: " + this.xtype);
21522         var _t = this;
21523         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
21524       
21525         this.wrap = this.inputEl().wrap({
21526             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
21527         });
21528         
21529         this.editorcore.onRender(ct, position);
21530          
21531         if (this.resizable) {
21532             this.resizeEl = new Roo.Resizable(this.wrap, {
21533                 pinned : true,
21534                 wrap: true,
21535                 dynamic : true,
21536                 minHeight : this.height,
21537                 height: this.height,
21538                 handles : this.resizable,
21539                 width: this.width,
21540                 listeners : {
21541                     resize : function(r, w, h) {
21542                         _t.onResize(w,h); // -something
21543                     }
21544                 }
21545             });
21546             
21547         }
21548         this.createToolbar(this);
21549        
21550         
21551         if(!this.width && this.resizable){
21552             this.setSize(this.wrap.getSize());
21553         }
21554         if (this.resizeEl) {
21555             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
21556             // should trigger onReize..
21557         }
21558         
21559     },
21560
21561     // private
21562     onResize : function(w, h)
21563     {
21564         Roo.log('resize: ' +w + ',' + h );
21565         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
21566         var ew = false;
21567         var eh = false;
21568         
21569         if(this.inputEl() ){
21570             if(typeof w == 'number'){
21571                 var aw = w - this.wrap.getFrameWidth('lr');
21572                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
21573                 ew = aw;
21574             }
21575             if(typeof h == 'number'){
21576                  var tbh = -11;  // fixme it needs to tool bar size!
21577                 for (var i =0; i < this.toolbars.length;i++) {
21578                     // fixme - ask toolbars for heights?
21579                     tbh += this.toolbars[i].el.getHeight();
21580                     //if (this.toolbars[i].footer) {
21581                     //    tbh += this.toolbars[i].footer.el.getHeight();
21582                     //}
21583                 }
21584               
21585                 
21586                 
21587                 
21588                 
21589                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
21590                 ah -= 5; // knock a few pixes off for look..
21591                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
21592                 var eh = ah;
21593             }
21594         }
21595         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
21596         this.editorcore.onResize(ew,eh);
21597         
21598     },
21599
21600     /**
21601      * Toggles the editor between standard and source edit mode.
21602      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21603      */
21604     toggleSourceEdit : function(sourceEditMode)
21605     {
21606         this.editorcore.toggleSourceEdit(sourceEditMode);
21607         
21608         if(this.editorcore.sourceEditMode){
21609             Roo.log('editor - showing textarea');
21610             
21611 //            Roo.log('in');
21612 //            Roo.log(this.syncValue());
21613             this.syncValue();
21614             this.inputEl().removeClass(['hide', 'x-hidden']);
21615             this.inputEl().dom.removeAttribute('tabIndex');
21616             this.inputEl().focus();
21617         }else{
21618             Roo.log('editor - hiding textarea');
21619 //            Roo.log('out')
21620 //            Roo.log(this.pushValue()); 
21621             this.pushValue();
21622             
21623             this.inputEl().addClass(['hide', 'x-hidden']);
21624             this.inputEl().dom.setAttribute('tabIndex', -1);
21625             //this.deferFocus();
21626         }
21627          
21628         if(this.resizable){
21629             this.setSize(this.wrap.getSize());
21630         }
21631         
21632         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
21633     },
21634  
21635     // private (for BoxComponent)
21636     adjustSize : Roo.BoxComponent.prototype.adjustSize,
21637
21638     // private (for BoxComponent)
21639     getResizeEl : function(){
21640         return this.wrap;
21641     },
21642
21643     // private (for BoxComponent)
21644     getPositionEl : function(){
21645         return this.wrap;
21646     },
21647
21648     // private
21649     initEvents : function(){
21650         this.originalValue = this.getValue();
21651     },
21652
21653 //    /**
21654 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21655 //     * @method
21656 //     */
21657 //    markInvalid : Roo.emptyFn,
21658 //    /**
21659 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
21660 //     * @method
21661 //     */
21662 //    clearInvalid : Roo.emptyFn,
21663
21664     setValue : function(v){
21665         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
21666         this.editorcore.pushValue();
21667     },
21668
21669      
21670     // private
21671     deferFocus : function(){
21672         this.focus.defer(10, this);
21673     },
21674
21675     // doc'ed in Field
21676     focus : function(){
21677         this.editorcore.focus();
21678         
21679     },
21680       
21681
21682     // private
21683     onDestroy : function(){
21684         
21685         
21686         
21687         if(this.rendered){
21688             
21689             for (var i =0; i < this.toolbars.length;i++) {
21690                 // fixme - ask toolbars for heights?
21691                 this.toolbars[i].onDestroy();
21692             }
21693             
21694             this.wrap.dom.innerHTML = '';
21695             this.wrap.remove();
21696         }
21697     },
21698
21699     // private
21700     onFirstFocus : function(){
21701         //Roo.log("onFirstFocus");
21702         this.editorcore.onFirstFocus();
21703          for (var i =0; i < this.toolbars.length;i++) {
21704             this.toolbars[i].onFirstFocus();
21705         }
21706         
21707     },
21708     
21709     // private
21710     syncValue : function()
21711     {   
21712         this.editorcore.syncValue();
21713     },
21714     
21715     pushValue : function()
21716     {   
21717         this.editorcore.pushValue();
21718     }
21719      
21720     
21721     // hide stuff that is not compatible
21722     /**
21723      * @event blur
21724      * @hide
21725      */
21726     /**
21727      * @event change
21728      * @hide
21729      */
21730     /**
21731      * @event focus
21732      * @hide
21733      */
21734     /**
21735      * @event specialkey
21736      * @hide
21737      */
21738     /**
21739      * @cfg {String} fieldClass @hide
21740      */
21741     /**
21742      * @cfg {String} focusClass @hide
21743      */
21744     /**
21745      * @cfg {String} autoCreate @hide
21746      */
21747     /**
21748      * @cfg {String} inputType @hide
21749      */
21750     /**
21751      * @cfg {String} invalidClass @hide
21752      */
21753     /**
21754      * @cfg {String} invalidText @hide
21755      */
21756     /**
21757      * @cfg {String} msgFx @hide
21758      */
21759     /**
21760      * @cfg {String} validateOnBlur @hide
21761      */
21762 });
21763  
21764     
21765    
21766    
21767    
21768       
21769 Roo.namespace('Roo.bootstrap.htmleditor');
21770 /**
21771  * @class Roo.bootstrap.HtmlEditorToolbar1
21772  * Basic Toolbar
21773  * 
21774  * Usage:
21775  *
21776  new Roo.bootstrap.HtmlEditor({
21777     ....
21778     toolbars : [
21779         new Roo.bootstrap.HtmlEditorToolbar1({
21780             disable : { fonts: 1 , format: 1, ..., ... , ...],
21781             btns : [ .... ]
21782         })
21783     }
21784      
21785  * 
21786  * @cfg {Object} disable List of elements to disable..
21787  * @cfg {Array} btns List of additional buttons.
21788  * 
21789  * 
21790  * NEEDS Extra CSS? 
21791  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
21792  */
21793  
21794 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
21795 {
21796     
21797     Roo.apply(this, config);
21798     
21799     // default disabled, based on 'good practice'..
21800     this.disable = this.disable || {};
21801     Roo.applyIf(this.disable, {
21802         fontSize : true,
21803         colors : true,
21804         specialElements : true
21805     });
21806     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
21807     
21808     this.editor = config.editor;
21809     this.editorcore = config.editor.editorcore;
21810     
21811     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
21812     
21813     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
21814     // dont call parent... till later.
21815 }
21816 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
21817      
21818     bar : true,
21819     
21820     editor : false,
21821     editorcore : false,
21822     
21823     
21824     formats : [
21825         "p" ,  
21826         "h1","h2","h3","h4","h5","h6", 
21827         "pre", "code", 
21828         "abbr", "acronym", "address", "cite", "samp", "var",
21829         'div','span'
21830     ],
21831     
21832     onRender : function(ct, position)
21833     {
21834        // Roo.log("Call onRender: " + this.xtype);
21835         
21836        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
21837        Roo.log(this.el);
21838        this.el.dom.style.marginBottom = '0';
21839        var _this = this;
21840        var editorcore = this.editorcore;
21841        var editor= this.editor;
21842        
21843        var children = [];
21844        var btn = function(id,cmd , toggle, handler){
21845        
21846             var  event = toggle ? 'toggle' : 'click';
21847        
21848             var a = {
21849                 size : 'sm',
21850                 xtype: 'Button',
21851                 xns: Roo.bootstrap,
21852                 glyphicon : id,
21853                 cmd : id || cmd,
21854                 enableToggle:toggle !== false,
21855                 //html : 'submit'
21856                 pressed : toggle ? false : null,
21857                 listeners : {}
21858             };
21859             a.listeners[toggle ? 'toggle' : 'click'] = function() {
21860                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
21861             };
21862             children.push(a);
21863             return a;
21864        }
21865         
21866         var style = {
21867                 xtype: 'Button',
21868                 size : 'sm',
21869                 xns: Roo.bootstrap,
21870                 glyphicon : 'font',
21871                 //html : 'submit'
21872                 menu : {
21873                     xtype: 'Menu',
21874                     xns: Roo.bootstrap,
21875                     items:  []
21876                 }
21877         };
21878         Roo.each(this.formats, function(f) {
21879             style.menu.items.push({
21880                 xtype :'MenuItem',
21881                 xns: Roo.bootstrap,
21882                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
21883                 tagname : f,
21884                 listeners : {
21885                     click : function()
21886                     {
21887                         editorcore.insertTag(this.tagname);
21888                         editor.focus();
21889                     }
21890                 }
21891                 
21892             });
21893         });
21894          children.push(style);   
21895             
21896             
21897         btn('bold',false,true);
21898         btn('italic',false,true);
21899         btn('align-left', 'justifyleft',true);
21900         btn('align-center', 'justifycenter',true);
21901         btn('align-right' , 'justifyright',true);
21902         btn('link', false, false, function(btn) {
21903             //Roo.log("create link?");
21904             var url = prompt(this.createLinkText, this.defaultLinkValue);
21905             if(url && url != 'http:/'+'/'){
21906                 this.editorcore.relayCmd('createlink', url);
21907             }
21908         }),
21909         btn('list','insertunorderedlist',true);
21910         btn('pencil', false,true, function(btn){
21911                 Roo.log(this);
21912                 
21913                 this.toggleSourceEdit(btn.pressed);
21914         });
21915         /*
21916         var cog = {
21917                 xtype: 'Button',
21918                 size : 'sm',
21919                 xns: Roo.bootstrap,
21920                 glyphicon : 'cog',
21921                 //html : 'submit'
21922                 menu : {
21923                     xtype: 'Menu',
21924                     xns: Roo.bootstrap,
21925                     items:  []
21926                 }
21927         };
21928         
21929         cog.menu.items.push({
21930             xtype :'MenuItem',
21931             xns: Roo.bootstrap,
21932             html : Clean styles,
21933             tagname : f,
21934             listeners : {
21935                 click : function()
21936                 {
21937                     editorcore.insertTag(this.tagname);
21938                     editor.focus();
21939                 }
21940             }
21941             
21942         });
21943        */
21944         
21945          
21946        this.xtype = 'NavSimplebar';
21947         
21948         for(var i=0;i< children.length;i++) {
21949             
21950             this.buttons.add(this.addxtypeChild(children[i]));
21951             
21952         }
21953         
21954         editor.on('editorevent', this.updateToolbar, this);
21955     },
21956     onBtnClick : function(id)
21957     {
21958        this.editorcore.relayCmd(id);
21959        this.editorcore.focus();
21960     },
21961     
21962     /**
21963      * Protected method that will not generally be called directly. It triggers
21964      * a toolbar update by reading the markup state of the current selection in the editor.
21965      */
21966     updateToolbar: function(){
21967
21968         if(!this.editorcore.activated){
21969             this.editor.onFirstFocus(); // is this neeed?
21970             return;
21971         }
21972
21973         var btns = this.buttons; 
21974         var doc = this.editorcore.doc;
21975         btns.get('bold').setActive(doc.queryCommandState('bold'));
21976         btns.get('italic').setActive(doc.queryCommandState('italic'));
21977         //btns.get('underline').setActive(doc.queryCommandState('underline'));
21978         
21979         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
21980         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
21981         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
21982         
21983         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
21984         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
21985          /*
21986         
21987         var ans = this.editorcore.getAllAncestors();
21988         if (this.formatCombo) {
21989             
21990             
21991             var store = this.formatCombo.store;
21992             this.formatCombo.setValue("");
21993             for (var i =0; i < ans.length;i++) {
21994                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
21995                     // select it..
21996                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
21997                     break;
21998                 }
21999             }
22000         }
22001         
22002         
22003         
22004         // hides menus... - so this cant be on a menu...
22005         Roo.bootstrap.MenuMgr.hideAll();
22006         */
22007         Roo.bootstrap.MenuMgr.hideAll();
22008         //this.editorsyncValue();
22009     },
22010     onFirstFocus: function() {
22011         this.buttons.each(function(item){
22012            item.enable();
22013         });
22014     },
22015     toggleSourceEdit : function(sourceEditMode){
22016         
22017           
22018         if(sourceEditMode){
22019             Roo.log("disabling buttons");
22020            this.buttons.each( function(item){
22021                 if(item.cmd != 'pencil'){
22022                     item.disable();
22023                 }
22024             });
22025           
22026         }else{
22027             Roo.log("enabling buttons");
22028             if(this.editorcore.initialized){
22029                 this.buttons.each( function(item){
22030                     item.enable();
22031                 });
22032             }
22033             
22034         }
22035         Roo.log("calling toggole on editor");
22036         // tell the editor that it's been pressed..
22037         this.editor.toggleSourceEdit(sourceEditMode);
22038        
22039     }
22040 });
22041
22042
22043
22044
22045
22046 /**
22047  * @class Roo.bootstrap.Table.AbstractSelectionModel
22048  * @extends Roo.util.Observable
22049  * Abstract base class for grid SelectionModels.  It provides the interface that should be
22050  * implemented by descendant classes.  This class should not be directly instantiated.
22051  * @constructor
22052  */
22053 Roo.bootstrap.Table.AbstractSelectionModel = function(){
22054     this.locked = false;
22055     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
22056 };
22057
22058
22059 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
22060     /** @ignore Called by the grid automatically. Do not call directly. */
22061     init : function(grid){
22062         this.grid = grid;
22063         this.initEvents();
22064     },
22065
22066     /**
22067      * Locks the selections.
22068      */
22069     lock : function(){
22070         this.locked = true;
22071     },
22072
22073     /**
22074      * Unlocks the selections.
22075      */
22076     unlock : function(){
22077         this.locked = false;
22078     },
22079
22080     /**
22081      * Returns true if the selections are locked.
22082      * @return {Boolean}
22083      */
22084     isLocked : function(){
22085         return this.locked;
22086     }
22087 });
22088 /**
22089  * @extends Roo.bootstrap.Table.AbstractSelectionModel
22090  * @class Roo.bootstrap.Table.RowSelectionModel
22091  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
22092  * It supports multiple selections and keyboard selection/navigation. 
22093  * @constructor
22094  * @param {Object} config
22095  */
22096
22097 Roo.bootstrap.Table.RowSelectionModel = function(config){
22098     Roo.apply(this, config);
22099     this.selections = new Roo.util.MixedCollection(false, function(o){
22100         return o.id;
22101     });
22102
22103     this.last = false;
22104     this.lastActive = false;
22105
22106     this.addEvents({
22107         /**
22108              * @event selectionchange
22109              * Fires when the selection changes
22110              * @param {SelectionModel} this
22111              */
22112             "selectionchange" : true,
22113         /**
22114              * @event afterselectionchange
22115              * Fires after the selection changes (eg. by key press or clicking)
22116              * @param {SelectionModel} this
22117              */
22118             "afterselectionchange" : true,
22119         /**
22120              * @event beforerowselect
22121              * Fires when a row is selected being selected, return false to cancel.
22122              * @param {SelectionModel} this
22123              * @param {Number} rowIndex The selected index
22124              * @param {Boolean} keepExisting False if other selections will be cleared
22125              */
22126             "beforerowselect" : true,
22127         /**
22128              * @event rowselect
22129              * Fires when a row is selected.
22130              * @param {SelectionModel} this
22131              * @param {Number} rowIndex The selected index
22132              * @param {Roo.data.Record} r The record
22133              */
22134             "rowselect" : true,
22135         /**
22136              * @event rowdeselect
22137              * Fires when a row is deselected.
22138              * @param {SelectionModel} this
22139              * @param {Number} rowIndex The selected index
22140              */
22141         "rowdeselect" : true
22142     });
22143     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
22144     this.locked = false;
22145 };
22146
22147 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
22148     /**
22149      * @cfg {Boolean} singleSelect
22150      * True to allow selection of only one row at a time (defaults to false)
22151      */
22152     singleSelect : false,
22153
22154     // private
22155     initEvents : function(){
22156
22157         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
22158             this.grid.on("mousedown", this.handleMouseDown, this);
22159         }else{ // allow click to work like normal
22160             this.grid.on("rowclick", this.handleDragableRowClick, this);
22161         }
22162
22163         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
22164             "up" : function(e){
22165                 if(!e.shiftKey){
22166                     this.selectPrevious(e.shiftKey);
22167                 }else if(this.last !== false && this.lastActive !== false){
22168                     var last = this.last;
22169                     this.selectRange(this.last,  this.lastActive-1);
22170                     this.grid.getView().focusRow(this.lastActive);
22171                     if(last !== false){
22172                         this.last = last;
22173                     }
22174                 }else{
22175                     this.selectFirstRow();
22176                 }
22177                 this.fireEvent("afterselectionchange", this);
22178             },
22179             "down" : function(e){
22180                 if(!e.shiftKey){
22181                     this.selectNext(e.shiftKey);
22182                 }else if(this.last !== false && this.lastActive !== false){
22183                     var last = this.last;
22184                     this.selectRange(this.last,  this.lastActive+1);
22185                     this.grid.getView().focusRow(this.lastActive);
22186                     if(last !== false){
22187                         this.last = last;
22188                     }
22189                 }else{
22190                     this.selectFirstRow();
22191                 }
22192                 this.fireEvent("afterselectionchange", this);
22193             },
22194             scope: this
22195         });
22196
22197         var view = this.grid.view;
22198         view.on("refresh", this.onRefresh, this);
22199         view.on("rowupdated", this.onRowUpdated, this);
22200         view.on("rowremoved", this.onRemove, this);
22201     },
22202
22203     // private
22204     onRefresh : function(){
22205         var ds = this.grid.dataSource, i, v = this.grid.view;
22206         var s = this.selections;
22207         s.each(function(r){
22208             if((i = ds.indexOfId(r.id)) != -1){
22209                 v.onRowSelect(i);
22210             }else{
22211                 s.remove(r);
22212             }
22213         });
22214     },
22215
22216     // private
22217     onRemove : function(v, index, r){
22218         this.selections.remove(r);
22219     },
22220
22221     // private
22222     onRowUpdated : function(v, index, r){
22223         if(this.isSelected(r)){
22224             v.onRowSelect(index);
22225         }
22226     },
22227
22228     /**
22229      * Select records.
22230      * @param {Array} records The records to select
22231      * @param {Boolean} keepExisting (optional) True to keep existing selections
22232      */
22233     selectRecords : function(records, keepExisting){
22234         if(!keepExisting){
22235             this.clearSelections();
22236         }
22237         var ds = this.grid.dataSource;
22238         for(var i = 0, len = records.length; i < len; i++){
22239             this.selectRow(ds.indexOf(records[i]), true);
22240         }
22241     },
22242
22243     /**
22244      * Gets the number of selected rows.
22245      * @return {Number}
22246      */
22247     getCount : function(){
22248         return this.selections.length;
22249     },
22250
22251     /**
22252      * Selects the first row in the grid.
22253      */
22254     selectFirstRow : function(){
22255         this.selectRow(0);
22256     },
22257
22258     /**
22259      * Select the last row.
22260      * @param {Boolean} keepExisting (optional) True to keep existing selections
22261      */
22262     selectLastRow : function(keepExisting){
22263         this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
22264     },
22265
22266     /**
22267      * Selects the row immediately following the last selected row.
22268      * @param {Boolean} keepExisting (optional) True to keep existing selections
22269      */
22270     selectNext : function(keepExisting){
22271         if(this.last !== false && (this.last+1) < this.grid.dataSource.getCount()){
22272             this.selectRow(this.last+1, keepExisting);
22273             this.grid.getView().focusRow(this.last);
22274         }
22275     },
22276
22277     /**
22278      * Selects the row that precedes the last selected row.
22279      * @param {Boolean} keepExisting (optional) True to keep existing selections
22280      */
22281     selectPrevious : function(keepExisting){
22282         if(this.last){
22283             this.selectRow(this.last-1, keepExisting);
22284             this.grid.getView().focusRow(this.last);
22285         }
22286     },
22287
22288     /**
22289      * Returns the selected records
22290      * @return {Array} Array of selected records
22291      */
22292     getSelections : function(){
22293         return [].concat(this.selections.items);
22294     },
22295
22296     /**
22297      * Returns the first selected record.
22298      * @return {Record}
22299      */
22300     getSelected : function(){
22301         return this.selections.itemAt(0);
22302     },
22303
22304
22305     /**
22306      * Clears all selections.
22307      */
22308     clearSelections : function(fast){
22309         if(this.locked) {
22310             return;
22311         }
22312         if(fast !== true){
22313             var ds = this.grid.dataSource;
22314             var s = this.selections;
22315             s.each(function(r){
22316                 this.deselectRow(ds.indexOfId(r.id));
22317             }, this);
22318             s.clear();
22319         }else{
22320             this.selections.clear();
22321         }
22322         this.last = false;
22323     },
22324
22325
22326     /**
22327      * Selects all rows.
22328      */
22329     selectAll : function(){
22330         if(this.locked) {
22331             return;
22332         }
22333         this.selections.clear();
22334         for(var i = 0, len = this.grid.dataSource.getCount(); i < len; i++){
22335             this.selectRow(i, true);
22336         }
22337     },
22338
22339     /**
22340      * Returns True if there is a selection.
22341      * @return {Boolean}
22342      */
22343     hasSelection : function(){
22344         return this.selections.length > 0;
22345     },
22346
22347     /**
22348      * Returns True if the specified row is selected.
22349      * @param {Number/Record} record The record or index of the record to check
22350      * @return {Boolean}
22351      */
22352     isSelected : function(index){
22353         var r = typeof index == "number" ? this.grid.dataSource.getAt(index) : index;
22354         return (r && this.selections.key(r.id) ? true : false);
22355     },
22356
22357     /**
22358      * Returns True if the specified record id is selected.
22359      * @param {String} id The id of record to check
22360      * @return {Boolean}
22361      */
22362     isIdSelected : function(id){
22363         return (this.selections.key(id) ? true : false);
22364     },
22365
22366     // private
22367     handleMouseDown : function(e, t){
22368         var view = this.grid.getView(), rowIndex;
22369         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
22370             return;
22371         };
22372         if(e.shiftKey && this.last !== false){
22373             var last = this.last;
22374             this.selectRange(last, rowIndex, e.ctrlKey);
22375             this.last = last; // reset the last
22376             view.focusRow(rowIndex);
22377         }else{
22378             var isSelected = this.isSelected(rowIndex);
22379             if(e.button !== 0 && isSelected){
22380                 view.focusRow(rowIndex);
22381             }else if(e.ctrlKey && isSelected){
22382                 this.deselectRow(rowIndex);
22383             }else if(!isSelected){
22384                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
22385                 view.focusRow(rowIndex);
22386             }
22387         }
22388         this.fireEvent("afterselectionchange", this);
22389     },
22390     // private
22391     handleDragableRowClick :  function(grid, rowIndex, e) 
22392     {
22393         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
22394             this.selectRow(rowIndex, false);
22395             grid.view.focusRow(rowIndex);
22396              this.fireEvent("afterselectionchange", this);
22397         }
22398     },
22399     
22400     /**
22401      * Selects multiple rows.
22402      * @param {Array} rows Array of the indexes of the row to select
22403      * @param {Boolean} keepExisting (optional) True to keep existing selections
22404      */
22405     selectRows : function(rows, keepExisting){
22406         if(!keepExisting){
22407             this.clearSelections();
22408         }
22409         for(var i = 0, len = rows.length; i < len; i++){
22410             this.selectRow(rows[i], true);
22411         }
22412     },
22413
22414     /**
22415      * Selects a range of rows. All rows in between startRow and endRow are also selected.
22416      * @param {Number} startRow The index of the first row in the range
22417      * @param {Number} endRow The index of the last row in the range
22418      * @param {Boolean} keepExisting (optional) True to retain existing selections
22419      */
22420     selectRange : function(startRow, endRow, keepExisting){
22421         if(this.locked) {
22422             return;
22423         }
22424         if(!keepExisting){
22425             this.clearSelections();
22426         }
22427         if(startRow <= endRow){
22428             for(var i = startRow; i <= endRow; i++){
22429                 this.selectRow(i, true);
22430             }
22431         }else{
22432             for(var i = startRow; i >= endRow; i--){
22433                 this.selectRow(i, true);
22434             }
22435         }
22436     },
22437
22438     /**
22439      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
22440      * @param {Number} startRow The index of the first row in the range
22441      * @param {Number} endRow The index of the last row in the range
22442      */
22443     deselectRange : function(startRow, endRow, preventViewNotify){
22444         if(this.locked) {
22445             return;
22446         }
22447         for(var i = startRow; i <= endRow; i++){
22448             this.deselectRow(i, preventViewNotify);
22449         }
22450     },
22451
22452     /**
22453      * Selects a row.
22454      * @param {Number} row The index of the row to select
22455      * @param {Boolean} keepExisting (optional) True to keep existing selections
22456      */
22457     selectRow : function(index, keepExisting, preventViewNotify){
22458         if(this.locked || (index < 0 || index >= this.grid.dataSource.getCount())) {
22459             return;
22460         }
22461         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
22462             if(!keepExisting || this.singleSelect){
22463                 this.clearSelections();
22464             }
22465             var r = this.grid.dataSource.getAt(index);
22466             this.selections.add(r);
22467             this.last = this.lastActive = index;
22468             if(!preventViewNotify){
22469                 this.grid.getView().onRowSelect(index);
22470             }
22471             this.fireEvent("rowselect", this, index, r);
22472             this.fireEvent("selectionchange", this);
22473         }
22474     },
22475
22476     /**
22477      * Deselects a row.
22478      * @param {Number} row The index of the row to deselect
22479      */
22480     deselectRow : function(index, preventViewNotify){
22481         if(this.locked) {
22482             return;
22483         }
22484         if(this.last == index){
22485             this.last = false;
22486         }
22487         if(this.lastActive == index){
22488             this.lastActive = false;
22489         }
22490         var r = this.grid.dataSource.getAt(index);
22491         this.selections.remove(r);
22492         if(!preventViewNotify){
22493             this.grid.getView().onRowDeselect(index);
22494         }
22495         this.fireEvent("rowdeselect", this, index);
22496         this.fireEvent("selectionchange", this);
22497     },
22498
22499     // private
22500     restoreLast : function(){
22501         if(this._last){
22502             this.last = this._last;
22503         }
22504     },
22505
22506     // private
22507     acceptsNav : function(row, col, cm){
22508         return !cm.isHidden(col) && cm.isCellEditable(col, row);
22509     },
22510
22511     // private
22512     onEditorKey : function(field, e){
22513         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
22514         if(k == e.TAB){
22515             e.stopEvent();
22516             ed.completeEdit();
22517             if(e.shiftKey){
22518                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
22519             }else{
22520                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
22521             }
22522         }else if(k == e.ENTER && !e.ctrlKey){
22523             e.stopEvent();
22524             ed.completeEdit();
22525             if(e.shiftKey){
22526                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
22527             }else{
22528                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
22529             }
22530         }else if(k == e.ESC){
22531             ed.cancelEdit();
22532         }
22533         if(newCell){
22534             g.startEditing(newCell[0], newCell[1]);
22535         }
22536     }
22537 });/*
22538  * Based on:
22539  * Ext JS Library 1.1.1
22540  * Copyright(c) 2006-2007, Ext JS, LLC.
22541  *
22542  * Originally Released Under LGPL - original licence link has changed is not relivant.
22543  *
22544  * Fork - LGPL
22545  * <script type="text/javascript">
22546  */
22547  
22548 /**
22549  * @class Roo.bootstrap.PagingToolbar
22550  * @extends Roo.bootstrap.NavSimplebar
22551  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
22552  * @constructor
22553  * Create a new PagingToolbar
22554  * @param {Object} config The config object
22555  * @param {Roo.data.Store} store
22556  */
22557 Roo.bootstrap.PagingToolbar = function(config)
22558 {
22559     // old args format still supported... - xtype is prefered..
22560         // created from xtype...
22561     
22562     this.ds = config.dataSource;
22563     
22564     if (config.store && !this.ds) {
22565         this.store= Roo.factory(config.store, Roo.data);
22566         this.ds = this.store;
22567         this.ds.xmodule = this.xmodule || false;
22568     }
22569     
22570     this.toolbarItems = [];
22571     if (config.items) {
22572         this.toolbarItems = config.items;
22573     }
22574     
22575     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
22576     
22577     this.cursor = 0;
22578     
22579     if (this.ds) { 
22580         this.bind(this.ds);
22581     }
22582     
22583     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
22584     
22585 };
22586
22587 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
22588     /**
22589      * @cfg {Roo.data.Store} dataSource
22590      * The underlying data store providing the paged data
22591      */
22592     /**
22593      * @cfg {String/HTMLElement/Element} container
22594      * container The id or element that will contain the toolbar
22595      */
22596     /**
22597      * @cfg {Boolean} displayInfo
22598      * True to display the displayMsg (defaults to false)
22599      */
22600     /**
22601      * @cfg {Number} pageSize
22602      * The number of records to display per page (defaults to 20)
22603      */
22604     pageSize: 20,
22605     /**
22606      * @cfg {String} displayMsg
22607      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
22608      */
22609     displayMsg : 'Displaying {0} - {1} of {2}',
22610     /**
22611      * @cfg {String} emptyMsg
22612      * The message to display when no records are found (defaults to "No data to display")
22613      */
22614     emptyMsg : 'No data to display',
22615     /**
22616      * Customizable piece of the default paging text (defaults to "Page")
22617      * @type String
22618      */
22619     beforePageText : "Page",
22620     /**
22621      * Customizable piece of the default paging text (defaults to "of %0")
22622      * @type String
22623      */
22624     afterPageText : "of {0}",
22625     /**
22626      * Customizable piece of the default paging text (defaults to "First Page")
22627      * @type String
22628      */
22629     firstText : "First Page",
22630     /**
22631      * Customizable piece of the default paging text (defaults to "Previous Page")
22632      * @type String
22633      */
22634     prevText : "Previous Page",
22635     /**
22636      * Customizable piece of the default paging text (defaults to "Next Page")
22637      * @type String
22638      */
22639     nextText : "Next Page",
22640     /**
22641      * Customizable piece of the default paging text (defaults to "Last Page")
22642      * @type String
22643      */
22644     lastText : "Last Page",
22645     /**
22646      * Customizable piece of the default paging text (defaults to "Refresh")
22647      * @type String
22648      */
22649     refreshText : "Refresh",
22650
22651     buttons : false,
22652     // private
22653     onRender : function(ct, position) 
22654     {
22655         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
22656         this.navgroup.parentId = this.id;
22657         this.navgroup.onRender(this.el, null);
22658         // add the buttons to the navgroup
22659         
22660         if(this.displayInfo){
22661             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
22662             this.displayEl = this.el.select('.x-paging-info', true).first();
22663 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
22664 //            this.displayEl = navel.el.select('span',true).first();
22665         }
22666         
22667         var _this = this;
22668         
22669         if(this.buttons){
22670             Roo.each(_this.buttons, function(e){ // this might need to use render????
22671                Roo.factory(e).onRender(_this.el, null);
22672             });
22673         }
22674             
22675         Roo.each(_this.toolbarItems, function(e) {
22676             _this.navgroup.addItem(e);
22677         });
22678         
22679         
22680         this.first = this.navgroup.addItem({
22681             tooltip: this.firstText,
22682             cls: "prev",
22683             icon : 'fa fa-backward',
22684             disabled: true,
22685             preventDefault: true,
22686             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
22687         });
22688         
22689         this.prev =  this.navgroup.addItem({
22690             tooltip: this.prevText,
22691             cls: "prev",
22692             icon : 'fa fa-step-backward',
22693             disabled: true,
22694             preventDefault: true,
22695             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
22696         });
22697     //this.addSeparator();
22698         
22699         
22700         var field = this.navgroup.addItem( {
22701             tagtype : 'span',
22702             cls : 'x-paging-position',
22703             
22704             html : this.beforePageText  +
22705                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
22706                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
22707          } ); //?? escaped?
22708         
22709         this.field = field.el.select('input', true).first();
22710         this.field.on("keydown", this.onPagingKeydown, this);
22711         this.field.on("focus", function(){this.dom.select();});
22712     
22713     
22714         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
22715         //this.field.setHeight(18);
22716         //this.addSeparator();
22717         this.next = this.navgroup.addItem({
22718             tooltip: this.nextText,
22719             cls: "next",
22720             html : ' <i class="fa fa-step-forward">',
22721             disabled: true,
22722             preventDefault: true,
22723             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
22724         });
22725         this.last = this.navgroup.addItem({
22726             tooltip: this.lastText,
22727             icon : 'fa fa-forward',
22728             cls: "next",
22729             disabled: true,
22730             preventDefault: true,
22731             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
22732         });
22733     //this.addSeparator();
22734         this.loading = this.navgroup.addItem({
22735             tooltip: this.refreshText,
22736             icon: 'fa fa-refresh',
22737             preventDefault: true,
22738             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
22739         });
22740         
22741     },
22742
22743     // private
22744     updateInfo : function(){
22745         if(this.displayEl){
22746             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
22747             var msg = count == 0 ?
22748                 this.emptyMsg :
22749                 String.format(
22750                     this.displayMsg,
22751                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
22752                 );
22753             this.displayEl.update(msg);
22754         }
22755     },
22756
22757     // private
22758     onLoad : function(ds, r, o){
22759        this.cursor = o.params ? o.params.start : 0;
22760        var d = this.getPageData(),
22761             ap = d.activePage,
22762             ps = d.pages;
22763         
22764        this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
22765        this.field.dom.value = ap;
22766        this.first.setDisabled(ap == 1);
22767        this.prev.setDisabled(ap == 1);
22768        this.next.setDisabled(ap == ps);
22769        this.last.setDisabled(ap == ps);
22770        this.loading.enable();
22771        this.updateInfo();
22772     },
22773
22774     // private
22775     getPageData : function(){
22776         var total = this.ds.getTotalCount();
22777         return {
22778             total : total,
22779             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
22780             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
22781         };
22782     },
22783
22784     // private
22785     onLoadError : function(){
22786         this.loading.enable();
22787     },
22788
22789     // private
22790     onPagingKeydown : function(e){
22791         var k = e.getKey();
22792         var d = this.getPageData();
22793         if(k == e.RETURN){
22794             var v = this.field.dom.value, pageNum;
22795             if(!v || isNaN(pageNum = parseInt(v, 10))){
22796                 this.field.dom.value = d.activePage;
22797                 return;
22798             }
22799             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
22800             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22801             e.stopEvent();
22802         }
22803         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))
22804         {
22805           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
22806           this.field.dom.value = pageNum;
22807           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
22808           e.stopEvent();
22809         }
22810         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
22811         {
22812           var v = this.field.dom.value, pageNum; 
22813           var increment = (e.shiftKey) ? 10 : 1;
22814           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
22815                 increment *= -1;
22816           }
22817           if(!v || isNaN(pageNum = parseInt(v, 10))) {
22818             this.field.dom.value = d.activePage;
22819             return;
22820           }
22821           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
22822           {
22823             this.field.dom.value = parseInt(v, 10) + increment;
22824             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
22825             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
22826           }
22827           e.stopEvent();
22828         }
22829     },
22830
22831     // private
22832     beforeLoad : function(){
22833         if(this.loading){
22834             this.loading.disable();
22835         }
22836     },
22837
22838     // private
22839     onClick : function(which){
22840         
22841         var ds = this.ds;
22842         if (!ds) {
22843             return;
22844         }
22845         
22846         switch(which){
22847             case "first":
22848                 ds.load({params:{start: 0, limit: this.pageSize}});
22849             break;
22850             case "prev":
22851                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
22852             break;
22853             case "next":
22854                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
22855             break;
22856             case "last":
22857                 var total = ds.getTotalCount();
22858                 var extra = total % this.pageSize;
22859                 var lastStart = extra ? (total - extra) : total-this.pageSize;
22860                 ds.load({params:{start: lastStart, limit: this.pageSize}});
22861             break;
22862             case "refresh":
22863                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
22864             break;
22865         }
22866     },
22867
22868     /**
22869      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
22870      * @param {Roo.data.Store} store The data store to unbind
22871      */
22872     unbind : function(ds){
22873         ds.un("beforeload", this.beforeLoad, this);
22874         ds.un("load", this.onLoad, this);
22875         ds.un("loadexception", this.onLoadError, this);
22876         ds.un("remove", this.updateInfo, this);
22877         ds.un("add", this.updateInfo, this);
22878         this.ds = undefined;
22879     },
22880
22881     /**
22882      * Binds the paging toolbar to the specified {@link Roo.data.Store}
22883      * @param {Roo.data.Store} store The data store to bind
22884      */
22885     bind : function(ds){
22886         ds.on("beforeload", this.beforeLoad, this);
22887         ds.on("load", this.onLoad, this);
22888         ds.on("loadexception", this.onLoadError, this);
22889         ds.on("remove", this.updateInfo, this);
22890         ds.on("add", this.updateInfo, this);
22891         this.ds = ds;
22892     }
22893 });/*
22894  * - LGPL
22895  *
22896  * element
22897  * 
22898  */
22899
22900 /**
22901  * @class Roo.bootstrap.MessageBar
22902  * @extends Roo.bootstrap.Component
22903  * Bootstrap MessageBar class
22904  * @cfg {String} html contents of the MessageBar
22905  * @cfg {String} weight (info | success | warning | danger) default info
22906  * @cfg {String} beforeClass insert the bar before the given class
22907  * @cfg {Boolean} closable (true | false) default false
22908  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
22909  * 
22910  * @constructor
22911  * Create a new Element
22912  * @param {Object} config The config object
22913  */
22914
22915 Roo.bootstrap.MessageBar = function(config){
22916     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
22917 };
22918
22919 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
22920     
22921     html: '',
22922     weight: 'info',
22923     closable: false,
22924     fixed: false,
22925     beforeClass: 'bootstrap-sticky-wrap',
22926     
22927     getAutoCreate : function(){
22928         
22929         var cfg = {
22930             tag: 'div',
22931             cls: 'alert alert-dismissable alert-' + this.weight,
22932             cn: [
22933                 {
22934                     tag: 'span',
22935                     cls: 'message',
22936                     html: this.html || ''
22937                 }
22938             ]
22939         };
22940         
22941         if(this.fixed){
22942             cfg.cls += ' alert-messages-fixed';
22943         }
22944         
22945         if(this.closable){
22946             cfg.cn.push({
22947                 tag: 'button',
22948                 cls: 'close',
22949                 html: 'x'
22950             });
22951         }
22952         
22953         return cfg;
22954     },
22955     
22956     onRender : function(ct, position)
22957     {
22958         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
22959         
22960         if(!this.el){
22961             var cfg = Roo.apply({},  this.getAutoCreate());
22962             cfg.id = Roo.id();
22963             
22964             if (this.cls) {
22965                 cfg.cls += ' ' + this.cls;
22966             }
22967             if (this.style) {
22968                 cfg.style = this.style;
22969             }
22970             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
22971             
22972             this.el.setVisibilityMode(Roo.Element.DISPLAY);
22973         }
22974         
22975         this.el.select('>button.close').on('click', this.hide, this);
22976         
22977     },
22978     
22979     show : function()
22980     {
22981         if (!this.rendered) {
22982             this.render();
22983         }
22984         
22985         this.el.show();
22986         
22987         this.fireEvent('show', this);
22988         
22989     },
22990     
22991     hide : function()
22992     {
22993         if (!this.rendered) {
22994             this.render();
22995         }
22996         
22997         this.el.hide();
22998         
22999         this.fireEvent('hide', this);
23000     },
23001     
23002     update : function()
23003     {
23004 //        var e = this.el.dom.firstChild;
23005 //        
23006 //        if(this.closable){
23007 //            e = e.nextSibling;
23008 //        }
23009 //        
23010 //        e.data = this.html || '';
23011
23012         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
23013     }
23014    
23015 });
23016
23017  
23018
23019      /*
23020  * - LGPL
23021  *
23022  * Graph
23023  * 
23024  */
23025
23026
23027 /**
23028  * @class Roo.bootstrap.Graph
23029  * @extends Roo.bootstrap.Component
23030  * Bootstrap Graph class
23031 > Prameters
23032  -sm {number} sm 4
23033  -md {number} md 5
23034  @cfg {String} graphtype  bar | vbar | pie
23035  @cfg {number} g_x coodinator | centre x (pie)
23036  @cfg {number} g_y coodinator | centre y (pie)
23037  @cfg {number} g_r radius (pie)
23038  @cfg {number} g_height height of the chart (respected by all elements in the set)
23039  @cfg {number} g_width width of the chart (respected by all elements in the set)
23040  @cfg {Object} title The title of the chart
23041     
23042  -{Array}  values
23043  -opts (object) options for the chart 
23044      o {
23045      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
23046      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
23047      o vgutter (number)
23048      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.
23049      o stacked (boolean) whether or not to tread values as in a stacked bar chart
23050      o to
23051      o stretch (boolean)
23052      o }
23053  -opts (object) options for the pie
23054      o{
23055      o cut
23056      o startAngle (number)
23057      o endAngle (number)
23058      } 
23059  *
23060  * @constructor
23061  * Create a new Input
23062  * @param {Object} config The config object
23063  */
23064
23065 Roo.bootstrap.Graph = function(config){
23066     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
23067     
23068     this.addEvents({
23069         // img events
23070         /**
23071          * @event click
23072          * The img click event for the img.
23073          * @param {Roo.EventObject} e
23074          */
23075         "click" : true
23076     });
23077 };
23078
23079 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
23080     
23081     sm: 4,
23082     md: 5,
23083     graphtype: 'bar',
23084     g_height: 250,
23085     g_width: 400,
23086     g_x: 50,
23087     g_y: 50,
23088     g_r: 30,
23089     opts:{
23090         //g_colors: this.colors,
23091         g_type: 'soft',
23092         g_gutter: '20%'
23093
23094     },
23095     title : false,
23096
23097     getAutoCreate : function(){
23098         
23099         var cfg = {
23100             tag: 'div',
23101             html : null
23102         };
23103         
23104         
23105         return  cfg;
23106     },
23107
23108     onRender : function(ct,position){
23109         
23110         
23111         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
23112         
23113         if (typeof(Raphael) == 'undefined') {
23114             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
23115             return;
23116         }
23117         
23118         this.raphael = Raphael(this.el.dom);
23119         
23120                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23121                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23122                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
23123                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
23124                 /*
23125                 r.text(160, 10, "Single Series Chart").attr(txtattr);
23126                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
23127                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
23128                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
23129                 
23130                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
23131                 r.barchart(330, 10, 300, 220, data1);
23132                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
23133                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
23134                 */
23135                 
23136                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23137                 // r.barchart(30, 30, 560, 250,  xdata, {
23138                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
23139                 //     axis : "0 0 1 1",
23140                 //     axisxlabels :  xdata
23141                 //     //yvalues : cols,
23142                    
23143                 // });
23144 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
23145 //        
23146 //        this.load(null,xdata,{
23147 //                axis : "0 0 1 1",
23148 //                axisxlabels :  xdata
23149 //                });
23150
23151     },
23152
23153     load : function(graphtype,xdata,opts)
23154     {
23155         this.raphael.clear();
23156         if(!graphtype) {
23157             graphtype = this.graphtype;
23158         }
23159         if(!opts){
23160             opts = this.opts;
23161         }
23162         var r = this.raphael,
23163             fin = function () {
23164                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
23165             },
23166             fout = function () {
23167                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
23168             },
23169             pfin = function() {
23170                 this.sector.stop();
23171                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
23172
23173                 if (this.label) {
23174                     this.label[0].stop();
23175                     this.label[0].attr({ r: 7.5 });
23176                     this.label[1].attr({ "font-weight": 800 });
23177                 }
23178             },
23179             pfout = function() {
23180                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
23181
23182                 if (this.label) {
23183                     this.label[0].animate({ r: 5 }, 500, "bounce");
23184                     this.label[1].attr({ "font-weight": 400 });
23185                 }
23186             };
23187
23188         switch(graphtype){
23189             case 'bar':
23190                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23191                 break;
23192             case 'hbar':
23193                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
23194                 break;
23195             case 'pie':
23196 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
23197 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
23198 //            
23199                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
23200                 
23201                 break;
23202
23203         }
23204         
23205         if(this.title){
23206             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
23207         }
23208         
23209     },
23210     
23211     setTitle: function(o)
23212     {
23213         this.title = o;
23214     },
23215     
23216     initEvents: function() {
23217         
23218         if(!this.href){
23219             this.el.on('click', this.onClick, this);
23220         }
23221     },
23222     
23223     onClick : function(e)
23224     {
23225         Roo.log('img onclick');
23226         this.fireEvent('click', this, e);
23227     }
23228    
23229 });
23230
23231  
23232 /*
23233  * - LGPL
23234  *
23235  * numberBox
23236  * 
23237  */
23238 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23239
23240 /**
23241  * @class Roo.bootstrap.dash.NumberBox
23242  * @extends Roo.bootstrap.Component
23243  * Bootstrap NumberBox class
23244  * @cfg {String} headline Box headline
23245  * @cfg {String} content Box content
23246  * @cfg {String} icon Box icon
23247  * @cfg {String} footer Footer text
23248  * @cfg {String} fhref Footer href
23249  * 
23250  * @constructor
23251  * Create a new NumberBox
23252  * @param {Object} config The config object
23253  */
23254
23255
23256 Roo.bootstrap.dash.NumberBox = function(config){
23257     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
23258     
23259 };
23260
23261 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
23262     
23263     headline : '',
23264     content : '',
23265     icon : '',
23266     footer : '',
23267     fhref : '',
23268     ficon : '',
23269     
23270     getAutoCreate : function(){
23271         
23272         var cfg = {
23273             tag : 'div',
23274             cls : 'small-box ',
23275             cn : [
23276                 {
23277                     tag : 'div',
23278                     cls : 'inner',
23279                     cn :[
23280                         {
23281                             tag : 'h3',
23282                             cls : 'roo-headline',
23283                             html : this.headline
23284                         },
23285                         {
23286                             tag : 'p',
23287                             cls : 'roo-content',
23288                             html : this.content
23289                         }
23290                     ]
23291                 }
23292             ]
23293         };
23294         
23295         if(this.icon){
23296             cfg.cn.push({
23297                 tag : 'div',
23298                 cls : 'icon',
23299                 cn :[
23300                     {
23301                         tag : 'i',
23302                         cls : 'ion ' + this.icon
23303                     }
23304                 ]
23305             });
23306         }
23307         
23308         if(this.footer){
23309             var footer = {
23310                 tag : 'a',
23311                 cls : 'small-box-footer',
23312                 href : this.fhref || '#',
23313                 html : this.footer
23314             };
23315             
23316             cfg.cn.push(footer);
23317             
23318         }
23319         
23320         return  cfg;
23321     },
23322
23323     onRender : function(ct,position){
23324         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
23325
23326
23327        
23328                 
23329     },
23330
23331     setHeadline: function (value)
23332     {
23333         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
23334     },
23335     
23336     setFooter: function (value, href)
23337     {
23338         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
23339         
23340         if(href){
23341             this.el.select('a.small-box-footer',true).first().attr('href', href);
23342         }
23343         
23344     },
23345
23346     setContent: function (value)
23347     {
23348         this.el.select('.roo-content',true).first().dom.innerHTML = value;
23349     },
23350
23351     initEvents: function() 
23352     {   
23353         
23354     }
23355     
23356 });
23357
23358  
23359 /*
23360  * - LGPL
23361  *
23362  * TabBox
23363  * 
23364  */
23365 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23366
23367 /**
23368  * @class Roo.bootstrap.dash.TabBox
23369  * @extends Roo.bootstrap.Component
23370  * Bootstrap TabBox class
23371  * @cfg {String} title Title of the TabBox
23372  * @cfg {String} icon Icon of the TabBox
23373  * @cfg {Boolean} showtabs (true|false) show the tabs default true
23374  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
23375  * 
23376  * @constructor
23377  * Create a new TabBox
23378  * @param {Object} config The config object
23379  */
23380
23381
23382 Roo.bootstrap.dash.TabBox = function(config){
23383     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
23384     this.addEvents({
23385         // raw events
23386         /**
23387          * @event addpane
23388          * When a pane is added
23389          * @param {Roo.bootstrap.dash.TabPane} pane
23390          */
23391         "addpane" : true,
23392         /**
23393          * @event activatepane
23394          * When a pane is activated
23395          * @param {Roo.bootstrap.dash.TabPane} pane
23396          */
23397         "activatepane" : true
23398         
23399          
23400     });
23401     
23402     this.panes = [];
23403 };
23404
23405 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
23406
23407     title : '',
23408     icon : false,
23409     showtabs : true,
23410     tabScrollable : false,
23411     
23412     getChildContainer : function()
23413     {
23414         return this.el.select('.tab-content', true).first();
23415     },
23416     
23417     getAutoCreate : function(){
23418         
23419         var header = {
23420             tag: 'li',
23421             cls: 'pull-left header',
23422             html: this.title,
23423             cn : []
23424         };
23425         
23426         if(this.icon){
23427             header.cn.push({
23428                 tag: 'i',
23429                 cls: 'fa ' + this.icon
23430             });
23431         }
23432         
23433         var h = {
23434             tag: 'ul',
23435             cls: 'nav nav-tabs pull-right',
23436             cn: [
23437                 header
23438             ]
23439         };
23440         
23441         if(this.tabScrollable){
23442             h = {
23443                 tag: 'div',
23444                 cls: 'tab-header',
23445                 cn: [
23446                     {
23447                         tag: 'ul',
23448                         cls: 'nav nav-tabs pull-right',
23449                         cn: [
23450                             header
23451                         ]
23452                     }
23453                 ]
23454             };
23455         }
23456         
23457         var cfg = {
23458             tag: 'div',
23459             cls: 'nav-tabs-custom',
23460             cn: [
23461                 h,
23462                 {
23463                     tag: 'div',
23464                     cls: 'tab-content no-padding',
23465                     cn: []
23466                 }
23467             ]
23468         };
23469
23470         return  cfg;
23471     },
23472     initEvents : function()
23473     {
23474         //Roo.log('add add pane handler');
23475         this.on('addpane', this.onAddPane, this);
23476     },
23477      /**
23478      * Updates the box title
23479      * @param {String} html to set the title to.
23480      */
23481     setTitle : function(value)
23482     {
23483         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
23484     },
23485     onAddPane : function(pane)
23486     {
23487         this.panes.push(pane);
23488         //Roo.log('addpane');
23489         //Roo.log(pane);
23490         // tabs are rendere left to right..
23491         if(!this.showtabs){
23492             return;
23493         }
23494         
23495         var ctr = this.el.select('.nav-tabs', true).first();
23496          
23497          
23498         var existing = ctr.select('.nav-tab',true);
23499         var qty = existing.getCount();;
23500         
23501         
23502         var tab = ctr.createChild({
23503             tag : 'li',
23504             cls : 'nav-tab' + (qty ? '' : ' active'),
23505             cn : [
23506                 {
23507                     tag : 'a',
23508                     href:'#',
23509                     html : pane.title
23510                 }
23511             ]
23512         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
23513         pane.tab = tab;
23514         
23515         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
23516         if (!qty) {
23517             pane.el.addClass('active');
23518         }
23519         
23520                 
23521     },
23522     onTabClick : function(ev,un,ob,pane)
23523     {
23524         //Roo.log('tab - prev default');
23525         ev.preventDefault();
23526         
23527         
23528         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
23529         pane.tab.addClass('active');
23530         //Roo.log(pane.title);
23531         this.getChildContainer().select('.tab-pane',true).removeClass('active');
23532         // technically we should have a deactivate event.. but maybe add later.
23533         // and it should not de-activate the selected tab...
23534         this.fireEvent('activatepane', pane);
23535         pane.el.addClass('active');
23536         pane.fireEvent('activate');
23537         
23538         
23539     },
23540     
23541     getActivePane : function()
23542     {
23543         var r = false;
23544         Roo.each(this.panes, function(p) {
23545             if(p.el.hasClass('active')){
23546                 r = p;
23547                 return false;
23548             }
23549             
23550             return;
23551         });
23552         
23553         return r;
23554     }
23555     
23556     
23557 });
23558
23559  
23560 /*
23561  * - LGPL
23562  *
23563  * Tab pane
23564  * 
23565  */
23566 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
23567 /**
23568  * @class Roo.bootstrap.TabPane
23569  * @extends Roo.bootstrap.Component
23570  * Bootstrap TabPane class
23571  * @cfg {Boolean} active (false | true) Default false
23572  * @cfg {String} title title of panel
23573
23574  * 
23575  * @constructor
23576  * Create a new TabPane
23577  * @param {Object} config The config object
23578  */
23579
23580 Roo.bootstrap.dash.TabPane = function(config){
23581     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
23582     
23583     this.addEvents({
23584         // raw events
23585         /**
23586          * @event activate
23587          * When a pane is activated
23588          * @param {Roo.bootstrap.dash.TabPane} pane
23589          */
23590         "activate" : true
23591          
23592     });
23593 };
23594
23595 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
23596     
23597     active : false,
23598     title : '',
23599     
23600     // the tabBox that this is attached to.
23601     tab : false,
23602      
23603     getAutoCreate : function() 
23604     {
23605         var cfg = {
23606             tag: 'div',
23607             cls: 'tab-pane'
23608         };
23609         
23610         if(this.active){
23611             cfg.cls += ' active';
23612         }
23613         
23614         return cfg;
23615     },
23616     initEvents  : function()
23617     {
23618         //Roo.log('trigger add pane handler');
23619         this.parent().fireEvent('addpane', this)
23620     },
23621     
23622      /**
23623      * Updates the tab title 
23624      * @param {String} html to set the title to.
23625      */
23626     setTitle: function(str)
23627     {
23628         if (!this.tab) {
23629             return;
23630         }
23631         this.title = str;
23632         this.tab.select('a', true).first().dom.innerHTML = str;
23633         
23634     }
23635     
23636     
23637     
23638 });
23639
23640  
23641
23642
23643  /*
23644  * - LGPL
23645  *
23646  * menu
23647  * 
23648  */
23649 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23650
23651 /**
23652  * @class Roo.bootstrap.menu.Menu
23653  * @extends Roo.bootstrap.Component
23654  * Bootstrap Menu class - container for Menu
23655  * @cfg {String} html Text of the menu
23656  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
23657  * @cfg {String} icon Font awesome icon
23658  * @cfg {String} pos Menu align to (top | bottom) default bottom
23659  * 
23660  * 
23661  * @constructor
23662  * Create a new Menu
23663  * @param {Object} config The config object
23664  */
23665
23666
23667 Roo.bootstrap.menu.Menu = function(config){
23668     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
23669     
23670     this.addEvents({
23671         /**
23672          * @event beforeshow
23673          * Fires before this menu is displayed
23674          * @param {Roo.bootstrap.menu.Menu} this
23675          */
23676         beforeshow : true,
23677         /**
23678          * @event beforehide
23679          * Fires before this menu is hidden
23680          * @param {Roo.bootstrap.menu.Menu} this
23681          */
23682         beforehide : true,
23683         /**
23684          * @event show
23685          * Fires after this menu is displayed
23686          * @param {Roo.bootstrap.menu.Menu} this
23687          */
23688         show : true,
23689         /**
23690          * @event hide
23691          * Fires after this menu is hidden
23692          * @param {Roo.bootstrap.menu.Menu} this
23693          */
23694         hide : true,
23695         /**
23696          * @event click
23697          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
23698          * @param {Roo.bootstrap.menu.Menu} this
23699          * @param {Roo.EventObject} e
23700          */
23701         click : true
23702     });
23703     
23704 };
23705
23706 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
23707     
23708     submenu : false,
23709     html : '',
23710     weight : 'default',
23711     icon : false,
23712     pos : 'bottom',
23713     
23714     
23715     getChildContainer : function() {
23716         if(this.isSubMenu){
23717             return this.el;
23718         }
23719         
23720         return this.el.select('ul.dropdown-menu', true).first();  
23721     },
23722     
23723     getAutoCreate : function()
23724     {
23725         var text = [
23726             {
23727                 tag : 'span',
23728                 cls : 'roo-menu-text',
23729                 html : this.html
23730             }
23731         ];
23732         
23733         if(this.icon){
23734             text.unshift({
23735                 tag : 'i',
23736                 cls : 'fa ' + this.icon
23737             })
23738         }
23739         
23740         
23741         var cfg = {
23742             tag : 'div',
23743             cls : 'btn-group',
23744             cn : [
23745                 {
23746                     tag : 'button',
23747                     cls : 'dropdown-button btn btn-' + this.weight,
23748                     cn : text
23749                 },
23750                 {
23751                     tag : 'button',
23752                     cls : 'dropdown-toggle btn btn-' + this.weight,
23753                     cn : [
23754                         {
23755                             tag : 'span',
23756                             cls : 'caret'
23757                         }
23758                     ]
23759                 },
23760                 {
23761                     tag : 'ul',
23762                     cls : 'dropdown-menu'
23763                 }
23764             ]
23765             
23766         };
23767         
23768         if(this.pos == 'top'){
23769             cfg.cls += ' dropup';
23770         }
23771         
23772         if(this.isSubMenu){
23773             cfg = {
23774                 tag : 'ul',
23775                 cls : 'dropdown-menu'
23776             }
23777         }
23778         
23779         return cfg;
23780     },
23781     
23782     onRender : function(ct, position)
23783     {
23784         this.isSubMenu = ct.hasClass('dropdown-submenu');
23785         
23786         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
23787     },
23788     
23789     initEvents : function() 
23790     {
23791         if(this.isSubMenu){
23792             return;
23793         }
23794         
23795         this.hidden = true;
23796         
23797         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
23798         this.triggerEl.on('click', this.onTriggerPress, this);
23799         
23800         this.buttonEl = this.el.select('button.dropdown-button', true).first();
23801         this.buttonEl.on('click', this.onClick, this);
23802         
23803     },
23804     
23805     list : function()
23806     {
23807         if(this.isSubMenu){
23808             return this.el;
23809         }
23810         
23811         return this.el.select('ul.dropdown-menu', true).first();
23812     },
23813     
23814     onClick : function(e)
23815     {
23816         this.fireEvent("click", this, e);
23817     },
23818     
23819     onTriggerPress  : function(e)
23820     {   
23821         if (this.isVisible()) {
23822             this.hide();
23823         } else {
23824             this.show();
23825         }
23826     },
23827     
23828     isVisible : function(){
23829         return !this.hidden;
23830     },
23831     
23832     show : function()
23833     {
23834         this.fireEvent("beforeshow", this);
23835         
23836         this.hidden = false;
23837         this.el.addClass('open');
23838         
23839         Roo.get(document).on("mouseup", this.onMouseUp, this);
23840         
23841         this.fireEvent("show", this);
23842         
23843         
23844     },
23845     
23846     hide : function()
23847     {
23848         this.fireEvent("beforehide", this);
23849         
23850         this.hidden = true;
23851         this.el.removeClass('open');
23852         
23853         Roo.get(document).un("mouseup", this.onMouseUp);
23854         
23855         this.fireEvent("hide", this);
23856     },
23857     
23858     onMouseUp : function()
23859     {
23860         this.hide();
23861     }
23862     
23863 });
23864
23865  
23866  /*
23867  * - LGPL
23868  *
23869  * menu item
23870  * 
23871  */
23872 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
23873
23874 /**
23875  * @class Roo.bootstrap.menu.Item
23876  * @extends Roo.bootstrap.Component
23877  * Bootstrap MenuItem class
23878  * @cfg {Boolean} submenu (true | false) default false
23879  * @cfg {String} html text of the item
23880  * @cfg {String} href the link
23881  * @cfg {Boolean} disable (true | false) default false
23882  * @cfg {Boolean} preventDefault (true | false) default true
23883  * @cfg {String} icon Font awesome icon
23884  * @cfg {String} pos Submenu align to (left | right) default right 
23885  * 
23886  * 
23887  * @constructor
23888  * Create a new Item
23889  * @param {Object} config The config object
23890  */
23891
23892
23893 Roo.bootstrap.menu.Item = function(config){
23894     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
23895     this.addEvents({
23896         /**
23897          * @event mouseover
23898          * Fires when the mouse is hovering over this menu
23899          * @param {Roo.bootstrap.menu.Item} this
23900          * @param {Roo.EventObject} e
23901          */
23902         mouseover : true,
23903         /**
23904          * @event mouseout
23905          * Fires when the mouse exits this menu
23906          * @param {Roo.bootstrap.menu.Item} this
23907          * @param {Roo.EventObject} e
23908          */
23909         mouseout : true,
23910         // raw events
23911         /**
23912          * @event click
23913          * The raw click event for the entire grid.
23914          * @param {Roo.EventObject} e
23915          */
23916         click : true
23917     });
23918 };
23919
23920 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
23921     
23922     submenu : false,
23923     href : '',
23924     html : '',
23925     preventDefault: true,
23926     disable : false,
23927     icon : false,
23928     pos : 'right',
23929     
23930     getAutoCreate : function()
23931     {
23932         var text = [
23933             {
23934                 tag : 'span',
23935                 cls : 'roo-menu-item-text',
23936                 html : this.html
23937             }
23938         ];
23939         
23940         if(this.icon){
23941             text.unshift({
23942                 tag : 'i',
23943                 cls : 'fa ' + this.icon
23944             })
23945         }
23946         
23947         var cfg = {
23948             tag : 'li',
23949             cn : [
23950                 {
23951                     tag : 'a',
23952                     href : this.href || '#',
23953                     cn : text
23954                 }
23955             ]
23956         };
23957         
23958         if(this.disable){
23959             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
23960         }
23961         
23962         if(this.submenu){
23963             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
23964             
23965             if(this.pos == 'left'){
23966                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
23967             }
23968         }
23969         
23970         return cfg;
23971     },
23972     
23973     initEvents : function() 
23974     {
23975         this.el.on('mouseover', this.onMouseOver, this);
23976         this.el.on('mouseout', this.onMouseOut, this);
23977         
23978         this.el.select('a', true).first().on('click', this.onClick, this);
23979         
23980     },
23981     
23982     onClick : function(e)
23983     {
23984         if(this.preventDefault){
23985             e.preventDefault();
23986         }
23987         
23988         this.fireEvent("click", this, e);
23989     },
23990     
23991     onMouseOver : function(e)
23992     {
23993         if(this.submenu && this.pos == 'left'){
23994             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
23995         }
23996         
23997         this.fireEvent("mouseover", this, e);
23998     },
23999     
24000     onMouseOut : function(e)
24001     {
24002         this.fireEvent("mouseout", this, e);
24003     }
24004 });
24005
24006  
24007
24008  /*
24009  * - LGPL
24010  *
24011  * menu separator
24012  * 
24013  */
24014 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
24015
24016 /**
24017  * @class Roo.bootstrap.menu.Separator
24018  * @extends Roo.bootstrap.Component
24019  * Bootstrap Separator class
24020  * 
24021  * @constructor
24022  * Create a new Separator
24023  * @param {Object} config The config object
24024  */
24025
24026
24027 Roo.bootstrap.menu.Separator = function(config){
24028     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
24029 };
24030
24031 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
24032     
24033     getAutoCreate : function(){
24034         var cfg = {
24035             tag : 'li',
24036             cls: 'divider'
24037         };
24038         
24039         return cfg;
24040     }
24041    
24042 });
24043
24044  
24045
24046  /*
24047  * - LGPL
24048  *
24049  * Tooltip
24050  * 
24051  */
24052
24053 /**
24054  * @class Roo.bootstrap.Tooltip
24055  * Bootstrap Tooltip class
24056  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
24057  * to determine which dom element triggers the tooltip.
24058  * 
24059  * It needs to add support for additional attributes like tooltip-position
24060  * 
24061  * @constructor
24062  * Create a new Toolti
24063  * @param {Object} config The config object
24064  */
24065
24066 Roo.bootstrap.Tooltip = function(config){
24067     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
24068 };
24069
24070 Roo.apply(Roo.bootstrap.Tooltip, {
24071     /**
24072      * @function init initialize tooltip monitoring.
24073      * @static
24074      */
24075     currentEl : false,
24076     currentTip : false,
24077     currentRegion : false,
24078     
24079     //  init : delay?
24080     
24081     init : function()
24082     {
24083         Roo.get(document).on('mouseover', this.enter ,this);
24084         Roo.get(document).on('mouseout', this.leave, this);
24085          
24086         
24087         this.currentTip = new Roo.bootstrap.Tooltip();
24088     },
24089     
24090     enter : function(ev)
24091     {
24092         var dom = ev.getTarget();
24093         
24094         //Roo.log(['enter',dom]);
24095         var el = Roo.fly(dom);
24096         if (this.currentEl) {
24097             //Roo.log(dom);
24098             //Roo.log(this.currentEl);
24099             //Roo.log(this.currentEl.contains(dom));
24100             if (this.currentEl == el) {
24101                 return;
24102             }
24103             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
24104                 return;
24105             }
24106
24107         }
24108         
24109         if (this.currentTip.el) {
24110             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
24111         }    
24112         //Roo.log(ev);
24113         var bindEl = el;
24114         
24115         // you can not look for children, as if el is the body.. then everythign is the child..
24116         if (!el.attr('tooltip')) { //
24117             if (!el.select("[tooltip]").elements.length) {
24118                 return;
24119             }
24120             // is the mouse over this child...?
24121             bindEl = el.select("[tooltip]").first();
24122             var xy = ev.getXY();
24123             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
24124                 //Roo.log("not in region.");
24125                 return;
24126             }
24127             //Roo.log("child element over..");
24128             
24129         }
24130         this.currentEl = bindEl;
24131         this.currentTip.bind(bindEl);
24132         this.currentRegion = Roo.lib.Region.getRegion(dom);
24133         this.currentTip.enter();
24134         
24135     },
24136     leave : function(ev)
24137     {
24138         var dom = ev.getTarget();
24139         //Roo.log(['leave',dom]);
24140         if (!this.currentEl) {
24141             return;
24142         }
24143         
24144         
24145         if (dom != this.currentEl.dom) {
24146             return;
24147         }
24148         var xy = ev.getXY();
24149         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
24150             return;
24151         }
24152         // only activate leave if mouse cursor is outside... bounding box..
24153         
24154         
24155         
24156         
24157         if (this.currentTip) {
24158             this.currentTip.leave();
24159         }
24160         //Roo.log('clear currentEl');
24161         this.currentEl = false;
24162         
24163         
24164     },
24165     alignment : {
24166         'left' : ['r-l', [-2,0], 'right'],
24167         'right' : ['l-r', [2,0], 'left'],
24168         'bottom' : ['t-b', [0,2], 'top'],
24169         'top' : [ 'b-t', [0,-2], 'bottom']
24170     }
24171     
24172 });
24173
24174
24175 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
24176     
24177     
24178     bindEl : false,
24179     
24180     delay : null, // can be { show : 300 , hide: 500}
24181     
24182     timeout : null,
24183     
24184     hoverState : null, //???
24185     
24186     placement : 'bottom', 
24187     
24188     getAutoCreate : function(){
24189     
24190         var cfg = {
24191            cls : 'tooltip',
24192            role : 'tooltip',
24193            cn : [
24194                 {
24195                     cls : 'tooltip-arrow'
24196                 },
24197                 {
24198                     cls : 'tooltip-inner'
24199                 }
24200            ]
24201         };
24202         
24203         return cfg;
24204     },
24205     bind : function(el)
24206     {
24207         this.bindEl = el;
24208     },
24209       
24210     
24211     enter : function () {
24212        
24213         if (this.timeout != null) {
24214             clearTimeout(this.timeout);
24215         }
24216         
24217         this.hoverState = 'in';
24218          //Roo.log("enter - show");
24219         if (!this.delay || !this.delay.show) {
24220             this.show();
24221             return;
24222         }
24223         var _t = this;
24224         this.timeout = setTimeout(function () {
24225             if (_t.hoverState == 'in') {
24226                 _t.show();
24227             }
24228         }, this.delay.show);
24229     },
24230     leave : function()
24231     {
24232         clearTimeout(this.timeout);
24233     
24234         this.hoverState = 'out';
24235          if (!this.delay || !this.delay.hide) {
24236             this.hide();
24237             return;
24238         }
24239        
24240         var _t = this;
24241         this.timeout = setTimeout(function () {
24242             //Roo.log("leave - timeout");
24243             
24244             if (_t.hoverState == 'out') {
24245                 _t.hide();
24246                 Roo.bootstrap.Tooltip.currentEl = false;
24247             }
24248         }, delay);
24249     },
24250     
24251     show : function ()
24252     {
24253         if (!this.el) {
24254             this.render(document.body);
24255         }
24256         // set content.
24257         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
24258         
24259         var tip = this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
24260         
24261         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
24262         
24263         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
24264         
24265         var placement = typeof this.placement == 'function' ?
24266             this.placement.call(this, this.el, on_el) :
24267             this.placement;
24268             
24269         var autoToken = /\s?auto?\s?/i;
24270         var autoPlace = autoToken.test(placement);
24271         if (autoPlace) {
24272             placement = placement.replace(autoToken, '') || 'top';
24273         }
24274         
24275         //this.el.detach()
24276         //this.el.setXY([0,0]);
24277         this.el.show();
24278         //this.el.dom.style.display='block';
24279         
24280         //this.el.appendTo(on_el);
24281         
24282         var p = this.getPosition();
24283         var box = this.el.getBox();
24284         
24285         if (autoPlace) {
24286             // fixme..
24287         }
24288         
24289         var align = Roo.bootstrap.Tooltip.alignment[placement];
24290         
24291         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
24292         
24293         if(placement == 'top' || placement == 'bottom'){
24294             if(xy[0] < 0){
24295                 placement = 'right';
24296             }
24297             
24298             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
24299                 placement = 'left';
24300             }
24301         }
24302         
24303         align = Roo.bootstrap.Tooltip.alignment[placement];
24304         
24305         this.el.alignTo(this.bindEl, align[0],align[1]);
24306         //var arrow = this.el.select('.arrow',true).first();
24307         //arrow.set(align[2], 
24308         
24309         this.el.addClass(placement);
24310         
24311         this.el.addClass('in fade');
24312         
24313         this.hoverState = null;
24314         
24315         if (this.el.hasClass('fade')) {
24316             // fade it?
24317         }
24318         
24319     },
24320     hide : function()
24321     {
24322          
24323         if (!this.el) {
24324             return;
24325         }
24326         //this.el.setXY([0,0]);
24327         this.el.removeClass('in');
24328         //this.el.hide();
24329         
24330     }
24331     
24332 });
24333  
24334
24335  /*
24336  * - LGPL
24337  *
24338  * Location Picker
24339  * 
24340  */
24341
24342 /**
24343  * @class Roo.bootstrap.LocationPicker
24344  * @extends Roo.bootstrap.Component
24345  * Bootstrap LocationPicker class
24346  * @cfg {Number} latitude Position when init default 0
24347  * @cfg {Number} longitude Position when init default 0
24348  * @cfg {Number} zoom default 15
24349  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
24350  * @cfg {Boolean} mapTypeControl default false
24351  * @cfg {Boolean} disableDoubleClickZoom default false
24352  * @cfg {Boolean} scrollwheel default true
24353  * @cfg {Boolean} streetViewControl default false
24354  * @cfg {Number} radius default 0
24355  * @cfg {String} locationName
24356  * @cfg {Boolean} draggable default true
24357  * @cfg {Boolean} enableAutocomplete default false
24358  * @cfg {Boolean} enableReverseGeocode default true
24359  * @cfg {String} markerTitle
24360  * 
24361  * @constructor
24362  * Create a new LocationPicker
24363  * @param {Object} config The config object
24364  */
24365
24366
24367 Roo.bootstrap.LocationPicker = function(config){
24368     
24369     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
24370     
24371     this.addEvents({
24372         /**
24373          * @event initial
24374          * Fires when the picker initialized.
24375          * @param {Roo.bootstrap.LocationPicker} this
24376          * @param {Google Location} location
24377          */
24378         initial : true,
24379         /**
24380          * @event positionchanged
24381          * Fires when the picker position changed.
24382          * @param {Roo.bootstrap.LocationPicker} this
24383          * @param {Google Location} location
24384          */
24385         positionchanged : true,
24386         /**
24387          * @event resize
24388          * Fires when the map resize.
24389          * @param {Roo.bootstrap.LocationPicker} this
24390          */
24391         resize : true,
24392         /**
24393          * @event show
24394          * Fires when the map show.
24395          * @param {Roo.bootstrap.LocationPicker} this
24396          */
24397         show : true,
24398         /**
24399          * @event hide
24400          * Fires when the map hide.
24401          * @param {Roo.bootstrap.LocationPicker} this
24402          */
24403         hide : true,
24404         /**
24405          * @event mapClick
24406          * Fires when click the map.
24407          * @param {Roo.bootstrap.LocationPicker} this
24408          * @param {Map event} e
24409          */
24410         mapClick : true,
24411         /**
24412          * @event mapRightClick
24413          * Fires when right click the map.
24414          * @param {Roo.bootstrap.LocationPicker} this
24415          * @param {Map event} e
24416          */
24417         mapRightClick : true,
24418         /**
24419          * @event markerClick
24420          * Fires when click the marker.
24421          * @param {Roo.bootstrap.LocationPicker} this
24422          * @param {Map event} e
24423          */
24424         markerClick : true,
24425         /**
24426          * @event markerRightClick
24427          * Fires when right click the marker.
24428          * @param {Roo.bootstrap.LocationPicker} this
24429          * @param {Map event} e
24430          */
24431         markerRightClick : true,
24432         /**
24433          * @event OverlayViewDraw
24434          * Fires when OverlayView Draw
24435          * @param {Roo.bootstrap.LocationPicker} this
24436          */
24437         OverlayViewDraw : true,
24438         /**
24439          * @event OverlayViewOnAdd
24440          * Fires when OverlayView Draw
24441          * @param {Roo.bootstrap.LocationPicker} this
24442          */
24443         OverlayViewOnAdd : true,
24444         /**
24445          * @event OverlayViewOnRemove
24446          * Fires when OverlayView Draw
24447          * @param {Roo.bootstrap.LocationPicker} this
24448          */
24449         OverlayViewOnRemove : true,
24450         /**
24451          * @event OverlayViewShow
24452          * Fires when OverlayView Draw
24453          * @param {Roo.bootstrap.LocationPicker} this
24454          * @param {Pixel} cpx
24455          */
24456         OverlayViewShow : true,
24457         /**
24458          * @event OverlayViewHide
24459          * Fires when OverlayView Draw
24460          * @param {Roo.bootstrap.LocationPicker} this
24461          */
24462         OverlayViewHide : true,
24463         /**
24464          * @event loadexception
24465          * Fires when load google lib failed.
24466          * @param {Roo.bootstrap.LocationPicker} this
24467          */
24468         loadexception : true
24469     });
24470         
24471 };
24472
24473 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
24474     
24475     gMapContext: false,
24476     
24477     latitude: 0,
24478     longitude: 0,
24479     zoom: 15,
24480     mapTypeId: false,
24481     mapTypeControl: false,
24482     disableDoubleClickZoom: false,
24483     scrollwheel: true,
24484     streetViewControl: false,
24485     radius: 0,
24486     locationName: '',
24487     draggable: true,
24488     enableAutocomplete: false,
24489     enableReverseGeocode: true,
24490     markerTitle: '',
24491     
24492     getAutoCreate: function()
24493     {
24494
24495         var cfg = {
24496             tag: 'div',
24497             cls: 'roo-location-picker'
24498         };
24499         
24500         return cfg
24501     },
24502     
24503     initEvents: function(ct, position)
24504     {       
24505         if(!this.el.getWidth() || this.isApplied()){
24506             return;
24507         }
24508         
24509         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24510         
24511         this.initial();
24512     },
24513     
24514     initial: function()
24515     {
24516         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
24517             this.fireEvent('loadexception', this);
24518             return;
24519         }
24520         
24521         if(!this.mapTypeId){
24522             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
24523         }
24524         
24525         this.gMapContext = this.GMapContext();
24526         
24527         this.initOverlayView();
24528         
24529         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
24530         
24531         var _this = this;
24532                 
24533         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
24534             _this.setPosition(_this.gMapContext.marker.position);
24535         });
24536         
24537         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
24538             _this.fireEvent('mapClick', this, event);
24539             
24540         });
24541
24542         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
24543             _this.fireEvent('mapRightClick', this, event);
24544             
24545         });
24546         
24547         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
24548             _this.fireEvent('markerClick', this, event);
24549             
24550         });
24551
24552         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
24553             _this.fireEvent('markerRightClick', this, event);
24554             
24555         });
24556         
24557         this.setPosition(this.gMapContext.location);
24558         
24559         this.fireEvent('initial', this, this.gMapContext.location);
24560     },
24561     
24562     initOverlayView: function()
24563     {
24564         var _this = this;
24565         
24566         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
24567             
24568             draw: function()
24569             {
24570                 _this.fireEvent('OverlayViewDraw', _this);
24571             },
24572             
24573             onAdd: function()
24574             {
24575                 _this.fireEvent('OverlayViewOnAdd', _this);
24576             },
24577             
24578             onRemove: function()
24579             {
24580                 _this.fireEvent('OverlayViewOnRemove', _this);
24581             },
24582             
24583             show: function(cpx)
24584             {
24585                 _this.fireEvent('OverlayViewShow', _this, cpx);
24586             },
24587             
24588             hide: function()
24589             {
24590                 _this.fireEvent('OverlayViewHide', _this);
24591             }
24592             
24593         });
24594     },
24595     
24596     fromLatLngToContainerPixel: function(event)
24597     {
24598         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
24599     },
24600     
24601     isApplied: function() 
24602     {
24603         return this.getGmapContext() == false ? false : true;
24604     },
24605     
24606     getGmapContext: function() 
24607     {
24608         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
24609     },
24610     
24611     GMapContext: function() 
24612     {
24613         var position = new google.maps.LatLng(this.latitude, this.longitude);
24614         
24615         var _map = new google.maps.Map(this.el.dom, {
24616             center: position,
24617             zoom: this.zoom,
24618             mapTypeId: this.mapTypeId,
24619             mapTypeControl: this.mapTypeControl,
24620             disableDoubleClickZoom: this.disableDoubleClickZoom,
24621             scrollwheel: this.scrollwheel,
24622             streetViewControl: this.streetViewControl,
24623             locationName: this.locationName,
24624             draggable: this.draggable,
24625             enableAutocomplete: this.enableAutocomplete,
24626             enableReverseGeocode: this.enableReverseGeocode
24627         });
24628         
24629         var _marker = new google.maps.Marker({
24630             position: position,
24631             map: _map,
24632             title: this.markerTitle,
24633             draggable: this.draggable
24634         });
24635         
24636         return {
24637             map: _map,
24638             marker: _marker,
24639             circle: null,
24640             location: position,
24641             radius: this.radius,
24642             locationName: this.locationName,
24643             addressComponents: {
24644                 formatted_address: null,
24645                 addressLine1: null,
24646                 addressLine2: null,
24647                 streetName: null,
24648                 streetNumber: null,
24649                 city: null,
24650                 district: null,
24651                 state: null,
24652                 stateOrProvince: null
24653             },
24654             settings: this,
24655             domContainer: this.el.dom,
24656             geodecoder: new google.maps.Geocoder()
24657         };
24658     },
24659     
24660     drawCircle: function(center, radius, options) 
24661     {
24662         if (this.gMapContext.circle != null) {
24663             this.gMapContext.circle.setMap(null);
24664         }
24665         if (radius > 0) {
24666             radius *= 1;
24667             options = Roo.apply({}, options, {
24668                 strokeColor: "#0000FF",
24669                 strokeOpacity: .35,
24670                 strokeWeight: 2,
24671                 fillColor: "#0000FF",
24672                 fillOpacity: .2
24673             });
24674             
24675             options.map = this.gMapContext.map;
24676             options.radius = radius;
24677             options.center = center;
24678             this.gMapContext.circle = new google.maps.Circle(options);
24679             return this.gMapContext.circle;
24680         }
24681         
24682         return null;
24683     },
24684     
24685     setPosition: function(location) 
24686     {
24687         this.gMapContext.location = location;
24688         this.gMapContext.marker.setPosition(location);
24689         this.gMapContext.map.panTo(location);
24690         this.drawCircle(location, this.gMapContext.radius, {});
24691         
24692         var _this = this;
24693         
24694         if (this.gMapContext.settings.enableReverseGeocode) {
24695             this.gMapContext.geodecoder.geocode({
24696                 latLng: this.gMapContext.location
24697             }, function(results, status) {
24698                 
24699                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
24700                     _this.gMapContext.locationName = results[0].formatted_address;
24701                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
24702                     
24703                     _this.fireEvent('positionchanged', this, location);
24704                 }
24705             });
24706             
24707             return;
24708         }
24709         
24710         this.fireEvent('positionchanged', this, location);
24711     },
24712     
24713     resize: function()
24714     {
24715         google.maps.event.trigger(this.gMapContext.map, "resize");
24716         
24717         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
24718         
24719         this.fireEvent('resize', this);
24720     },
24721     
24722     setPositionByLatLng: function(latitude, longitude)
24723     {
24724         this.setPosition(new google.maps.LatLng(latitude, longitude));
24725     },
24726     
24727     getCurrentPosition: function() 
24728     {
24729         return {
24730             latitude: this.gMapContext.location.lat(),
24731             longitude: this.gMapContext.location.lng()
24732         };
24733     },
24734     
24735     getAddressName: function() 
24736     {
24737         return this.gMapContext.locationName;
24738     },
24739     
24740     getAddressComponents: function() 
24741     {
24742         return this.gMapContext.addressComponents;
24743     },
24744     
24745     address_component_from_google_geocode: function(address_components) 
24746     {
24747         var result = {};
24748         
24749         for (var i = 0; i < address_components.length; i++) {
24750             var component = address_components[i];
24751             if (component.types.indexOf("postal_code") >= 0) {
24752                 result.postalCode = component.short_name;
24753             } else if (component.types.indexOf("street_number") >= 0) {
24754                 result.streetNumber = component.short_name;
24755             } else if (component.types.indexOf("route") >= 0) {
24756                 result.streetName = component.short_name;
24757             } else if (component.types.indexOf("neighborhood") >= 0) {
24758                 result.city = component.short_name;
24759             } else if (component.types.indexOf("locality") >= 0) {
24760                 result.city = component.short_name;
24761             } else if (component.types.indexOf("sublocality") >= 0) {
24762                 result.district = component.short_name;
24763             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
24764                 result.stateOrProvince = component.short_name;
24765             } else if (component.types.indexOf("country") >= 0) {
24766                 result.country = component.short_name;
24767             }
24768         }
24769         
24770         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
24771         result.addressLine2 = "";
24772         return result;
24773     },
24774     
24775     setZoomLevel: function(zoom)
24776     {
24777         this.gMapContext.map.setZoom(zoom);
24778     },
24779     
24780     show: function()
24781     {
24782         if(!this.el){
24783             return;
24784         }
24785         
24786         this.el.show();
24787         
24788         this.resize();
24789         
24790         this.fireEvent('show', this);
24791     },
24792     
24793     hide: function()
24794     {
24795         if(!this.el){
24796             return;
24797         }
24798         
24799         this.el.hide();
24800         
24801         this.fireEvent('hide', this);
24802     }
24803     
24804 });
24805
24806 Roo.apply(Roo.bootstrap.LocationPicker, {
24807     
24808     OverlayView : function(map, options)
24809     {
24810         options = options || {};
24811         
24812         this.setMap(map);
24813     }
24814     
24815     
24816 });/*
24817  * - LGPL
24818  *
24819  * Alert
24820  * 
24821  */
24822
24823 /**
24824  * @class Roo.bootstrap.Alert
24825  * @extends Roo.bootstrap.Component
24826  * Bootstrap Alert class
24827  * @cfg {String} title The title of alert
24828  * @cfg {String} html The content of alert
24829  * @cfg {String} weight (  success | info | warning | danger )
24830  * @cfg {String} faicon font-awesomeicon
24831  * 
24832  * @constructor
24833  * Create a new alert
24834  * @param {Object} config The config object
24835  */
24836
24837
24838 Roo.bootstrap.Alert = function(config){
24839     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
24840     
24841 };
24842
24843 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
24844     
24845     title: '',
24846     html: '',
24847     weight: false,
24848     faicon: false,
24849     
24850     getAutoCreate : function()
24851     {
24852         
24853         var cfg = {
24854             tag : 'div',
24855             cls : 'alert',
24856             cn : [
24857                 {
24858                     tag : 'i',
24859                     cls : 'roo-alert-icon'
24860                     
24861                 },
24862                 {
24863                     tag : 'b',
24864                     cls : 'roo-alert-title',
24865                     html : this.title
24866                 },
24867                 {
24868                     tag : 'span',
24869                     cls : 'roo-alert-text',
24870                     html : this.html
24871                 }
24872             ]
24873         };
24874         
24875         if(this.faicon){
24876             cfg.cn[0].cls += ' fa ' + this.faicon;
24877         }
24878         
24879         if(this.weight){
24880             cfg.cls += ' alert-' + this.weight;
24881         }
24882         
24883         return cfg;
24884     },
24885     
24886     initEvents: function() 
24887     {
24888         this.el.setVisibilityMode(Roo.Element.DISPLAY);
24889     },
24890     
24891     setTitle : function(str)
24892     {
24893         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
24894     },
24895     
24896     setText : function(str)
24897     {
24898         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
24899     },
24900     
24901     setWeight : function(weight)
24902     {
24903         if(this.weight){
24904             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
24905         }
24906         
24907         this.weight = weight;
24908         
24909         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
24910     },
24911     
24912     setIcon : function(icon)
24913     {
24914         if(this.faicon){
24915             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
24916         }
24917         
24918         this.faicon = icon;
24919         
24920         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
24921     },
24922     
24923     hide: function() 
24924     {
24925         this.el.hide();   
24926     },
24927     
24928     show: function() 
24929     {  
24930         this.el.show();   
24931     }
24932     
24933 });
24934
24935  
24936 /*
24937 * Licence: LGPL
24938 */
24939
24940 /**
24941  * @class Roo.bootstrap.UploadCropbox
24942  * @extends Roo.bootstrap.Component
24943  * Bootstrap UploadCropbox class
24944  * @cfg {String} emptyText show when image has been loaded
24945  * @cfg {String} rotateNotify show when image too small to rotate
24946  * @cfg {Number} errorTimeout default 3000
24947  * @cfg {Number} minWidth default 300
24948  * @cfg {Number} minHeight default 300
24949  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
24950  * @cfg {Boolean} isDocument (true|false) default false
24951  * @cfg {String} url action url
24952  * @cfg {String} paramName default 'imageUpload'
24953  * @cfg {String} method default POST
24954  * @cfg {Boolean} loadMask (true|false) default true
24955  * @cfg {Boolean} loadingText default 'Loading...'
24956  * 
24957  * @constructor
24958  * Create a new UploadCropbox
24959  * @param {Object} config The config object
24960  */
24961
24962 Roo.bootstrap.UploadCropbox = function(config){
24963     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
24964     
24965     this.addEvents({
24966         /**
24967          * @event beforeselectfile
24968          * Fire before select file
24969          * @param {Roo.bootstrap.UploadCropbox} this
24970          */
24971         "beforeselectfile" : true,
24972         /**
24973          * @event initial
24974          * Fire after initEvent
24975          * @param {Roo.bootstrap.UploadCropbox} this
24976          */
24977         "initial" : true,
24978         /**
24979          * @event crop
24980          * Fire after initEvent
24981          * @param {Roo.bootstrap.UploadCropbox} this
24982          * @param {String} data
24983          */
24984         "crop" : true,
24985         /**
24986          * @event prepare
24987          * Fire when preparing the file data
24988          * @param {Roo.bootstrap.UploadCropbox} this
24989          * @param {Object} file
24990          */
24991         "prepare" : true,
24992         /**
24993          * @event exception
24994          * Fire when get exception
24995          * @param {Roo.bootstrap.UploadCropbox} this
24996          * @param {XMLHttpRequest} xhr
24997          */
24998         "exception" : true,
24999         /**
25000          * @event beforeloadcanvas
25001          * Fire before load the canvas
25002          * @param {Roo.bootstrap.UploadCropbox} this
25003          * @param {String} src
25004          */
25005         "beforeloadcanvas" : true,
25006         /**
25007          * @event trash
25008          * Fire when trash image
25009          * @param {Roo.bootstrap.UploadCropbox} this
25010          */
25011         "trash" : true,
25012         /**
25013          * @event download
25014          * Fire when download the image
25015          * @param {Roo.bootstrap.UploadCropbox} this
25016          */
25017         "download" : true,
25018         /**
25019          * @event footerbuttonclick
25020          * Fire when footerbuttonclick
25021          * @param {Roo.bootstrap.UploadCropbox} this
25022          * @param {String} type
25023          */
25024         "footerbuttonclick" : true,
25025         /**
25026          * @event resize
25027          * Fire when resize
25028          * @param {Roo.bootstrap.UploadCropbox} this
25029          */
25030         "resize" : true,
25031         /**
25032          * @event rotate
25033          * Fire when rotate the image
25034          * @param {Roo.bootstrap.UploadCropbox} this
25035          * @param {String} pos
25036          */
25037         "rotate" : true,
25038         /**
25039          * @event inspect
25040          * Fire when inspect the file
25041          * @param {Roo.bootstrap.UploadCropbox} this
25042          * @param {Object} file
25043          */
25044         "inspect" : true,
25045         /**
25046          * @event upload
25047          * Fire when xhr upload the file
25048          * @param {Roo.bootstrap.UploadCropbox} this
25049          * @param {Object} data
25050          */
25051         "upload" : true,
25052         /**
25053          * @event arrange
25054          * Fire when arrange the file data
25055          * @param {Roo.bootstrap.UploadCropbox} this
25056          * @param {Object} formData
25057          */
25058         "arrange" : true
25059     });
25060     
25061     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
25062 };
25063
25064 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
25065     
25066     emptyText : 'Click to upload image',
25067     rotateNotify : 'Image is too small to rotate',
25068     errorTimeout : 3000,
25069     scale : 0,
25070     baseScale : 1,
25071     rotate : 0,
25072     dragable : false,
25073     pinching : false,
25074     mouseX : 0,
25075     mouseY : 0,
25076     cropData : false,
25077     minWidth : 300,
25078     minHeight : 300,
25079     file : false,
25080     exif : {},
25081     baseRotate : 1,
25082     cropType : 'image/jpeg',
25083     buttons : false,
25084     canvasLoaded : false,
25085     isDocument : false,
25086     method : 'POST',
25087     paramName : 'imageUpload',
25088     loadMask : true,
25089     loadingText : 'Loading...',
25090     maskEl : false,
25091     
25092     getAutoCreate : function()
25093     {
25094         var cfg = {
25095             tag : 'div',
25096             cls : 'roo-upload-cropbox',
25097             cn : [
25098                 {
25099                     tag : 'input',
25100                     cls : 'roo-upload-cropbox-selector',
25101                     type : 'file'
25102                 },
25103                 {
25104                     tag : 'div',
25105                     cls : 'roo-upload-cropbox-body',
25106                     style : 'cursor:pointer',
25107                     cn : [
25108                         {
25109                             tag : 'div',
25110                             cls : 'roo-upload-cropbox-preview'
25111                         },
25112                         {
25113                             tag : 'div',
25114                             cls : 'roo-upload-cropbox-thumb'
25115                         },
25116                         {
25117                             tag : 'div',
25118                             cls : 'roo-upload-cropbox-empty-notify',
25119                             html : this.emptyText
25120                         },
25121                         {
25122                             tag : 'div',
25123                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
25124                             html : this.rotateNotify
25125                         }
25126                     ]
25127                 },
25128                 {
25129                     tag : 'div',
25130                     cls : 'roo-upload-cropbox-footer',
25131                     cn : {
25132                         tag : 'div',
25133                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
25134                         cn : []
25135                     }
25136                 }
25137             ]
25138         };
25139         
25140         return cfg;
25141     },
25142     
25143     onRender : function(ct, position)
25144     {
25145         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
25146         
25147         if (this.buttons.length) {
25148             
25149             Roo.each(this.buttons, function(bb) {
25150                 
25151                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
25152                 
25153                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
25154                 
25155             }, this);
25156         }
25157         
25158         if(this.loadMask){
25159             this.maskEl = this.el;
25160         }
25161     },
25162     
25163     initEvents : function()
25164     {
25165         this.urlAPI = (window.createObjectURL && window) || 
25166                                 (window.URL && URL.revokeObjectURL && URL) || 
25167                                 (window.webkitURL && webkitURL);
25168                         
25169         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
25170         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25171         
25172         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
25173         this.selectorEl.hide();
25174         
25175         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
25176         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25177         
25178         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
25179         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25180         this.thumbEl.hide();
25181         
25182         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
25183         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25184         
25185         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
25186         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25187         this.errorEl.hide();
25188         
25189         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
25190         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
25191         this.footerEl.hide();
25192         
25193         this.setThumbBoxSize();
25194         
25195         this.bind();
25196         
25197         this.resize();
25198         
25199         this.fireEvent('initial', this);
25200     },
25201
25202     bind : function()
25203     {
25204         var _this = this;
25205         
25206         window.addEventListener("resize", function() { _this.resize(); } );
25207         
25208         this.bodyEl.on('click', this.beforeSelectFile, this);
25209         
25210         if(Roo.isTouch){
25211             this.bodyEl.on('touchstart', this.onTouchStart, this);
25212             this.bodyEl.on('touchmove', this.onTouchMove, this);
25213             this.bodyEl.on('touchend', this.onTouchEnd, this);
25214         }
25215         
25216         if(!Roo.isTouch){
25217             this.bodyEl.on('mousedown', this.onMouseDown, this);
25218             this.bodyEl.on('mousemove', this.onMouseMove, this);
25219             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
25220             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
25221             Roo.get(document).on('mouseup', this.onMouseUp, this);
25222         }
25223         
25224         this.selectorEl.on('change', this.onFileSelected, this);
25225     },
25226     
25227     reset : function()
25228     {    
25229         this.scale = 0;
25230         this.baseScale = 1;
25231         this.rotate = 0;
25232         this.baseRotate = 1;
25233         this.dragable = false;
25234         this.pinching = false;
25235         this.mouseX = 0;
25236         this.mouseY = 0;
25237         this.cropData = false;
25238         this.notifyEl.dom.innerHTML = this.emptyText;
25239         
25240         this.selectorEl.dom.value = '';
25241         
25242     },
25243     
25244     resize : function()
25245     {
25246         if(this.fireEvent('resize', this) != false){
25247             this.setThumbBoxPosition();
25248             this.setCanvasPosition();
25249         }
25250     },
25251     
25252     onFooterButtonClick : function(e, el, o, type)
25253     {
25254         switch (type) {
25255             case 'rotate-left' :
25256                 this.onRotateLeft(e);
25257                 break;
25258             case 'rotate-right' :
25259                 this.onRotateRight(e);
25260                 break;
25261             case 'picture' :
25262                 this.beforeSelectFile(e);
25263                 break;
25264             case 'trash' :
25265                 this.trash(e);
25266                 break;
25267             case 'crop' :
25268                 this.crop(e);
25269                 break;
25270             case 'download' :
25271                 this.download(e);
25272                 break;
25273             default :
25274                 break;
25275         }
25276         
25277         this.fireEvent('footerbuttonclick', this, type);
25278     },
25279     
25280     beforeSelectFile : function(e)
25281     {
25282         e.preventDefault();
25283         
25284         if(this.fireEvent('beforeselectfile', this) != false){
25285             this.selectorEl.dom.click();
25286         }
25287     },
25288     
25289     onFileSelected : function(e)
25290     {
25291         e.preventDefault();
25292         
25293         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
25294             return;
25295         }
25296         
25297         var file = this.selectorEl.dom.files[0];
25298         
25299         if(this.fireEvent('inspect', this, file) != false){
25300             this.prepare(file);
25301         }
25302         
25303     },
25304     
25305     trash : function(e)
25306     {
25307         this.fireEvent('trash', this);
25308     },
25309     
25310     download : function(e)
25311     {
25312         this.fireEvent('download', this);
25313     },
25314     
25315     loadCanvas : function(src)
25316     {   
25317         if(this.fireEvent('beforeloadcanvas', this, src) != false){
25318             
25319             this.reset();
25320             
25321             this.imageEl = document.createElement('img');
25322             
25323             var _this = this;
25324             
25325             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
25326             
25327             this.imageEl.src = src;
25328         }
25329     },
25330     
25331     onLoadCanvas : function()
25332     {   
25333         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
25334         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
25335         
25336         this.bodyEl.un('click', this.beforeSelectFile, this);
25337         
25338         this.notifyEl.hide();
25339         this.thumbEl.show();
25340         this.footerEl.show();
25341         
25342         this.baseRotateLevel();
25343         
25344         if(this.isDocument){
25345             this.setThumbBoxSize();
25346         }
25347         
25348         this.setThumbBoxPosition();
25349         
25350         this.baseScaleLevel();
25351         
25352         this.draw();
25353         
25354         this.resize();
25355         
25356         this.canvasLoaded = true;
25357         
25358         if(this.loadMask){
25359             this.maskEl.unmask();
25360         }
25361         
25362     },
25363     
25364     setCanvasPosition : function()
25365     {   
25366         if(!this.canvasEl){
25367             return;
25368         }
25369         
25370         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
25371         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
25372         
25373         this.previewEl.setLeft(pw);
25374         this.previewEl.setTop(ph);
25375         
25376     },
25377     
25378     onMouseDown : function(e)
25379     {   
25380         e.stopEvent();
25381         
25382         this.dragable = true;
25383         this.pinching = false;
25384         
25385         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
25386             this.dragable = false;
25387             return;
25388         }
25389         
25390         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25391         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25392         
25393     },
25394     
25395     onMouseMove : function(e)
25396     {   
25397         e.stopEvent();
25398         
25399         if(!this.canvasLoaded){
25400             return;
25401         }
25402         
25403         if (!this.dragable){
25404             return;
25405         }
25406         
25407         var minX = Math.ceil(this.thumbEl.getLeft(true));
25408         var minY = Math.ceil(this.thumbEl.getTop(true));
25409         
25410         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
25411         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
25412         
25413         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25414         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25415         
25416         x = x - this.mouseX;
25417         y = y - this.mouseY;
25418         
25419         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
25420         var bgY = Math.ceil(y + this.previewEl.getTop(true));
25421         
25422         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
25423         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
25424         
25425         this.previewEl.setLeft(bgX);
25426         this.previewEl.setTop(bgY);
25427         
25428         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
25429         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
25430     },
25431     
25432     onMouseUp : function(e)
25433     {   
25434         e.stopEvent();
25435         
25436         this.dragable = false;
25437     },
25438     
25439     onMouseWheel : function(e)
25440     {   
25441         e.stopEvent();
25442         
25443         this.startScale = this.scale;
25444         
25445         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
25446         
25447         if(!this.zoomable()){
25448             this.scale = this.startScale;
25449             return;
25450         }
25451         
25452         this.draw();
25453         
25454         return;
25455     },
25456     
25457     zoomable : function()
25458     {
25459         var minScale = this.thumbEl.getWidth() / this.minWidth;
25460         
25461         if(this.minWidth < this.minHeight){
25462             minScale = this.thumbEl.getHeight() / this.minHeight;
25463         }
25464         
25465         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
25466         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
25467         
25468         if(
25469                 this.isDocument &&
25470                 (this.rotate == 0 || this.rotate == 180) && 
25471                 (
25472                     width > this.imageEl.OriginWidth || 
25473                     height > this.imageEl.OriginHeight ||
25474                     (width < this.minWidth && height < this.minHeight)
25475                 )
25476         ){
25477             return false;
25478         }
25479         
25480         if(
25481                 this.isDocument &&
25482                 (this.rotate == 90 || this.rotate == 270) && 
25483                 (
25484                     width > this.imageEl.OriginWidth || 
25485                     height > this.imageEl.OriginHeight ||
25486                     (width < this.minHeight && height < this.minWidth)
25487                 )
25488         ){
25489             return false;
25490         }
25491         
25492         if(
25493                 !this.isDocument &&
25494                 (this.rotate == 0 || this.rotate == 180) && 
25495                 (
25496                     width < this.minWidth || 
25497                     width > this.imageEl.OriginWidth || 
25498                     height < this.minHeight || 
25499                     height > this.imageEl.OriginHeight
25500                 )
25501         ){
25502             return false;
25503         }
25504         
25505         if(
25506                 !this.isDocument &&
25507                 (this.rotate == 90 || this.rotate == 270) && 
25508                 (
25509                     width < this.minHeight || 
25510                     width > this.imageEl.OriginWidth || 
25511                     height < this.minWidth || 
25512                     height > this.imageEl.OriginHeight
25513                 )
25514         ){
25515             return false;
25516         }
25517         
25518         return true;
25519         
25520     },
25521     
25522     onRotateLeft : function(e)
25523     {   
25524         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25525             
25526             var minScale = this.thumbEl.getWidth() / this.minWidth;
25527             
25528             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25529             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25530             
25531             this.startScale = this.scale;
25532             
25533             while (this.getScaleLevel() < minScale){
25534             
25535                 this.scale = this.scale + 1;
25536                 
25537                 if(!this.zoomable()){
25538                     break;
25539                 }
25540                 
25541                 if(
25542                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25543                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25544                 ){
25545                     continue;
25546                 }
25547                 
25548                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25549
25550                 this.draw();
25551                 
25552                 return;
25553             }
25554             
25555             this.scale = this.startScale;
25556             
25557             this.onRotateFail();
25558             
25559             return false;
25560         }
25561         
25562         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
25563
25564         if(this.isDocument){
25565             this.setThumbBoxSize();
25566             this.setThumbBoxPosition();
25567             this.setCanvasPosition();
25568         }
25569         
25570         this.draw();
25571         
25572         this.fireEvent('rotate', this, 'left');
25573         
25574     },
25575     
25576     onRotateRight : function(e)
25577     {
25578         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
25579             
25580             var minScale = this.thumbEl.getWidth() / this.minWidth;
25581         
25582             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
25583             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
25584             
25585             this.startScale = this.scale;
25586             
25587             while (this.getScaleLevel() < minScale){
25588             
25589                 this.scale = this.scale + 1;
25590                 
25591                 if(!this.zoomable()){
25592                     break;
25593                 }
25594                 
25595                 if(
25596                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
25597                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
25598                 ){
25599                     continue;
25600                 }
25601                 
25602                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25603
25604                 this.draw();
25605                 
25606                 return;
25607             }
25608             
25609             this.scale = this.startScale;
25610             
25611             this.onRotateFail();
25612             
25613             return false;
25614         }
25615         
25616         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
25617
25618         if(this.isDocument){
25619             this.setThumbBoxSize();
25620             this.setThumbBoxPosition();
25621             this.setCanvasPosition();
25622         }
25623         
25624         this.draw();
25625         
25626         this.fireEvent('rotate', this, 'right');
25627     },
25628     
25629     onRotateFail : function()
25630     {
25631         this.errorEl.show(true);
25632         
25633         var _this = this;
25634         
25635         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
25636     },
25637     
25638     draw : function()
25639     {
25640         this.previewEl.dom.innerHTML = '';
25641         
25642         var canvasEl = document.createElement("canvas");
25643         
25644         var contextEl = canvasEl.getContext("2d");
25645         
25646         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25647         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25648         var center = this.imageEl.OriginWidth / 2;
25649         
25650         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
25651             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25652             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25653             center = this.imageEl.OriginHeight / 2;
25654         }
25655         
25656         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
25657         
25658         contextEl.translate(center, center);
25659         contextEl.rotate(this.rotate * Math.PI / 180);
25660
25661         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25662         
25663         this.canvasEl = document.createElement("canvas");
25664         
25665         this.contextEl = this.canvasEl.getContext("2d");
25666         
25667         switch (this.rotate) {
25668             case 0 :
25669                 
25670                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25671                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25672                 
25673                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25674                 
25675                 break;
25676             case 90 : 
25677                 
25678                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25679                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25680                 
25681                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25682                     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);
25683                     break;
25684                 }
25685                 
25686                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25687                 
25688                 break;
25689             case 180 :
25690                 
25691                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
25692                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
25693                 
25694                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25695                     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);
25696                     break;
25697                 }
25698                 
25699                 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);
25700                 
25701                 break;
25702             case 270 :
25703                 
25704                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
25705                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
25706         
25707                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
25708                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
25709                     break;
25710                 }
25711                 
25712                 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);
25713                 
25714                 break;
25715             default : 
25716                 break;
25717         }
25718         
25719         this.previewEl.appendChild(this.canvasEl);
25720         
25721         this.setCanvasPosition();
25722     },
25723     
25724     crop : function()
25725     {
25726         if(!this.canvasLoaded){
25727             return;
25728         }
25729         
25730         var imageCanvas = document.createElement("canvas");
25731         
25732         var imageContext = imageCanvas.getContext("2d");
25733         
25734         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25735         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
25736         
25737         var center = imageCanvas.width / 2;
25738         
25739         imageContext.translate(center, center);
25740         
25741         imageContext.rotate(this.rotate * Math.PI / 180);
25742         
25743         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
25744         
25745         var canvas = document.createElement("canvas");
25746         
25747         var context = canvas.getContext("2d");
25748                 
25749         canvas.width = this.minWidth;
25750         canvas.height = this.minHeight;
25751
25752         switch (this.rotate) {
25753             case 0 :
25754                 
25755                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25756                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25757                 
25758                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25759                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25760                 
25761                 var targetWidth = this.minWidth - 2 * x;
25762                 var targetHeight = this.minHeight - 2 * y;
25763                 
25764                 var scale = 1;
25765                 
25766                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25767                     scale = targetWidth / width;
25768                 }
25769                 
25770                 if(x > 0 && y == 0){
25771                     scale = targetHeight / height;
25772                 }
25773                 
25774                 if(x > 0 && y > 0){
25775                     scale = targetWidth / width;
25776                     
25777                     if(width < height){
25778                         scale = targetHeight / height;
25779                     }
25780                 }
25781                 
25782                 context.scale(scale, scale);
25783                 
25784                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25785                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25786
25787                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25788                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25789
25790                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25791                 
25792                 break;
25793             case 90 : 
25794                 
25795                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25796                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25797                 
25798                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25799                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25800                 
25801                 var targetWidth = this.minWidth - 2 * x;
25802                 var targetHeight = this.minHeight - 2 * y;
25803                 
25804                 var scale = 1;
25805                 
25806                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25807                     scale = targetWidth / width;
25808                 }
25809                 
25810                 if(x > 0 && y == 0){
25811                     scale = targetHeight / height;
25812                 }
25813                 
25814                 if(x > 0 && y > 0){
25815                     scale = targetWidth / width;
25816                     
25817                     if(width < height){
25818                         scale = targetHeight / height;
25819                     }
25820                 }
25821                 
25822                 context.scale(scale, scale);
25823                 
25824                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25825                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25826
25827                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25828                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25829                 
25830                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25831                 
25832                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25833                 
25834                 break;
25835             case 180 :
25836                 
25837                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
25838                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
25839                 
25840                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25841                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25842                 
25843                 var targetWidth = this.minWidth - 2 * x;
25844                 var targetHeight = this.minHeight - 2 * y;
25845                 
25846                 var scale = 1;
25847                 
25848                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25849                     scale = targetWidth / width;
25850                 }
25851                 
25852                 if(x > 0 && y == 0){
25853                     scale = targetHeight / height;
25854                 }
25855                 
25856                 if(x > 0 && y > 0){
25857                     scale = targetWidth / width;
25858                     
25859                     if(width < height){
25860                         scale = targetHeight / height;
25861                     }
25862                 }
25863                 
25864                 context.scale(scale, scale);
25865                 
25866                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25867                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25868
25869                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25870                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25871
25872                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25873                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
25874                 
25875                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25876                 
25877                 break;
25878             case 270 :
25879                 
25880                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
25881                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
25882                 
25883                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
25884                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
25885                 
25886                 var targetWidth = this.minWidth - 2 * x;
25887                 var targetHeight = this.minHeight - 2 * y;
25888                 
25889                 var scale = 1;
25890                 
25891                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
25892                     scale = targetWidth / width;
25893                 }
25894                 
25895                 if(x > 0 && y == 0){
25896                     scale = targetHeight / height;
25897                 }
25898                 
25899                 if(x > 0 && y > 0){
25900                     scale = targetWidth / width;
25901                     
25902                     if(width < height){
25903                         scale = targetHeight / height;
25904                     }
25905                 }
25906                 
25907                 context.scale(scale, scale);
25908                 
25909                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
25910                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
25911
25912                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
25913                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
25914                 
25915                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
25916                 
25917                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
25918                 
25919                 break;
25920             default : 
25921                 break;
25922         }
25923         
25924         this.cropData = canvas.toDataURL(this.cropType);
25925         
25926         if(this.fireEvent('crop', this, this.cropData) !== false){
25927             this.process(this.file, this.cropData);
25928         }
25929         
25930         return;
25931         
25932     },
25933     
25934     setThumbBoxSize : function()
25935     {
25936         var width, height;
25937         
25938         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
25939             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
25940             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
25941             
25942             this.minWidth = width;
25943             this.minHeight = height;
25944             
25945             if(this.rotate == 90 || this.rotate == 270){
25946                 this.minWidth = height;
25947                 this.minHeight = width;
25948             }
25949         }
25950         
25951         height = 300;
25952         width = Math.ceil(this.minWidth * height / this.minHeight);
25953         
25954         if(this.minWidth > this.minHeight){
25955             width = 300;
25956             height = Math.ceil(this.minHeight * width / this.minWidth);
25957         }
25958         
25959         this.thumbEl.setStyle({
25960             width : width + 'px',
25961             height : height + 'px'
25962         });
25963
25964         return;
25965             
25966     },
25967     
25968     setThumbBoxPosition : function()
25969     {
25970         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
25971         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
25972         
25973         this.thumbEl.setLeft(x);
25974         this.thumbEl.setTop(y);
25975         
25976     },
25977     
25978     baseRotateLevel : function()
25979     {
25980         this.baseRotate = 1;
25981         
25982         if(
25983                 typeof(this.exif) != 'undefined' &&
25984                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
25985                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
25986         ){
25987             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
25988         }
25989         
25990         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
25991         
25992     },
25993     
25994     baseScaleLevel : function()
25995     {
25996         var width, height;
25997         
25998         if(this.isDocument){
25999             
26000             if(this.baseRotate == 6 || this.baseRotate == 8){
26001             
26002                 height = this.thumbEl.getHeight();
26003                 this.baseScale = height / this.imageEl.OriginWidth;
26004
26005                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
26006                     width = this.thumbEl.getWidth();
26007                     this.baseScale = width / this.imageEl.OriginHeight;
26008                 }
26009
26010                 return;
26011             }
26012
26013             height = this.thumbEl.getHeight();
26014             this.baseScale = height / this.imageEl.OriginHeight;
26015
26016             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
26017                 width = this.thumbEl.getWidth();
26018                 this.baseScale = width / this.imageEl.OriginWidth;
26019             }
26020
26021             return;
26022         }
26023         
26024         if(this.baseRotate == 6 || this.baseRotate == 8){
26025             
26026             width = this.thumbEl.getHeight();
26027             this.baseScale = width / this.imageEl.OriginHeight;
26028             
26029             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
26030                 height = this.thumbEl.getWidth();
26031                 this.baseScale = height / this.imageEl.OriginHeight;
26032             }
26033             
26034             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26035                 height = this.thumbEl.getWidth();
26036                 this.baseScale = height / this.imageEl.OriginHeight;
26037                 
26038                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
26039                     width = this.thumbEl.getHeight();
26040                     this.baseScale = width / this.imageEl.OriginWidth;
26041                 }
26042             }
26043             
26044             return;
26045         }
26046         
26047         width = this.thumbEl.getWidth();
26048         this.baseScale = width / this.imageEl.OriginWidth;
26049         
26050         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
26051             height = this.thumbEl.getHeight();
26052             this.baseScale = height / this.imageEl.OriginHeight;
26053         }
26054         
26055         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
26056             
26057             height = this.thumbEl.getHeight();
26058             this.baseScale = height / this.imageEl.OriginHeight;
26059             
26060             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
26061                 width = this.thumbEl.getWidth();
26062                 this.baseScale = width / this.imageEl.OriginWidth;
26063             }
26064             
26065         }
26066         
26067         return;
26068     },
26069     
26070     getScaleLevel : function()
26071     {
26072         return this.baseScale * Math.pow(1.1, this.scale);
26073     },
26074     
26075     onTouchStart : function(e)
26076     {
26077         if(!this.canvasLoaded){
26078             this.beforeSelectFile(e);
26079             return;
26080         }
26081         
26082         var touches = e.browserEvent.touches;
26083         
26084         if(!touches){
26085             return;
26086         }
26087         
26088         if(touches.length == 1){
26089             this.onMouseDown(e);
26090             return;
26091         }
26092         
26093         if(touches.length != 2){
26094             return;
26095         }
26096         
26097         var coords = [];
26098         
26099         for(var i = 0, finger; finger = touches[i]; i++){
26100             coords.push(finger.pageX, finger.pageY);
26101         }
26102         
26103         var x = Math.pow(coords[0] - coords[2], 2);
26104         var y = Math.pow(coords[1] - coords[3], 2);
26105         
26106         this.startDistance = Math.sqrt(x + y);
26107         
26108         this.startScale = this.scale;
26109         
26110         this.pinching = true;
26111         this.dragable = false;
26112         
26113     },
26114     
26115     onTouchMove : function(e)
26116     {
26117         if(!this.pinching && !this.dragable){
26118             return;
26119         }
26120         
26121         var touches = e.browserEvent.touches;
26122         
26123         if(!touches){
26124             return;
26125         }
26126         
26127         if(this.dragable){
26128             this.onMouseMove(e);
26129             return;
26130         }
26131         
26132         var coords = [];
26133         
26134         for(var i = 0, finger; finger = touches[i]; i++){
26135             coords.push(finger.pageX, finger.pageY);
26136         }
26137         
26138         var x = Math.pow(coords[0] - coords[2], 2);
26139         var y = Math.pow(coords[1] - coords[3], 2);
26140         
26141         this.endDistance = Math.sqrt(x + y);
26142         
26143         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
26144         
26145         if(!this.zoomable()){
26146             this.scale = this.startScale;
26147             return;
26148         }
26149         
26150         this.draw();
26151         
26152     },
26153     
26154     onTouchEnd : function(e)
26155     {
26156         this.pinching = false;
26157         this.dragable = false;
26158         
26159     },
26160     
26161     process : function(file, crop)
26162     {
26163         if(this.loadMask){
26164             this.maskEl.mask(this.loadingText);
26165         }
26166         
26167         this.xhr = new XMLHttpRequest();
26168         
26169         file.xhr = this.xhr;
26170
26171         this.xhr.open(this.method, this.url, true);
26172         
26173         var headers = {
26174             "Accept": "application/json",
26175             "Cache-Control": "no-cache",
26176             "X-Requested-With": "XMLHttpRequest"
26177         };
26178         
26179         for (var headerName in headers) {
26180             var headerValue = headers[headerName];
26181             if (headerValue) {
26182                 this.xhr.setRequestHeader(headerName, headerValue);
26183             }
26184         }
26185         
26186         var _this = this;
26187         
26188         this.xhr.onload = function()
26189         {
26190             _this.xhrOnLoad(_this.xhr);
26191         }
26192         
26193         this.xhr.onerror = function()
26194         {
26195             _this.xhrOnError(_this.xhr);
26196         }
26197         
26198         var formData = new FormData();
26199
26200         formData.append('returnHTML', 'NO');
26201         
26202         if(crop){
26203             formData.append('crop', crop);
26204         }
26205         
26206         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
26207             formData.append(this.paramName, file, file.name);
26208         }
26209         
26210         if(typeof(file.filename) != 'undefined'){
26211             formData.append('filename', file.filename);
26212         }
26213         
26214         if(typeof(file.mimetype) != 'undefined'){
26215             formData.append('mimetype', file.mimetype);
26216         }
26217         
26218         if(this.fireEvent('arrange', this, formData) != false){
26219             this.xhr.send(formData);
26220         };
26221     },
26222     
26223     xhrOnLoad : function(xhr)
26224     {
26225         if(this.loadMask){
26226             this.maskEl.unmask();
26227         }
26228         
26229         if (xhr.readyState !== 4) {
26230             this.fireEvent('exception', this, xhr);
26231             return;
26232         }
26233
26234         var response = Roo.decode(xhr.responseText);
26235         
26236         if(!response.success){
26237             this.fireEvent('exception', this, xhr);
26238             return;
26239         }
26240         
26241         var response = Roo.decode(xhr.responseText);
26242         
26243         this.fireEvent('upload', this, response);
26244         
26245     },
26246     
26247     xhrOnError : function()
26248     {
26249         if(this.loadMask){
26250             this.maskEl.unmask();
26251         }
26252         
26253         Roo.log('xhr on error');
26254         
26255         var response = Roo.decode(xhr.responseText);
26256           
26257         Roo.log(response);
26258         
26259     },
26260     
26261     prepare : function(file)
26262     {   
26263         if(this.loadMask){
26264             this.maskEl.mask(this.loadingText);
26265         }
26266         
26267         this.file = false;
26268         this.exif = {};
26269         
26270         if(typeof(file) === 'string'){
26271             this.loadCanvas(file);
26272             return;
26273         }
26274         
26275         if(!file || !this.urlAPI){
26276             return;
26277         }
26278         
26279         this.file = file;
26280         this.cropType = file.type;
26281         
26282         var _this = this;
26283         
26284         if(this.fireEvent('prepare', this, this.file) != false){
26285             
26286             var reader = new FileReader();
26287             
26288             reader.onload = function (e) {
26289                 if (e.target.error) {
26290                     Roo.log(e.target.error);
26291                     return;
26292                 }
26293                 
26294                 var buffer = e.target.result,
26295                     dataView = new DataView(buffer),
26296                     offset = 2,
26297                     maxOffset = dataView.byteLength - 4,
26298                     markerBytes,
26299                     markerLength;
26300                 
26301                 if (dataView.getUint16(0) === 0xffd8) {
26302                     while (offset < maxOffset) {
26303                         markerBytes = dataView.getUint16(offset);
26304                         
26305                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
26306                             markerLength = dataView.getUint16(offset + 2) + 2;
26307                             if (offset + markerLength > dataView.byteLength) {
26308                                 Roo.log('Invalid meta data: Invalid segment size.');
26309                                 break;
26310                             }
26311                             
26312                             if(markerBytes == 0xffe1){
26313                                 _this.parseExifData(
26314                                     dataView,
26315                                     offset,
26316                                     markerLength
26317                                 );
26318                             }
26319                             
26320                             offset += markerLength;
26321                             
26322                             continue;
26323                         }
26324                         
26325                         break;
26326                     }
26327                     
26328                 }
26329                 
26330                 var url = _this.urlAPI.createObjectURL(_this.file);
26331                 
26332                 _this.loadCanvas(url);
26333                 
26334                 return;
26335             }
26336             
26337             reader.readAsArrayBuffer(this.file);
26338             
26339         }
26340         
26341     },
26342     
26343     parseExifData : function(dataView, offset, length)
26344     {
26345         var tiffOffset = offset + 10,
26346             littleEndian,
26347             dirOffset;
26348     
26349         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26350             // No Exif data, might be XMP data instead
26351             return;
26352         }
26353         
26354         // Check for the ASCII code for "Exif" (0x45786966):
26355         if (dataView.getUint32(offset + 4) !== 0x45786966) {
26356             // No Exif data, might be XMP data instead
26357             return;
26358         }
26359         if (tiffOffset + 8 > dataView.byteLength) {
26360             Roo.log('Invalid Exif data: Invalid segment size.');
26361             return;
26362         }
26363         // Check for the two null bytes:
26364         if (dataView.getUint16(offset + 8) !== 0x0000) {
26365             Roo.log('Invalid Exif data: Missing byte alignment offset.');
26366             return;
26367         }
26368         // Check the byte alignment:
26369         switch (dataView.getUint16(tiffOffset)) {
26370         case 0x4949:
26371             littleEndian = true;
26372             break;
26373         case 0x4D4D:
26374             littleEndian = false;
26375             break;
26376         default:
26377             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
26378             return;
26379         }
26380         // Check for the TIFF tag marker (0x002A):
26381         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
26382             Roo.log('Invalid Exif data: Missing TIFF marker.');
26383             return;
26384         }
26385         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
26386         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
26387         
26388         this.parseExifTags(
26389             dataView,
26390             tiffOffset,
26391             tiffOffset + dirOffset,
26392             littleEndian
26393         );
26394     },
26395     
26396     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
26397     {
26398         var tagsNumber,
26399             dirEndOffset,
26400             i;
26401         if (dirOffset + 6 > dataView.byteLength) {
26402             Roo.log('Invalid Exif data: Invalid directory offset.');
26403             return;
26404         }
26405         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
26406         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
26407         if (dirEndOffset + 4 > dataView.byteLength) {
26408             Roo.log('Invalid Exif data: Invalid directory size.');
26409             return;
26410         }
26411         for (i = 0; i < tagsNumber; i += 1) {
26412             this.parseExifTag(
26413                 dataView,
26414                 tiffOffset,
26415                 dirOffset + 2 + 12 * i, // tag offset
26416                 littleEndian
26417             );
26418         }
26419         // Return the offset to the next directory:
26420         return dataView.getUint32(dirEndOffset, littleEndian);
26421     },
26422     
26423     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
26424     {
26425         var tag = dataView.getUint16(offset, littleEndian);
26426         
26427         this.exif[tag] = this.getExifValue(
26428             dataView,
26429             tiffOffset,
26430             offset,
26431             dataView.getUint16(offset + 2, littleEndian), // tag type
26432             dataView.getUint32(offset + 4, littleEndian), // tag length
26433             littleEndian
26434         );
26435     },
26436     
26437     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
26438     {
26439         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
26440             tagSize,
26441             dataOffset,
26442             values,
26443             i,
26444             str,
26445             c;
26446     
26447         if (!tagType) {
26448             Roo.log('Invalid Exif data: Invalid tag type.');
26449             return;
26450         }
26451         
26452         tagSize = tagType.size * length;
26453         // Determine if the value is contained in the dataOffset bytes,
26454         // or if the value at the dataOffset is a pointer to the actual data:
26455         dataOffset = tagSize > 4 ?
26456                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
26457         if (dataOffset + tagSize > dataView.byteLength) {
26458             Roo.log('Invalid Exif data: Invalid data offset.');
26459             return;
26460         }
26461         if (length === 1) {
26462             return tagType.getValue(dataView, dataOffset, littleEndian);
26463         }
26464         values = [];
26465         for (i = 0; i < length; i += 1) {
26466             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
26467         }
26468         
26469         if (tagType.ascii) {
26470             str = '';
26471             // Concatenate the chars:
26472             for (i = 0; i < values.length; i += 1) {
26473                 c = values[i];
26474                 // Ignore the terminating NULL byte(s):
26475                 if (c === '\u0000') {
26476                     break;
26477                 }
26478                 str += c;
26479             }
26480             return str;
26481         }
26482         return values;
26483     }
26484     
26485 });
26486
26487 Roo.apply(Roo.bootstrap.UploadCropbox, {
26488     tags : {
26489         'Orientation': 0x0112
26490     },
26491     
26492     Orientation: {
26493             1: 0, //'top-left',
26494 //            2: 'top-right',
26495             3: 180, //'bottom-right',
26496 //            4: 'bottom-left',
26497 //            5: 'left-top',
26498             6: 90, //'right-top',
26499 //            7: 'right-bottom',
26500             8: 270 //'left-bottom'
26501     },
26502     
26503     exifTagTypes : {
26504         // byte, 8-bit unsigned int:
26505         1: {
26506             getValue: function (dataView, dataOffset) {
26507                 return dataView.getUint8(dataOffset);
26508             },
26509             size: 1
26510         },
26511         // ascii, 8-bit byte:
26512         2: {
26513             getValue: function (dataView, dataOffset) {
26514                 return String.fromCharCode(dataView.getUint8(dataOffset));
26515             },
26516             size: 1,
26517             ascii: true
26518         },
26519         // short, 16 bit int:
26520         3: {
26521             getValue: function (dataView, dataOffset, littleEndian) {
26522                 return dataView.getUint16(dataOffset, littleEndian);
26523             },
26524             size: 2
26525         },
26526         // long, 32 bit int:
26527         4: {
26528             getValue: function (dataView, dataOffset, littleEndian) {
26529                 return dataView.getUint32(dataOffset, littleEndian);
26530             },
26531             size: 4
26532         },
26533         // rational = two long values, first is numerator, second is denominator:
26534         5: {
26535             getValue: function (dataView, dataOffset, littleEndian) {
26536                 return dataView.getUint32(dataOffset, littleEndian) /
26537                     dataView.getUint32(dataOffset + 4, littleEndian);
26538             },
26539             size: 8
26540         },
26541         // slong, 32 bit signed int:
26542         9: {
26543             getValue: function (dataView, dataOffset, littleEndian) {
26544                 return dataView.getInt32(dataOffset, littleEndian);
26545             },
26546             size: 4
26547         },
26548         // srational, two slongs, first is numerator, second is denominator:
26549         10: {
26550             getValue: function (dataView, dataOffset, littleEndian) {
26551                 return dataView.getInt32(dataOffset, littleEndian) /
26552                     dataView.getInt32(dataOffset + 4, littleEndian);
26553             },
26554             size: 8
26555         }
26556     },
26557     
26558     footer : {
26559         STANDARD : [
26560             {
26561                 tag : 'div',
26562                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26563                 action : 'rotate-left',
26564                 cn : [
26565                     {
26566                         tag : 'button',
26567                         cls : 'btn btn-default',
26568                         html : '<i class="fa fa-undo"></i>'
26569                     }
26570                 ]
26571             },
26572             {
26573                 tag : 'div',
26574                 cls : 'btn-group roo-upload-cropbox-picture',
26575                 action : 'picture',
26576                 cn : [
26577                     {
26578                         tag : 'button',
26579                         cls : 'btn btn-default',
26580                         html : '<i class="fa fa-picture-o"></i>'
26581                     }
26582                 ]
26583             },
26584             {
26585                 tag : 'div',
26586                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26587                 action : 'rotate-right',
26588                 cn : [
26589                     {
26590                         tag : 'button',
26591                         cls : 'btn btn-default',
26592                         html : '<i class="fa fa-repeat"></i>'
26593                     }
26594                 ]
26595             }
26596         ],
26597         DOCUMENT : [
26598             {
26599                 tag : 'div',
26600                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26601                 action : 'rotate-left',
26602                 cn : [
26603                     {
26604                         tag : 'button',
26605                         cls : 'btn btn-default',
26606                         html : '<i class="fa fa-undo"></i>'
26607                     }
26608                 ]
26609             },
26610             {
26611                 tag : 'div',
26612                 cls : 'btn-group roo-upload-cropbox-download',
26613                 action : 'download',
26614                 cn : [
26615                     {
26616                         tag : 'button',
26617                         cls : 'btn btn-default',
26618                         html : '<i class="fa fa-download"></i>'
26619                     }
26620                 ]
26621             },
26622             {
26623                 tag : 'div',
26624                 cls : 'btn-group roo-upload-cropbox-crop',
26625                 action : 'crop',
26626                 cn : [
26627                     {
26628                         tag : 'button',
26629                         cls : 'btn btn-default',
26630                         html : '<i class="fa fa-crop"></i>'
26631                     }
26632                 ]
26633             },
26634             {
26635                 tag : 'div',
26636                 cls : 'btn-group roo-upload-cropbox-trash',
26637                 action : 'trash',
26638                 cn : [
26639                     {
26640                         tag : 'button',
26641                         cls : 'btn btn-default',
26642                         html : '<i class="fa fa-trash"></i>'
26643                     }
26644                 ]
26645             },
26646             {
26647                 tag : 'div',
26648                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26649                 action : 'rotate-right',
26650                 cn : [
26651                     {
26652                         tag : 'button',
26653                         cls : 'btn btn-default',
26654                         html : '<i class="fa fa-repeat"></i>'
26655                     }
26656                 ]
26657             }
26658         ],
26659         ROTATOR : [
26660             {
26661                 tag : 'div',
26662                 cls : 'btn-group roo-upload-cropbox-rotate-left',
26663                 action : 'rotate-left',
26664                 cn : [
26665                     {
26666                         tag : 'button',
26667                         cls : 'btn btn-default',
26668                         html : '<i class="fa fa-undo"></i>'
26669                     }
26670                 ]
26671             },
26672             {
26673                 tag : 'div',
26674                 cls : 'btn-group roo-upload-cropbox-rotate-right',
26675                 action : 'rotate-right',
26676                 cn : [
26677                     {
26678                         tag : 'button',
26679                         cls : 'btn btn-default',
26680                         html : '<i class="fa fa-repeat"></i>'
26681                     }
26682                 ]
26683             }
26684         ]
26685     }
26686 });
26687
26688 /*
26689 * Licence: LGPL
26690 */
26691
26692 /**
26693  * @class Roo.bootstrap.DocumentManager
26694  * @extends Roo.bootstrap.Component
26695  * Bootstrap DocumentManager class
26696  * @cfg {String} paramName default 'imageUpload'
26697  * @cfg {String} method default POST
26698  * @cfg {String} url action url
26699  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
26700  * @cfg {Boolean} multiple multiple upload default true
26701  * @cfg {Number} thumbSize default 300
26702  * @cfg {String} fieldLabel
26703  * @cfg {Number} labelWidth default 4
26704  * @cfg {String} labelAlign (left|top) default left
26705  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
26706  * 
26707  * @constructor
26708  * Create a new DocumentManager
26709  * @param {Object} config The config object
26710  */
26711
26712 Roo.bootstrap.DocumentManager = function(config){
26713     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
26714     
26715     this.addEvents({
26716         /**
26717          * @event initial
26718          * Fire when initial the DocumentManager
26719          * @param {Roo.bootstrap.DocumentManager} this
26720          */
26721         "initial" : true,
26722         /**
26723          * @event inspect
26724          * inspect selected file
26725          * @param {Roo.bootstrap.DocumentManager} this
26726          * @param {File} file
26727          */
26728         "inspect" : true,
26729         /**
26730          * @event exception
26731          * Fire when xhr load exception
26732          * @param {Roo.bootstrap.DocumentManager} this
26733          * @param {XMLHttpRequest} xhr
26734          */
26735         "exception" : true,
26736         /**
26737          * @event prepare
26738          * prepare the form data
26739          * @param {Roo.bootstrap.DocumentManager} this
26740          * @param {Object} formData
26741          */
26742         "prepare" : true,
26743         /**
26744          * @event remove
26745          * Fire when remove the file
26746          * @param {Roo.bootstrap.DocumentManager} this
26747          * @param {Object} file
26748          */
26749         "remove" : true,
26750         /**
26751          * @event refresh
26752          * Fire after refresh the file
26753          * @param {Roo.bootstrap.DocumentManager} this
26754          */
26755         "refresh" : true,
26756         /**
26757          * @event click
26758          * Fire after click the image
26759          * @param {Roo.bootstrap.DocumentManager} this
26760          * @param {Object} file
26761          */
26762         "click" : true,
26763         /**
26764          * @event edit
26765          * Fire when upload a image and editable set to true
26766          * @param {Roo.bootstrap.DocumentManager} this
26767          * @param {Object} file
26768          */
26769         "edit" : true,
26770         /**
26771          * @event beforeselectfile
26772          * Fire before select file
26773          * @param {Roo.bootstrap.DocumentManager} this
26774          */
26775         "beforeselectfile" : true,
26776         /**
26777          * @event process
26778          * Fire before process file
26779          * @param {Roo.bootstrap.DocumentManager} this
26780          * @param {Object} file
26781          */
26782         "process" : true
26783         
26784     });
26785 };
26786
26787 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
26788     
26789     boxes : 0,
26790     inputName : '',
26791     thumbSize : 300,
26792     multiple : true,
26793     files : [],
26794     method : 'POST',
26795     url : '',
26796     paramName : 'imageUpload',
26797     fieldLabel : '',
26798     labelWidth : 4,
26799     labelAlign : 'left',
26800     editable : true,
26801     delegates : [],
26802     
26803     
26804     xhr : false, 
26805     
26806     getAutoCreate : function()
26807     {   
26808         var managerWidget = {
26809             tag : 'div',
26810             cls : 'roo-document-manager',
26811             cn : [
26812                 {
26813                     tag : 'input',
26814                     cls : 'roo-document-manager-selector',
26815                     type : 'file'
26816                 },
26817                 {
26818                     tag : 'div',
26819                     cls : 'roo-document-manager-uploader',
26820                     cn : [
26821                         {
26822                             tag : 'div',
26823                             cls : 'roo-document-manager-upload-btn',
26824                             html : '<i class="fa fa-plus"></i>'
26825                         }
26826                     ]
26827                     
26828                 }
26829             ]
26830         };
26831         
26832         var content = [
26833             {
26834                 tag : 'div',
26835                 cls : 'column col-md-12',
26836                 cn : managerWidget
26837             }
26838         ];
26839         
26840         if(this.fieldLabel.length){
26841             
26842             content = [
26843                 {
26844                     tag : 'div',
26845                     cls : 'column col-md-12',
26846                     html : this.fieldLabel
26847                 },
26848                 {
26849                     tag : 'div',
26850                     cls : 'column col-md-12',
26851                     cn : managerWidget
26852                 }
26853             ];
26854
26855             if(this.labelAlign == 'left'){
26856                 content = [
26857                     {
26858                         tag : 'div',
26859                         cls : 'column col-md-' + this.labelWidth,
26860                         html : this.fieldLabel
26861                     },
26862                     {
26863                         tag : 'div',
26864                         cls : 'column col-md-' + (12 - this.labelWidth),
26865                         cn : managerWidget
26866                     }
26867                 ];
26868                 
26869             }
26870         }
26871         
26872         var cfg = {
26873             tag : 'div',
26874             cls : 'row clearfix',
26875             cn : content
26876         };
26877         
26878         return cfg;
26879         
26880     },
26881     
26882     initEvents : function()
26883     {
26884         this.managerEl = this.el.select('.roo-document-manager', true).first();
26885         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26886         
26887         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
26888         this.selectorEl.hide();
26889         
26890         if(this.multiple){
26891             this.selectorEl.attr('multiple', 'multiple');
26892         }
26893         
26894         this.selectorEl.on('change', this.onFileSelected, this);
26895         
26896         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
26897         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26898         
26899         this.uploader.on('click', this.onUploaderClick, this);
26900         
26901         this.renderProgressDialog();
26902         
26903         var _this = this;
26904         
26905         window.addEventListener("resize", function() { _this.refresh(); } );
26906         
26907         this.fireEvent('initial', this);
26908     },
26909     
26910     renderProgressDialog : function()
26911     {
26912         var _this = this;
26913         
26914         this.progressDialog = new Roo.bootstrap.Modal({
26915             cls : 'roo-document-manager-progress-dialog',
26916             allow_close : false,
26917             title : '',
26918             buttons : [
26919                 {
26920                     name  :'cancel',
26921                     weight : 'danger',
26922                     html : 'Cancel'
26923                 }
26924             ], 
26925             listeners : { 
26926                 btnclick : function() {
26927                     _this.uploadCancel();
26928                     this.hide();
26929                 }
26930             }
26931         });
26932          
26933         this.progressDialog.render(Roo.get(document.body));
26934          
26935         this.progress = new Roo.bootstrap.Progress({
26936             cls : 'roo-document-manager-progress',
26937             active : true,
26938             striped : true
26939         });
26940         
26941         this.progress.render(this.progressDialog.getChildContainer());
26942         
26943         this.progressBar = new Roo.bootstrap.ProgressBar({
26944             cls : 'roo-document-manager-progress-bar',
26945             aria_valuenow : 0,
26946             aria_valuemin : 0,
26947             aria_valuemax : 12,
26948             panel : 'success'
26949         });
26950         
26951         this.progressBar.render(this.progress.getChildContainer());
26952     },
26953     
26954     onUploaderClick : function(e)
26955     {
26956         e.preventDefault();
26957      
26958         if(this.fireEvent('beforeselectfile', this) != false){
26959             this.selectorEl.dom.click();
26960         }
26961         
26962     },
26963     
26964     onFileSelected : function(e)
26965     {
26966         e.preventDefault();
26967         
26968         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26969             return;
26970         }
26971         
26972         Roo.each(this.selectorEl.dom.files, function(file){
26973             if(this.fireEvent('inspect', this, file) != false){
26974                 this.files.push(file);
26975             }
26976         }, this);
26977         
26978         this.queue();
26979         
26980     },
26981     
26982     queue : function()
26983     {
26984         this.selectorEl.dom.value = '';
26985         
26986         if(!this.files.length){
26987             return;
26988         }
26989         
26990         if(this.boxes > 0 && this.files.length > this.boxes){
26991             this.files = this.files.slice(0, this.boxes);
26992         }
26993         
26994         this.uploader.show();
26995         
26996         if(this.boxes > 0 && this.files.length > this.boxes - 1){
26997             this.uploader.hide();
26998         }
26999         
27000         var _this = this;
27001         
27002         var files = [];
27003         
27004         var docs = [];
27005         
27006         Roo.each(this.files, function(file){
27007             
27008             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27009                 var f = this.renderPreview(file);
27010                 files.push(f);
27011                 return;
27012             }
27013             
27014             if(file.type.indexOf('image') != -1){
27015                 this.delegates.push(
27016                     (function(){
27017                         _this.process(file);
27018                     }).createDelegate(this)
27019                 );
27020         
27021                 return;
27022             }
27023             
27024             docs.push(
27025                 (function(){
27026                     _this.process(file);
27027                 }).createDelegate(this)
27028             );
27029             
27030         }, this);
27031         
27032         this.files = files;
27033         
27034         this.delegates = this.delegates.concat(docs);
27035         
27036         if(!this.delegates.length){
27037             this.refresh();
27038             return;
27039         }
27040         
27041         this.progressBar.aria_valuemax = this.delegates.length;
27042         
27043         this.arrange();
27044         
27045         return;
27046     },
27047     
27048     arrange : function()
27049     {
27050         if(!this.delegates.length){
27051             this.progressDialog.hide();
27052             this.refresh();
27053             return;
27054         }
27055         
27056         var delegate = this.delegates.shift();
27057         
27058         this.progressDialog.show();
27059         
27060         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
27061         
27062         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
27063         
27064         delegate();
27065     },
27066     
27067     refresh : function()
27068     {
27069         this.uploader.show();
27070         
27071         if(this.boxes > 0 && this.files.length > this.boxes - 1){
27072             this.uploader.hide();
27073         }
27074         
27075         Roo.isTouch ? this.closable(false) : this.closable(true);
27076         
27077         this.fireEvent('refresh', this);
27078     },
27079     
27080     onRemove : function(e, el, o)
27081     {
27082         e.preventDefault();
27083         
27084         this.fireEvent('remove', this, o);
27085         
27086     },
27087     
27088     remove : function(o)
27089     {
27090         var files = [];
27091         
27092         Roo.each(this.files, function(file){
27093             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
27094                 files.push(file);
27095                 return;
27096             }
27097
27098             o.target.remove();
27099
27100         }, this);
27101         
27102         this.files = files;
27103         
27104         this.refresh();
27105     },
27106     
27107     clear : function()
27108     {
27109         Roo.each(this.files, function(file){
27110             if(!file.target){
27111                 return;
27112             }
27113             
27114             file.target.remove();
27115
27116         }, this);
27117         
27118         this.files = [];
27119         
27120         this.refresh();
27121     },
27122     
27123     onClick : function(e, el, o)
27124     {
27125         e.preventDefault();
27126         
27127         this.fireEvent('click', this, o);
27128         
27129     },
27130     
27131     closable : function(closable)
27132     {
27133         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
27134             
27135             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27136             
27137             if(closable){
27138                 el.show();
27139                 return;
27140             }
27141             
27142             el.hide();
27143             
27144         }, this);
27145     },
27146     
27147     xhrOnLoad : function(xhr)
27148     {
27149         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27150             el.remove();
27151         }, this);
27152         
27153         if (xhr.readyState !== 4) {
27154             this.arrange();
27155             this.fireEvent('exception', this, xhr);
27156             return;
27157         }
27158
27159         var response = Roo.decode(xhr.responseText);
27160         
27161         if(!response.success){
27162             this.arrange();
27163             this.fireEvent('exception', this, xhr);
27164             return;
27165         }
27166         
27167         var file = this.renderPreview(response.data);
27168         
27169         this.files.push(file);
27170         
27171         this.arrange();
27172         
27173     },
27174     
27175     xhrOnError : function(xhr)
27176     {
27177         Roo.log('xhr on error');
27178         
27179         var response = Roo.decode(xhr.responseText);
27180           
27181         Roo.log(response);
27182         
27183         this.arrange();
27184     },
27185     
27186     process : function(file)
27187     {
27188         if(this.fireEvent('process', this, file) !== false){
27189             if(this.editable && file.type.indexOf('image') != -1){
27190                 this.fireEvent('edit', this, file);
27191                 return;
27192             }
27193
27194             this.uploadStart(file, false);
27195
27196             return;
27197         }
27198         
27199     },
27200     
27201     uploadStart : function(file, crop)
27202     {
27203         this.xhr = new XMLHttpRequest();
27204         
27205         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
27206             this.arrange();
27207             return;
27208         }
27209         
27210         file.xhr = this.xhr;
27211             
27212         this.managerEl.createChild({
27213             tag : 'div',
27214             cls : 'roo-document-manager-loading',
27215             cn : [
27216                 {
27217                     tag : 'div',
27218                     tooltip : file.name,
27219                     cls : 'roo-document-manager-thumb',
27220                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27221                 }
27222             ]
27223
27224         });
27225
27226         this.xhr.open(this.method, this.url, true);
27227         
27228         var headers = {
27229             "Accept": "application/json",
27230             "Cache-Control": "no-cache",
27231             "X-Requested-With": "XMLHttpRequest"
27232         };
27233         
27234         for (var headerName in headers) {
27235             var headerValue = headers[headerName];
27236             if (headerValue) {
27237                 this.xhr.setRequestHeader(headerName, headerValue);
27238             }
27239         }
27240         
27241         var _this = this;
27242         
27243         this.xhr.onload = function()
27244         {
27245             _this.xhrOnLoad(_this.xhr);
27246         }
27247         
27248         this.xhr.onerror = function()
27249         {
27250             _this.xhrOnError(_this.xhr);
27251         }
27252         
27253         var formData = new FormData();
27254
27255         formData.append('returnHTML', 'NO');
27256         
27257         if(crop){
27258             formData.append('crop', crop);
27259         }
27260         
27261         formData.append(this.paramName, file, file.name);
27262         
27263         if(this.fireEvent('prepare', this, formData) != false){
27264             this.xhr.send(formData);
27265         };
27266     },
27267     
27268     uploadCancel : function()
27269     {
27270         if (this.xhr) {
27271             this.xhr.abort();
27272         }
27273         
27274         
27275         this.delegates = [];
27276         
27277         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
27278             el.remove();
27279         }, this);
27280         
27281         this.arrange();
27282     },
27283     
27284     renderPreview : function(file)
27285     {
27286         if(typeof(file.target) != 'undefined' && file.target){
27287             return file;
27288         }
27289         
27290         var previewEl = this.managerEl.createChild({
27291             tag : 'div',
27292             cls : 'roo-document-manager-preview',
27293             cn : [
27294                 {
27295                     tag : 'div',
27296                     tooltip : file.filename,
27297                     cls : 'roo-document-manager-thumb',
27298                     html : '<img src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
27299                 },
27300                 {
27301                     tag : 'button',
27302                     cls : 'close',
27303                     html : '<i class="fa fa-times-circle"></i>'
27304                 }
27305             ]
27306         });
27307
27308         var close = previewEl.select('button.close', true).first();
27309
27310         close.on('click', this.onRemove, this, file);
27311
27312         file.target = previewEl;
27313
27314         var image = previewEl.select('img', true).first();
27315         
27316         var _this = this;
27317         
27318         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
27319         
27320         image.on('click', this.onClick, this, file);
27321         
27322         return file;
27323         
27324     },
27325     
27326     onPreviewLoad : function(file, image)
27327     {
27328         if(typeof(file.target) == 'undefined' || !file.target){
27329             return;
27330         }
27331         
27332         var width = image.dom.naturalWidth || image.dom.width;
27333         var height = image.dom.naturalHeight || image.dom.height;
27334         
27335         if(width > height){
27336             file.target.addClass('wide');
27337             return;
27338         }
27339         
27340         file.target.addClass('tall');
27341         return;
27342         
27343     },
27344     
27345     uploadFromSource : function(file, crop)
27346     {
27347         this.xhr = new XMLHttpRequest();
27348         
27349         this.managerEl.createChild({
27350             tag : 'div',
27351             cls : 'roo-document-manager-loading',
27352             cn : [
27353                 {
27354                     tag : 'div',
27355                     tooltip : file.name,
27356                     cls : 'roo-document-manager-thumb',
27357                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
27358                 }
27359             ]
27360
27361         });
27362
27363         this.xhr.open(this.method, this.url, true);
27364         
27365         var headers = {
27366             "Accept": "application/json",
27367             "Cache-Control": "no-cache",
27368             "X-Requested-With": "XMLHttpRequest"
27369         };
27370         
27371         for (var headerName in headers) {
27372             var headerValue = headers[headerName];
27373             if (headerValue) {
27374                 this.xhr.setRequestHeader(headerName, headerValue);
27375             }
27376         }
27377         
27378         var _this = this;
27379         
27380         this.xhr.onload = function()
27381         {
27382             _this.xhrOnLoad(_this.xhr);
27383         }
27384         
27385         this.xhr.onerror = function()
27386         {
27387             _this.xhrOnError(_this.xhr);
27388         }
27389         
27390         var formData = new FormData();
27391
27392         formData.append('returnHTML', 'NO');
27393         
27394         formData.append('crop', crop);
27395         
27396         if(typeof(file.filename) != 'undefined'){
27397             formData.append('filename', file.filename);
27398         }
27399         
27400         if(typeof(file.mimetype) != 'undefined'){
27401             formData.append('mimetype', file.mimetype);
27402         }
27403         
27404         if(this.fireEvent('prepare', this, formData) != false){
27405             this.xhr.send(formData);
27406         };
27407     }
27408 });
27409
27410 /*
27411 * Licence: LGPL
27412 */
27413
27414 /**
27415  * @class Roo.bootstrap.DocumentViewer
27416  * @extends Roo.bootstrap.Component
27417  * Bootstrap DocumentViewer class
27418  * 
27419  * @constructor
27420  * Create a new DocumentViewer
27421  * @param {Object} config The config object
27422  */
27423
27424 Roo.bootstrap.DocumentViewer = function(config){
27425     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
27426     
27427     this.addEvents({
27428         /**
27429          * @event initial
27430          * Fire after initEvent
27431          * @param {Roo.bootstrap.DocumentViewer} this
27432          */
27433         "initial" : true,
27434         /**
27435          * @event click
27436          * Fire after click
27437          * @param {Roo.bootstrap.DocumentViewer} this
27438          */
27439         "click" : true,
27440         /**
27441          * @event trash
27442          * Fire after trash button
27443          * @param {Roo.bootstrap.DocumentViewer} this
27444          */
27445         "trash" : true
27446         
27447     });
27448 };
27449
27450 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
27451     
27452     getAutoCreate : function()
27453     {
27454         var cfg = {
27455             tag : 'div',
27456             cls : 'roo-document-viewer',
27457             cn : [
27458                 {
27459                     tag : 'div',
27460                     cls : 'roo-document-viewer-body',
27461                     cn : [
27462                         {
27463                             tag : 'div',
27464                             cls : 'roo-document-viewer-thumb',
27465                             cn : [
27466                                 {
27467                                     tag : 'img',
27468                                     cls : 'roo-document-viewer-image'
27469                                 }
27470                             ]
27471                         }
27472                     ]
27473                 },
27474                 {
27475                     tag : 'div',
27476                     cls : 'roo-document-viewer-footer',
27477                     cn : {
27478                         tag : 'div',
27479                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
27480                         cn : [
27481                             {
27482                                 tag : 'div',
27483                                 cls : 'btn-group',
27484                                 cn : [
27485                                     {
27486                                         tag : 'button',
27487                                         cls : 'btn btn-default roo-document-viewer-trash',
27488                                         html : '<i class="fa fa-trash"></i>'
27489                                     }
27490                                 ]
27491                             }
27492                         ]
27493                     }
27494                 }
27495             ]
27496         };
27497         
27498         return cfg;
27499     },
27500     
27501     initEvents : function()
27502     {
27503         
27504         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
27505         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27506         
27507         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
27508         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27509         
27510         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
27511         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27512         
27513         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
27514         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27515         
27516         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
27517         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
27518         
27519         this.bodyEl.on('click', this.onClick, this);
27520         
27521         this.trashBtn.on('click', this.onTrash, this);
27522         
27523     },
27524     
27525     initial : function()
27526     {
27527 //        this.thumbEl.setStyle('line-height', this.thumbEl.getHeight(true) + 'px');
27528         
27529         
27530         this.fireEvent('initial', this);
27531         
27532     },
27533     
27534     onClick : function(e)
27535     {
27536         e.preventDefault();
27537         
27538         this.fireEvent('click', this);
27539     },
27540     
27541     onTrash : function(e)
27542     {
27543         e.preventDefault();
27544         
27545         this.fireEvent('trash', this);
27546     }
27547     
27548 });
27549 /*
27550  * - LGPL
27551  *
27552  * nav progress bar
27553  * 
27554  */
27555
27556 /**
27557  * @class Roo.bootstrap.NavProgressBar
27558  * @extends Roo.bootstrap.Component
27559  * Bootstrap NavProgressBar class
27560  * 
27561  * @constructor
27562  * Create a new nav progress bar
27563  * @param {Object} config The config object
27564  */
27565
27566 Roo.bootstrap.NavProgressBar = function(config){
27567     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
27568
27569     this.bullets = this.bullets || [];
27570    
27571 //    Roo.bootstrap.NavProgressBar.register(this);
27572      this.addEvents({
27573         /**
27574              * @event changed
27575              * Fires when the active item changes
27576              * @param {Roo.bootstrap.NavProgressBar} this
27577              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
27578              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
27579          */
27580         'changed': true
27581      });
27582     
27583 };
27584
27585 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
27586     
27587     bullets : [],
27588     barItems : [],
27589     
27590     getAutoCreate : function()
27591     {
27592         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
27593         
27594         cfg = {
27595             tag : 'div',
27596             cls : 'roo-navigation-bar-group',
27597             cn : [
27598                 {
27599                     tag : 'div',
27600                     cls : 'roo-navigation-top-bar'
27601                 },
27602                 {
27603                     tag : 'div',
27604                     cls : 'roo-navigation-bullets-bar',
27605                     cn : [
27606                         {
27607                             tag : 'ul',
27608                             cls : 'roo-navigation-bar'
27609                         }
27610                     ]
27611                 },
27612                 
27613                 {
27614                     tag : 'div',
27615                     cls : 'roo-navigation-bottom-bar'
27616                 }
27617             ]
27618             
27619         };
27620         
27621         return cfg;
27622         
27623     },
27624     
27625     initEvents: function() 
27626     {
27627         
27628     },
27629     
27630     onRender : function(ct, position) 
27631     {
27632         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
27633         
27634         if(this.bullets.length){
27635             Roo.each(this.bullets, function(b){
27636                this.addItem(b);
27637             }, this);
27638         }
27639         
27640         this.format();
27641         
27642     },
27643     
27644     addItem : function(cfg)
27645     {
27646         var item = new Roo.bootstrap.NavProgressItem(cfg);
27647         
27648         item.parentId = this.id;
27649         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
27650         
27651         if(cfg.html){
27652             var top = new Roo.bootstrap.Element({
27653                 tag : 'div',
27654                 cls : 'roo-navigation-bar-text'
27655             });
27656             
27657             var bottom = new Roo.bootstrap.Element({
27658                 tag : 'div',
27659                 cls : 'roo-navigation-bar-text'
27660             });
27661             
27662             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
27663             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
27664             
27665             var topText = new Roo.bootstrap.Element({
27666                 tag : 'span',
27667                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
27668             });
27669             
27670             var bottomText = new Roo.bootstrap.Element({
27671                 tag : 'span',
27672                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
27673             });
27674             
27675             topText.onRender(top.el, null);
27676             bottomText.onRender(bottom.el, null);
27677             
27678             item.topEl = top;
27679             item.bottomEl = bottom;
27680         }
27681         
27682         this.barItems.push(item);
27683         
27684         return item;
27685     },
27686     
27687     getActive : function()
27688     {
27689         var active = false;
27690         
27691         Roo.each(this.barItems, function(v){
27692             
27693             if (!v.isActive()) {
27694                 return;
27695             }
27696             
27697             active = v;
27698             return false;
27699             
27700         });
27701         
27702         return active;
27703     },
27704     
27705     setActiveItem : function(item)
27706     {
27707         var prev = false;
27708         
27709         Roo.each(this.barItems, function(v){
27710             if (v.rid == item.rid) {
27711                 return ;
27712             }
27713             
27714             if (v.isActive()) {
27715                 v.setActive(false);
27716                 prev = v;
27717             }
27718         });
27719
27720         item.setActive(true);
27721         
27722         this.fireEvent('changed', this, item, prev);
27723     },
27724     
27725     getBarItem: function(rid)
27726     {
27727         var ret = false;
27728         
27729         Roo.each(this.barItems, function(e) {
27730             if (e.rid != rid) {
27731                 return;
27732             }
27733             
27734             ret =  e;
27735             return false;
27736         });
27737         
27738         return ret;
27739     },
27740     
27741     indexOfItem : function(item)
27742     {
27743         var index = false;
27744         
27745         Roo.each(this.barItems, function(v, i){
27746             
27747             if (v.rid != item.rid) {
27748                 return;
27749             }
27750             
27751             index = i;
27752             return false
27753         });
27754         
27755         return index;
27756     },
27757     
27758     setActiveNext : function()
27759     {
27760         var i = this.indexOfItem(this.getActive());
27761         
27762         if (i > this.barItems.length) {
27763             return;
27764         }
27765         
27766         this.setActiveItem(this.barItems[i+1]);
27767     },
27768     
27769     setActivePrev : function()
27770     {
27771         var i = this.indexOfItem(this.getActive());
27772         
27773         if (i  < 1) {
27774             return;
27775         }
27776         
27777         this.setActiveItem(this.barItems[i-1]);
27778     },
27779     
27780     format : function()
27781     {
27782         if(!this.barItems.length){
27783             return;
27784         }
27785      
27786         var width = 100 / this.barItems.length;
27787         
27788         Roo.each(this.barItems, function(i){
27789             i.el.setStyle('width', width + '%');
27790             i.topEl.el.setStyle('width', width + '%');
27791             i.bottomEl.el.setStyle('width', width + '%');
27792         }, this);
27793         
27794     }
27795     
27796 });
27797 /*
27798  * - LGPL
27799  *
27800  * Nav Progress Item
27801  * 
27802  */
27803
27804 /**
27805  * @class Roo.bootstrap.NavProgressItem
27806  * @extends Roo.bootstrap.Component
27807  * Bootstrap NavProgressItem class
27808  * @cfg {String} rid the reference id
27809  * @cfg {Boolean} active (true|false) Is item active default false
27810  * @cfg {Boolean} disabled (true|false) Is item active default false
27811  * @cfg {String} html
27812  * @cfg {String} position (top|bottom) text position default bottom
27813  * @cfg {String} icon show icon instead of number
27814  * 
27815  * @constructor
27816  * Create a new NavProgressItem
27817  * @param {Object} config The config object
27818  */
27819 Roo.bootstrap.NavProgressItem = function(config){
27820     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
27821     this.addEvents({
27822         // raw events
27823         /**
27824          * @event click
27825          * The raw click event for the entire grid.
27826          * @param {Roo.bootstrap.NavProgressItem} this
27827          * @param {Roo.EventObject} e
27828          */
27829         "click" : true
27830     });
27831    
27832 };
27833
27834 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
27835     
27836     rid : '',
27837     active : false,
27838     disabled : false,
27839     html : '',
27840     position : 'bottom',
27841     icon : false,
27842     
27843     getAutoCreate : function()
27844     {
27845         var iconCls = 'roo-navigation-bar-item-icon';
27846         
27847         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
27848         
27849         var cfg = {
27850             tag: 'li',
27851             cls: 'roo-navigation-bar-item',
27852             cn : [
27853                 {
27854                     tag : 'i',
27855                     cls : iconCls
27856                 }
27857             ]
27858         };
27859         
27860         if(this.active){
27861             cfg.cls += ' active';
27862         }
27863         if(this.disabled){
27864             cfg.cls += ' disabled';
27865         }
27866         
27867         return cfg;
27868     },
27869     
27870     disable : function()
27871     {
27872         this.setDisabled(true);
27873     },
27874     
27875     enable : function()
27876     {
27877         this.setDisabled(false);
27878     },
27879     
27880     initEvents: function() 
27881     {
27882         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
27883         
27884         this.iconEl.on('click', this.onClick, this);
27885     },
27886     
27887     onClick : function(e)
27888     {
27889         e.preventDefault();
27890         
27891         if(this.disabled){
27892             return;
27893         }
27894         
27895         if(this.fireEvent('click', this, e) === false){
27896             return;
27897         };
27898         
27899         this.parent().setActiveItem(this);
27900     },
27901     
27902     isActive: function () 
27903     {
27904         return this.active;
27905     },
27906     
27907     setActive : function(state)
27908     {
27909         if(this.active == state){
27910             return;
27911         }
27912         
27913         this.active = state;
27914         
27915         if (state) {
27916             this.el.addClass('active');
27917             return;
27918         }
27919         
27920         this.el.removeClass('active');
27921         
27922         return;
27923     },
27924     
27925     setDisabled : function(state)
27926     {
27927         if(this.disabled == state){
27928             return;
27929         }
27930         
27931         this.disabled = state;
27932         
27933         if (state) {
27934             this.el.addClass('disabled');
27935             return;
27936         }
27937         
27938         this.el.removeClass('disabled');
27939     },
27940     
27941     tooltipEl : function()
27942     {
27943         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
27944     }
27945 });
27946  
27947
27948  /*
27949  * - LGPL
27950  *
27951  * FieldLabel
27952  * 
27953  */
27954
27955 /**
27956  * @class Roo.bootstrap.FieldLabel
27957  * @extends Roo.bootstrap.Component
27958  * Bootstrap FieldLabel class
27959  * @cfg {String} html contents of the element
27960  * @cfg {String} tag tag of the element default label
27961  * @cfg {String} cls class of the element
27962  * @cfg {String} target label target 
27963  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
27964  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
27965  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
27966  * @cfg {String} iconTooltip default "This field is required"
27967  * 
27968  * @constructor
27969  * Create a new FieldLabel
27970  * @param {Object} config The config object
27971  */
27972
27973 Roo.bootstrap.FieldLabel = function(config){
27974     Roo.bootstrap.Element.superclass.constructor.call(this, config);
27975     
27976     this.addEvents({
27977             /**
27978              * @event invalid
27979              * Fires after the field has been marked as invalid.
27980              * @param {Roo.form.FieldLabel} this
27981              * @param {String} msg The validation message
27982              */
27983             invalid : true,
27984             /**
27985              * @event valid
27986              * Fires after the field has been validated with no errors.
27987              * @param {Roo.form.FieldLabel} this
27988              */
27989             valid : true
27990         });
27991 };
27992
27993 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
27994     
27995     tag: 'label',
27996     cls: '',
27997     html: '',
27998     target: '',
27999     allowBlank : true,
28000     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
28001     validClass : 'text-success fa fa-lg fa-check',
28002     iconTooltip : 'This field is required',
28003     
28004     getAutoCreate : function(){
28005         
28006         var cfg = {
28007             tag : this.tag,
28008             cls : 'roo-bootstrap-field-label ' + this.cls,
28009             for : this.target,
28010             cn : [
28011                 {
28012                     tag : 'i',
28013                     cls : '',
28014                     tooltip : this.iconTooltip
28015                 },
28016                 {
28017                     tag : 'span',
28018                     html : this.html
28019                 }
28020             ] 
28021         };
28022         
28023         return cfg;
28024     },
28025     
28026     initEvents: function() 
28027     {
28028         Roo.bootstrap.Element.superclass.initEvents.call(this);
28029         
28030         this.iconEl = this.el.select('i', true).first();
28031         
28032         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
28033         
28034         Roo.bootstrap.FieldLabel.register(this);
28035     },
28036     
28037     /**
28038      * Mark this field as valid
28039      */
28040     markValid : function()
28041     {
28042         this.iconEl.show();
28043         
28044         this.iconEl.removeClass(this.invalidClass);
28045         
28046         this.iconEl.addClass(this.validClass);
28047         
28048         this.fireEvent('valid', this);
28049     },
28050     
28051     /**
28052      * Mark this field as invalid
28053      * @param {String} msg The validation message
28054      */
28055     markInvalid : function(msg)
28056     {
28057         this.iconEl.show();
28058         
28059         this.iconEl.removeClass(this.validClass);
28060         
28061         this.iconEl.addClass(this.invalidClass);
28062         
28063         this.fireEvent('invalid', this, msg);
28064     }
28065     
28066    
28067 });
28068
28069 Roo.apply(Roo.bootstrap.FieldLabel, {
28070     
28071     groups: {},
28072     
28073      /**
28074     * register a FieldLabel Group
28075     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
28076     */
28077     register : function(label)
28078     {
28079         if(this.groups.hasOwnProperty(label.target)){
28080             return;
28081         }
28082      
28083         this.groups[label.target] = label;
28084         
28085     },
28086     /**
28087     * fetch a FieldLabel Group based on the target
28088     * @param {string} target
28089     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
28090     */
28091     get: function(target) {
28092         if (typeof(this.groups[target]) == 'undefined') {
28093             return false;
28094         }
28095         
28096         return this.groups[target] ;
28097     }
28098 });
28099
28100  
28101
28102  /*
28103  * - LGPL
28104  *
28105  * page DateSplitField.
28106  * 
28107  */
28108
28109
28110 /**
28111  * @class Roo.bootstrap.DateSplitField
28112  * @extends Roo.bootstrap.Component
28113  * Bootstrap DateSplitField class
28114  * @cfg {string} fieldLabel - the label associated
28115  * @cfg {Number} labelWidth set the width of label (0-12)
28116  * @cfg {String} labelAlign (top|left)
28117  * @cfg {Boolean} dayAllowBlank (true|false) default false
28118  * @cfg {Boolean} monthAllowBlank (true|false) default false
28119  * @cfg {Boolean} yearAllowBlank (true|false) default false
28120  * @cfg {string} dayPlaceholder 
28121  * @cfg {string} monthPlaceholder
28122  * @cfg {string} yearPlaceholder
28123  * @cfg {string} dayFormat default 'd'
28124  * @cfg {string} monthFormat default 'm'
28125  * @cfg {string} yearFormat default 'Y'
28126
28127  *     
28128  * @constructor
28129  * Create a new DateSplitField
28130  * @param {Object} config The config object
28131  */
28132
28133 Roo.bootstrap.DateSplitField = function(config){
28134     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
28135     
28136     this.addEvents({
28137         // raw events
28138          /**
28139          * @event years
28140          * getting the data of years
28141          * @param {Roo.bootstrap.DateSplitField} this
28142          * @param {Object} years
28143          */
28144         "years" : true,
28145         /**
28146          * @event days
28147          * getting the data of days
28148          * @param {Roo.bootstrap.DateSplitField} this
28149          * @param {Object} days
28150          */
28151         "days" : true,
28152         /**
28153          * @event invalid
28154          * Fires after the field has been marked as invalid.
28155          * @param {Roo.form.Field} this
28156          * @param {String} msg The validation message
28157          */
28158         invalid : true,
28159        /**
28160          * @event valid
28161          * Fires after the field has been validated with no errors.
28162          * @param {Roo.form.Field} this
28163          */
28164         valid : true
28165     });
28166 };
28167
28168 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
28169     
28170     fieldLabel : '',
28171     labelAlign : 'top',
28172     labelWidth : 3,
28173     dayAllowBlank : false,
28174     monthAllowBlank : false,
28175     yearAllowBlank : false,
28176     dayPlaceholder : '',
28177     monthPlaceholder : '',
28178     yearPlaceholder : '',
28179     dayFormat : 'd',
28180     monthFormat : 'm',
28181     yearFormat : 'Y',
28182     isFormField : true,
28183     
28184     getAutoCreate : function()
28185     {
28186         var cfg = {
28187             tag : 'div',
28188             cls : 'row roo-date-split-field-group',
28189             cn : [
28190                 {
28191                     tag : 'input',
28192                     type : 'hidden',
28193                     cls : 'form-hidden-field roo-date-split-field-group-value',
28194                     name : this.name
28195                 }
28196             ]
28197         };
28198         
28199         if(this.fieldLabel){
28200             cfg.cn.push({
28201                 tag : 'div',
28202                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
28203                 cn : [
28204                     {
28205                         tag : 'label',
28206                         html : this.fieldLabel
28207                     }
28208                 ]
28209             });
28210         }
28211         
28212         Roo.each(['day', 'month', 'year'], function(t){
28213             cfg.cn.push({
28214                 tag : 'div',
28215                 cls : 'column roo-date-split-field-' + t + ' col-md-' + ((this.labelAlign == 'top') ? '4' : ((12 - this.labelWidth) / 3))
28216             });
28217         }, this);
28218         
28219         return cfg;
28220     },
28221     
28222     inputEl: function ()
28223     {
28224         return this.el.select('.roo-date-split-field-group-value', true).first();
28225     },
28226     
28227     onRender : function(ct, position) 
28228     {
28229         var _this = this;
28230         
28231         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
28232         
28233         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
28234         
28235         this.dayField = new Roo.bootstrap.ComboBox({
28236             allowBlank : this.dayAllowBlank,
28237             alwaysQuery : true,
28238             displayField : 'value',
28239             editable : false,
28240             fieldLabel : '',
28241             forceSelection : true,
28242             mode : 'local',
28243             placeholder : this.dayPlaceholder,
28244             selectOnFocus : true,
28245             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28246             triggerAction : 'all',
28247             typeAhead : true,
28248             valueField : 'value',
28249             store : new Roo.data.SimpleStore({
28250                 data : (function() {    
28251                     var days = [];
28252                     _this.fireEvent('days', _this, days);
28253                     return days;
28254                 })(),
28255                 fields : [ 'value' ]
28256             }),
28257             listeners : {
28258                 select : function (_self, record, index)
28259                 {
28260                     _this.setValue(_this.getValue());
28261                 }
28262             }
28263         });
28264
28265         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
28266         
28267         this.monthField = new Roo.bootstrap.MonthField({
28268             after : '<i class=\"fa fa-calendar\"></i>',
28269             allowBlank : this.monthAllowBlank,
28270             placeholder : this.monthPlaceholder,
28271             readOnly : true,
28272             listeners : {
28273                 render : function (_self)
28274                 {
28275                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
28276                         e.preventDefault();
28277                         _self.focus();
28278                     });
28279                 },
28280                 select : function (_self, oldvalue, newvalue)
28281                 {
28282                     _this.setValue(_this.getValue());
28283                 }
28284             }
28285         });
28286         
28287         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
28288         
28289         this.yearField = new Roo.bootstrap.ComboBox({
28290             allowBlank : this.yearAllowBlank,
28291             alwaysQuery : true,
28292             displayField : 'value',
28293             editable : false,
28294             fieldLabel : '',
28295             forceSelection : true,
28296             mode : 'local',
28297             placeholder : this.yearPlaceholder,
28298             selectOnFocus : true,
28299             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
28300             triggerAction : 'all',
28301             typeAhead : true,
28302             valueField : 'value',
28303             store : new Roo.data.SimpleStore({
28304                 data : (function() {
28305                     var years = [];
28306                     _this.fireEvent('years', _this, years);
28307                     return years;
28308                 })(),
28309                 fields : [ 'value' ]
28310             }),
28311             listeners : {
28312                 select : function (_self, record, index)
28313                 {
28314                     _this.setValue(_this.getValue());
28315                 }
28316             }
28317         });
28318
28319         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
28320     },
28321     
28322     setValue : function(v, format)
28323     {
28324         this.inputEl.dom.value = v;
28325         
28326         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
28327         
28328         var d = Date.parseDate(v, f);
28329         
28330         if(!d){
28331             this.validate();
28332             return;
28333         }
28334         
28335         this.setDay(d.format(this.dayFormat));
28336         this.setMonth(d.format(this.monthFormat));
28337         this.setYear(d.format(this.yearFormat));
28338         
28339         this.validate();
28340         
28341         return;
28342     },
28343     
28344     setDay : function(v)
28345     {
28346         this.dayField.setValue(v);
28347         this.inputEl.dom.value = this.getValue();
28348         this.validate();
28349         return;
28350     },
28351     
28352     setMonth : function(v)
28353     {
28354         this.monthField.setValue(v, true);
28355         this.inputEl.dom.value = this.getValue();
28356         this.validate();
28357         return;
28358     },
28359     
28360     setYear : function(v)
28361     {
28362         this.yearField.setValue(v);
28363         this.inputEl.dom.value = this.getValue();
28364         this.validate();
28365         return;
28366     },
28367     
28368     getDay : function()
28369     {
28370         return this.dayField.getValue();
28371     },
28372     
28373     getMonth : function()
28374     {
28375         return this.monthField.getValue();
28376     },
28377     
28378     getYear : function()
28379     {
28380         return this.yearField.getValue();
28381     },
28382     
28383     getValue : function()
28384     {
28385         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
28386         
28387         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
28388         
28389         return date;
28390     },
28391     
28392     reset : function()
28393     {
28394         this.setDay('');
28395         this.setMonth('');
28396         this.setYear('');
28397         this.inputEl.dom.value = '';
28398         this.validate();
28399         return;
28400     },
28401     
28402     validate : function()
28403     {
28404         var d = this.dayField.validate();
28405         var m = this.monthField.validate();
28406         var y = this.yearField.validate();
28407         
28408         var valid = true;
28409         
28410         if(
28411                 (!this.dayAllowBlank && !d) ||
28412                 (!this.monthAllowBlank && !m) ||
28413                 (!this.yearAllowBlank && !y)
28414         ){
28415             valid = false;
28416         }
28417         
28418         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
28419             return valid;
28420         }
28421         
28422         if(valid){
28423             this.markValid();
28424             return valid;
28425         }
28426         
28427         this.markInvalid();
28428         
28429         return valid;
28430     },
28431     
28432     markValid : function()
28433     {
28434         
28435         var label = this.el.select('label', true).first();
28436         var icon = this.el.select('i.fa-star', true).first();
28437
28438         if(label && icon){
28439             icon.remove();
28440         }
28441         
28442         this.fireEvent('valid', this);
28443     },
28444     
28445      /**
28446      * Mark this field as invalid
28447      * @param {String} msg The validation message
28448      */
28449     markInvalid : function(msg)
28450     {
28451         
28452         var label = this.el.select('label', true).first();
28453         var icon = this.el.select('i.fa-star', true).first();
28454
28455         if(label && !icon){
28456             this.el.select('.roo-date-split-field-label', true).createChild({
28457                 tag : 'i',
28458                 cls : 'text-danger fa fa-lg fa-star',
28459                 tooltip : 'This field is required',
28460                 style : 'margin-right:5px;'
28461             }, label, true);
28462         }
28463         
28464         this.fireEvent('invalid', this, msg);
28465     },
28466     
28467     clearInvalid : function()
28468     {
28469         var label = this.el.select('label', true).first();
28470         var icon = this.el.select('i.fa-star', true).first();
28471
28472         if(label && icon){
28473             icon.remove();
28474         }
28475         
28476         this.fireEvent('valid', this);
28477     },
28478     
28479     getName: function()
28480     {
28481         return this.name;
28482     }
28483     
28484 });
28485
28486  /**
28487  *
28488  * This is based on 
28489  * http://masonry.desandro.com
28490  *
28491  * The idea is to render all the bricks based on vertical width...
28492  *
28493  * The original code extends 'outlayer' - we might need to use that....
28494  * 
28495  */
28496
28497
28498 /**
28499  * @class Roo.bootstrap.LayoutMasonry
28500  * @extends Roo.bootstrap.Component
28501  * Bootstrap Layout Masonry class
28502  * 
28503  * @constructor
28504  * Create a new Element
28505  * @param {Object} config The config object
28506  */
28507
28508 Roo.bootstrap.LayoutMasonry = function(config){
28509     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
28510     
28511     this.bricks = [];
28512     
28513 };
28514
28515 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
28516     
28517     /**
28518      * @cfg {Boolean} isLayoutInstant = no animation?
28519      */   
28520     isLayoutInstant : false, // needed?
28521    
28522     /**
28523      * @cfg {Number} boxWidth  width of the columns
28524      */   
28525     boxWidth : 450,
28526     
28527       /**
28528      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
28529      */   
28530     boxHeight : 0,
28531     
28532     /**
28533      * @cfg {Number} padWidth padding below box..
28534      */   
28535     padWidth : 10, 
28536     
28537     /**
28538      * @cfg {Number} gutter gutter width..
28539      */   
28540     gutter : 10,
28541     
28542      /**
28543      * @cfg {Number} maxCols maximum number of columns
28544      */   
28545     
28546     maxCols: 0,
28547     
28548     /**
28549      * @cfg {Boolean} isAutoInitial defalut true
28550      */   
28551     isAutoInitial : true, 
28552     
28553     containerWidth: 0,
28554     
28555     /**
28556      * @cfg {Boolean} isHorizontal defalut false
28557      */   
28558     isHorizontal : false, 
28559
28560     currentSize : null,
28561     
28562     tag: 'div',
28563     
28564     cls: '',
28565     
28566     bricks: null, //CompositeElement
28567     
28568     cols : 1,
28569     
28570     _isLayoutInited : false,
28571     
28572 //    isAlternative : false, // only use for vertical layout...
28573     
28574     /**
28575      * @cfg {Number} alternativePadWidth padding below box..
28576      */   
28577     alternativePadWidth : 50, 
28578     
28579     getAutoCreate : function(){
28580         
28581         var cfg = {
28582             tag: this.tag,
28583             cls: 'blog-masonary-wrapper ' + this.cls,
28584             cn : {
28585                 cls : 'mas-boxes masonary'
28586             }
28587         };
28588         
28589         return cfg;
28590     },
28591     
28592     getChildContainer: function( )
28593     {
28594         if (this.boxesEl) {
28595             return this.boxesEl;
28596         }
28597         
28598         this.boxesEl = this.el.select('.mas-boxes').first();
28599         
28600         return this.boxesEl;
28601     },
28602     
28603     
28604     initEvents : function()
28605     {
28606         var _this = this;
28607         
28608         if(this.isAutoInitial){
28609             Roo.log('hook children rendered');
28610             this.on('childrenrendered', function() {
28611                 Roo.log('children rendered');
28612                 _this.initial();
28613             } ,this);
28614         }
28615     },
28616     
28617     initial : function()
28618     {
28619         this.currentSize = this.el.getBox(true);
28620         
28621         Roo.EventManager.onWindowResize(this.resize, this); 
28622
28623         if(!this.isAutoInitial){
28624             this.layout();
28625             return;
28626         }
28627         
28628         this.layout();
28629         
28630         return;
28631         //this.layout.defer(500,this);
28632         
28633     },
28634     
28635     resize : function()
28636     {
28637         Roo.log('resize');
28638         
28639         var cs = this.el.getBox(true);
28640         
28641         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
28642             Roo.log("no change in with or X");
28643             return;
28644         }
28645         
28646         this.currentSize = cs;
28647         
28648         this.layout();
28649         
28650     },
28651     
28652     layout : function()
28653     {   
28654         this._resetLayout();
28655         
28656         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
28657         
28658         this.layoutItems( isInstant );
28659       
28660         this._isLayoutInited = true;
28661         
28662     },
28663     
28664     _resetLayout : function()
28665     {
28666         if(this.isHorizontal){
28667             this.horizontalMeasureColumns();
28668             return;
28669         }
28670         
28671         this.verticalMeasureColumns();
28672         
28673     },
28674     
28675     verticalMeasureColumns : function()
28676     {
28677         this.getContainerWidth();
28678         
28679 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28680 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
28681 //            return;
28682 //        }
28683         
28684         var boxWidth = this.boxWidth + this.padWidth;
28685         
28686         if(this.containerWidth < this.boxWidth){
28687             boxWidth = this.containerWidth
28688         }
28689         
28690         var containerWidth = this.containerWidth;
28691         
28692         var cols = Math.floor(containerWidth / boxWidth);
28693         
28694         this.cols = Math.max( cols, 1 );
28695         
28696         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
28697         
28698         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
28699         
28700         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
28701         
28702         this.colWidth = boxWidth + avail - this.padWidth;
28703         
28704         this.unitWidth = Math.floor((this.colWidth - (this.gutter * 2)) / 3);
28705         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
28706     },
28707     
28708     horizontalMeasureColumns : function()
28709     {
28710         this.getContainerWidth();
28711         
28712         var boxWidth = this.boxWidth;
28713         
28714         if(this.containerWidth < boxWidth){
28715             boxWidth = this.containerWidth;
28716         }
28717         
28718         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
28719         
28720         this.el.setHeight(boxWidth);
28721         
28722     },
28723     
28724     getContainerWidth : function()
28725     {
28726         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
28727     },
28728     
28729     layoutItems : function( isInstant )
28730     {
28731         var items = Roo.apply([], this.bricks);
28732         
28733         if(this.isHorizontal){
28734             this._horizontalLayoutItems( items , isInstant );
28735             return;
28736         }
28737         
28738 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
28739 //            this._verticalAlternativeLayoutItems( items , isInstant );
28740 //            return;
28741 //        }
28742         
28743         this._verticalLayoutItems( items , isInstant );
28744         
28745     },
28746     
28747     _verticalLayoutItems : function ( items , isInstant)
28748     {
28749         if ( !items || !items.length ) {
28750             return;
28751         }
28752         
28753         var standard = [
28754             ['xs', 'xs', 'xs', 'tall'],
28755             ['xs', 'xs', 'tall'],
28756             ['xs', 'xs', 'sm'],
28757             ['xs', 'xs', 'xs'],
28758             ['xs', 'tall'],
28759             ['xs', 'sm'],
28760             ['xs', 'xs'],
28761             ['xs'],
28762             
28763             ['sm', 'xs', 'xs'],
28764             ['sm', 'xs'],
28765             ['sm'],
28766             
28767             ['tall', 'xs', 'xs', 'xs'],
28768             ['tall', 'xs', 'xs'],
28769             ['tall', 'xs'],
28770             ['tall']
28771             
28772         ];
28773         
28774         var queue = [];
28775         
28776         var boxes = [];
28777         
28778         var box = [];
28779         
28780         Roo.each(items, function(item, k){
28781             
28782             switch (item.size) {
28783                 // these layouts take up a full box,
28784                 case 'md' :
28785                 case 'md-left' :
28786                 case 'md-right' :
28787                 case 'wide' :
28788                     
28789                     if(box.length){
28790                         boxes.push(box);
28791                         box = [];
28792                     }
28793                     
28794                     boxes.push([item]);
28795                     
28796                     break;
28797                     
28798                 case 'xs' :
28799                 case 'sm' :
28800                 case 'tall' :
28801                     
28802                     box.push(item);
28803                     
28804                     break;
28805                 default :
28806                     break;
28807                     
28808             }
28809             
28810         }, this);
28811         
28812         if(box.length){
28813             boxes.push(box);
28814             box = [];
28815         }
28816         
28817         var filterPattern = function(box, length)
28818         {
28819             if(!box.length){
28820                 return;
28821             }
28822             
28823             var match = false;
28824             
28825             var pattern = box.slice(0, length);
28826             
28827             var format = [];
28828             
28829             Roo.each(pattern, function(i){
28830                 format.push(i.size);
28831             }, this);
28832             
28833             Roo.each(standard, function(s){
28834                 
28835                 if(String(s) != String(format)){
28836                     return;
28837                 }
28838                 
28839                 match = true;
28840                 return false;
28841                 
28842             }, this);
28843             
28844             if(!match && length == 1){
28845                 return;
28846             }
28847             
28848             if(!match){
28849                 filterPattern(box, length - 1);
28850                 return;
28851             }
28852                 
28853             queue.push(pattern);
28854
28855             box = box.slice(length, box.length);
28856
28857             filterPattern(box, 4);
28858
28859             return;
28860             
28861         }
28862         
28863         Roo.each(boxes, function(box, k){
28864             
28865             if(!box.length){
28866                 return;
28867             }
28868             
28869             if(box.length == 1){
28870                 queue.push(box);
28871                 return;
28872             }
28873             
28874             filterPattern(box, 4);
28875             
28876         }, this);
28877         
28878         this._processVerticalLayoutQueue( queue, isInstant );
28879         
28880     },
28881     
28882 //    _verticalAlternativeLayoutItems : function( items , isInstant )
28883 //    {
28884 //        if ( !items || !items.length ) {
28885 //            return;
28886 //        }
28887 //
28888 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
28889 //        
28890 //    },
28891     
28892     _horizontalLayoutItems : function ( items , isInstant)
28893     {
28894         if ( !items || !items.length || items.length < 3) {
28895             return;
28896         }
28897         
28898         items.reverse();
28899         
28900         var eItems = items.slice(0, 3);
28901         
28902         items = items.slice(3, items.length);
28903         
28904         var standard = [
28905             ['xs', 'xs', 'xs', 'wide'],
28906             ['xs', 'xs', 'wide'],
28907             ['xs', 'xs', 'sm'],
28908             ['xs', 'xs', 'xs'],
28909             ['xs', 'wide'],
28910             ['xs', 'sm'],
28911             ['xs', 'xs'],
28912             ['xs'],
28913             
28914             ['sm', 'xs', 'xs'],
28915             ['sm', 'xs'],
28916             ['sm'],
28917             
28918             ['wide', 'xs', 'xs', 'xs'],
28919             ['wide', 'xs', 'xs'],
28920             ['wide', 'xs'],
28921             ['wide'],
28922             
28923             ['wide-thin']
28924         ];
28925         
28926         var queue = [];
28927         
28928         var boxes = [];
28929         
28930         var box = [];
28931         
28932         Roo.each(items, function(item, k){
28933             
28934             switch (item.size) {
28935                 case 'md' :
28936                 case 'md-left' :
28937                 case 'md-right' :
28938                 case 'tall' :
28939                     
28940                     if(box.length){
28941                         boxes.push(box);
28942                         box = [];
28943                     }
28944                     
28945                     boxes.push([item]);
28946                     
28947                     break;
28948                     
28949                 case 'xs' :
28950                 case 'sm' :
28951                 case 'wide' :
28952                 case 'wide-thin' :
28953                     
28954                     box.push(item);
28955                     
28956                     break;
28957                 default :
28958                     break;
28959                     
28960             }
28961             
28962         }, this);
28963         
28964         if(box.length){
28965             boxes.push(box);
28966             box = [];
28967         }
28968         
28969         var filterPattern = function(box, length)
28970         {
28971             if(!box.length){
28972                 return;
28973             }
28974             
28975             var match = false;
28976             
28977             var pattern = box.slice(0, length);
28978             
28979             var format = [];
28980             
28981             Roo.each(pattern, function(i){
28982                 format.push(i.size);
28983             }, this);
28984             
28985             Roo.each(standard, function(s){
28986                 
28987                 if(String(s) != String(format)){
28988                     return;
28989                 }
28990                 
28991                 match = true;
28992                 return false;
28993                 
28994             }, this);
28995             
28996             if(!match && length == 1){
28997                 return;
28998             }
28999             
29000             if(!match){
29001                 filterPattern(box, length - 1);
29002                 return;
29003             }
29004                 
29005             queue.push(pattern);
29006
29007             box = box.slice(length, box.length);
29008
29009             filterPattern(box, 4);
29010
29011             return;
29012             
29013         }
29014         
29015         Roo.each(boxes, function(box, k){
29016             
29017             if(!box.length){
29018                 return;
29019             }
29020             
29021             if(box.length == 1){
29022                 queue.push(box);
29023                 return;
29024             }
29025             
29026             filterPattern(box, 4);
29027             
29028         }, this);
29029         
29030         
29031         var prune = [];
29032         
29033         var pos = this.el.getBox(true);
29034         
29035         var minX = pos.x;
29036         
29037         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29038         
29039         var hit_end = false;
29040         
29041         Roo.each(queue, function(box){
29042             
29043             if(hit_end){
29044                 
29045                 Roo.each(box, function(b){
29046                 
29047                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29048                     b.el.hide();
29049
29050                 }, this);
29051
29052                 return;
29053             }
29054             
29055             var mx = 0;
29056             
29057             Roo.each(box, function(b){
29058                 
29059                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
29060                 b.el.show();
29061
29062                 mx = Math.max(mx, b.x);
29063                 
29064             }, this);
29065             
29066             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
29067             
29068             if(maxX < minX){
29069                 
29070                 Roo.each(box, function(b){
29071                 
29072                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
29073                     b.el.hide();
29074                     
29075                 }, this);
29076                 
29077                 hit_end = true;
29078                 
29079                 return;
29080             }
29081             
29082             prune.push(box);
29083             
29084         }, this);
29085         
29086         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
29087     },
29088     
29089     /** Sets position of item in DOM
29090     * @param {Element} item
29091     * @param {Number} x - horizontal position
29092     * @param {Number} y - vertical position
29093     * @param {Boolean} isInstant - disables transitions
29094     */
29095     _processVerticalLayoutQueue : function( queue, isInstant )
29096     {
29097         var pos = this.el.getBox(true);
29098         var x = pos.x;
29099         var y = pos.y;
29100         var maxY = [];
29101         
29102         for (var i = 0; i < this.cols; i++){
29103             maxY[i] = pos.y;
29104         }
29105         
29106         Roo.each(queue, function(box, k){
29107             
29108             var col = k % this.cols;
29109             
29110             Roo.each(box, function(b,kk){
29111                 
29112                 b.el.position('absolute');
29113                 
29114                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29115                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29116                 
29117                 if(b.size == 'md-left' || b.size == 'md-right'){
29118                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29119                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29120                 }
29121                 
29122                 b.el.setWidth(width);
29123                 b.el.setHeight(height);
29124                 // iframe?
29125                 b.el.select('iframe',true).setSize(width,height);
29126                 
29127             }, this);
29128             
29129             for (var i = 0; i < this.cols; i++){
29130                 
29131                 if(maxY[i] < maxY[col]){
29132                     col = i;
29133                     continue;
29134                 }
29135                 
29136                 col = Math.min(col, i);
29137                 
29138             }
29139             
29140             x = pos.x + col * (this.colWidth + this.padWidth);
29141             
29142             y = maxY[col];
29143             
29144             var positions = [];
29145             
29146             switch (box.length){
29147                 case 1 :
29148                     positions = this.getVerticalOneBoxColPositions(x, y, box);
29149                     break;
29150                 case 2 :
29151                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
29152                     break;
29153                 case 3 :
29154                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
29155                     break;
29156                 case 4 :
29157                     positions = this.getVerticalFourBoxColPositions(x, y, box);
29158                     break;
29159                 default :
29160                     break;
29161             }
29162             
29163             Roo.each(box, function(b,kk){
29164                 
29165                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29166                 
29167                 var sz = b.el.getSize();
29168                 
29169                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
29170                 
29171             }, this);
29172             
29173         }, this);
29174         
29175         var mY = 0;
29176         
29177         for (var i = 0; i < this.cols; i++){
29178             mY = Math.max(mY, maxY[i]);
29179         }
29180         
29181         this.el.setHeight(mY - pos.y);
29182         
29183     },
29184     
29185 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
29186 //    {
29187 //        var pos = this.el.getBox(true);
29188 //        var x = pos.x;
29189 //        var y = pos.y;
29190 //        var maxX = pos.right;
29191 //        
29192 //        var maxHeight = 0;
29193 //        
29194 //        Roo.each(items, function(item, k){
29195 //            
29196 //            var c = k % 2;
29197 //            
29198 //            item.el.position('absolute');
29199 //                
29200 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
29201 //
29202 //            item.el.setWidth(width);
29203 //
29204 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
29205 //
29206 //            item.el.setHeight(height);
29207 //            
29208 //            if(c == 0){
29209 //                item.el.setXY([x, y], isInstant ? false : true);
29210 //            } else {
29211 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
29212 //            }
29213 //            
29214 //            y = y + height + this.alternativePadWidth;
29215 //            
29216 //            maxHeight = maxHeight + height + this.alternativePadWidth;
29217 //            
29218 //        }, this);
29219 //        
29220 //        this.el.setHeight(maxHeight);
29221 //        
29222 //    },
29223     
29224     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
29225     {
29226         var pos = this.el.getBox(true);
29227         
29228         var minX = pos.x;
29229         var minY = pos.y;
29230         
29231         var maxX = pos.right;
29232         
29233         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
29234         
29235         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
29236         
29237         Roo.each(queue, function(box, k){
29238             
29239             Roo.each(box, function(b, kk){
29240                 
29241                 b.el.position('absolute');
29242                 
29243                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29244                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29245                 
29246                 if(b.size == 'md-left' || b.size == 'md-right'){
29247                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
29248                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
29249                 }
29250                 
29251                 b.el.setWidth(width);
29252                 b.el.setHeight(height);
29253                 
29254             }, this);
29255             
29256             if(!box.length){
29257                 return;
29258             }
29259             
29260             var positions = [];
29261             
29262             switch (box.length){
29263                 case 1 :
29264                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
29265                     break;
29266                 case 2 :
29267                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
29268                     break;
29269                 case 3 :
29270                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
29271                     break;
29272                 case 4 :
29273                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
29274                     break;
29275                 default :
29276                     break;
29277             }
29278             
29279             Roo.each(box, function(b,kk){
29280                 
29281                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
29282                 
29283                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
29284                 
29285             }, this);
29286             
29287         }, this);
29288         
29289     },
29290     
29291     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
29292     {
29293         Roo.each(eItems, function(b,k){
29294             
29295             b.size = (k == 0) ? 'sm' : 'xs';
29296             b.x = (k == 0) ? 2 : 1;
29297             b.y = (k == 0) ? 2 : 1;
29298             
29299             b.el.position('absolute');
29300             
29301             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
29302                 
29303             b.el.setWidth(width);
29304             
29305             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
29306             
29307             b.el.setHeight(height);
29308             
29309         }, this);
29310
29311         var positions = [];
29312         
29313         positions.push({
29314             x : maxX - this.unitWidth * 2 - this.gutter,
29315             y : minY
29316         });
29317         
29318         positions.push({
29319             x : maxX - this.unitWidth,
29320             y : minY + (this.unitWidth + this.gutter) * 2
29321         });
29322         
29323         positions.push({
29324             x : maxX - this.unitWidth * 3 - this.gutter * 2,
29325             y : minY
29326         });
29327         
29328         Roo.each(eItems, function(b,k){
29329             
29330             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
29331
29332         }, this);
29333         
29334     },
29335     
29336     getVerticalOneBoxColPositions : function(x, y, box)
29337     {
29338         var pos = [];
29339         
29340         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
29341         
29342         if(box[0].size == 'md-left'){
29343             rand = 0;
29344         }
29345         
29346         if(box[0].size == 'md-right'){
29347             rand = 1;
29348         }
29349         
29350         pos.push({
29351             x : x + (this.unitWidth + this.gutter) * rand,
29352             y : y
29353         });
29354         
29355         return pos;
29356     },
29357     
29358     getVerticalTwoBoxColPositions : function(x, y, box)
29359     {
29360         var pos = [];
29361         
29362         if(box[0].size == 'xs'){
29363             
29364             pos.push({
29365                 x : x,
29366                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
29367             });
29368
29369             pos.push({
29370                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
29371                 y : y
29372             });
29373             
29374             return pos;
29375             
29376         }
29377         
29378         pos.push({
29379             x : x,
29380             y : y
29381         });
29382
29383         pos.push({
29384             x : x + (this.unitWidth + this.gutter) * 2,
29385             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
29386         });
29387         
29388         return pos;
29389         
29390     },
29391     
29392     getVerticalThreeBoxColPositions : function(x, y, box)
29393     {
29394         var pos = [];
29395         
29396         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29397             
29398             pos.push({
29399                 x : x,
29400                 y : y
29401             });
29402
29403             pos.push({
29404                 x : x + (this.unitWidth + this.gutter) * 1,
29405                 y : y
29406             });
29407             
29408             pos.push({
29409                 x : x + (this.unitWidth + this.gutter) * 2,
29410                 y : y
29411             });
29412             
29413             return pos;
29414             
29415         }
29416         
29417         if(box[0].size == 'xs' && box[1].size == 'xs'){
29418             
29419             pos.push({
29420                 x : x,
29421                 y : y
29422             });
29423
29424             pos.push({
29425                 x : x,
29426                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
29427             });
29428             
29429             pos.push({
29430                 x : x + (this.unitWidth + this.gutter) * 1,
29431                 y : y
29432             });
29433             
29434             return pos;
29435             
29436         }
29437         
29438         pos.push({
29439             x : x,
29440             y : y
29441         });
29442
29443         pos.push({
29444             x : x + (this.unitWidth + this.gutter) * 2,
29445             y : y
29446         });
29447
29448         pos.push({
29449             x : x + (this.unitWidth + this.gutter) * 2,
29450             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
29451         });
29452             
29453         return pos;
29454         
29455     },
29456     
29457     getVerticalFourBoxColPositions : function(x, y, box)
29458     {
29459         var pos = [];
29460         
29461         if(box[0].size == 'xs'){
29462             
29463             pos.push({
29464                 x : x,
29465                 y : y
29466             });
29467
29468             pos.push({
29469                 x : x,
29470                 y : y + (this.unitHeight + this.gutter) * 1
29471             });
29472             
29473             pos.push({
29474                 x : x,
29475                 y : y + (this.unitHeight + this.gutter) * 2
29476             });
29477             
29478             pos.push({
29479                 x : x + (this.unitWidth + this.gutter) * 1,
29480                 y : y
29481             });
29482             
29483             return pos;
29484             
29485         }
29486         
29487         pos.push({
29488             x : x,
29489             y : y
29490         });
29491
29492         pos.push({
29493             x : x + (this.unitWidth + this.gutter) * 2,
29494             y : y
29495         });
29496
29497         pos.push({
29498             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
29499             y : y + (this.unitHeight + this.gutter) * 1
29500         });
29501
29502         pos.push({
29503             x : x + (this.unitWidth + this.gutter) * 2,
29504             y : y + (this.unitWidth + this.gutter) * 2
29505         });
29506
29507         return pos;
29508         
29509     },
29510     
29511     getHorizontalOneBoxColPositions : function(maxX, minY, box)
29512     {
29513         var pos = [];
29514         
29515         if(box[0].size == 'md-left'){
29516             pos.push({
29517                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29518                 y : minY
29519             });
29520             
29521             return pos;
29522         }
29523         
29524         if(box[0].size == 'md-right'){
29525             pos.push({
29526                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
29527                 y : minY + (this.unitWidth + this.gutter) * 1
29528             });
29529             
29530             return pos;
29531         }
29532         
29533         var rand = Math.floor(Math.random() * (4 - box[0].y));
29534         
29535         pos.push({
29536             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29537             y : minY + (this.unitWidth + this.gutter) * rand
29538         });
29539         
29540         return pos;
29541         
29542     },
29543     
29544     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
29545     {
29546         var pos = [];
29547         
29548         if(box[0].size == 'xs'){
29549             
29550             pos.push({
29551                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29552                 y : minY
29553             });
29554
29555             pos.push({
29556                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29557                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
29558             });
29559             
29560             return pos;
29561             
29562         }
29563         
29564         pos.push({
29565             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29566             y : minY
29567         });
29568
29569         pos.push({
29570             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29571             y : minY + (this.unitWidth + this.gutter) * 2
29572         });
29573         
29574         return pos;
29575         
29576     },
29577     
29578     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
29579     {
29580         var pos = [];
29581         
29582         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
29583             
29584             pos.push({
29585                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29586                 y : minY
29587             });
29588
29589             pos.push({
29590                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29591                 y : minY + (this.unitWidth + this.gutter) * 1
29592             });
29593             
29594             pos.push({
29595                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29596                 y : minY + (this.unitWidth + this.gutter) * 2
29597             });
29598             
29599             return pos;
29600             
29601         }
29602         
29603         if(box[0].size == 'xs' && box[1].size == 'xs'){
29604             
29605             pos.push({
29606                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29607                 y : minY
29608             });
29609
29610             pos.push({
29611                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29612                 y : minY
29613             });
29614             
29615             pos.push({
29616                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29617                 y : minY + (this.unitWidth + this.gutter) * 1
29618             });
29619             
29620             return pos;
29621             
29622         }
29623         
29624         pos.push({
29625             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29626             y : minY
29627         });
29628
29629         pos.push({
29630             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29631             y : minY + (this.unitWidth + this.gutter) * 2
29632         });
29633
29634         pos.push({
29635             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29636             y : minY + (this.unitWidth + this.gutter) * 2
29637         });
29638             
29639         return pos;
29640         
29641     },
29642     
29643     getHorizontalFourBoxColPositions : function(maxX, minY, box)
29644     {
29645         var pos = [];
29646         
29647         if(box[0].size == 'xs'){
29648             
29649             pos.push({
29650                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29651                 y : minY
29652             });
29653
29654             pos.push({
29655                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29656                 y : minY
29657             });
29658             
29659             pos.push({
29660                 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),
29661                 y : minY
29662             });
29663             
29664             pos.push({
29665                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
29666                 y : minY + (this.unitWidth + this.gutter) * 1
29667             });
29668             
29669             return pos;
29670             
29671         }
29672         
29673         pos.push({
29674             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
29675             y : minY
29676         });
29677         
29678         pos.push({
29679             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
29680             y : minY + (this.unitWidth + this.gutter) * 2
29681         });
29682         
29683         pos.push({
29684             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
29685             y : minY + (this.unitWidth + this.gutter) * 2
29686         });
29687         
29688         pos.push({
29689             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),
29690             y : minY + (this.unitWidth + this.gutter) * 2
29691         });
29692
29693         return pos;
29694         
29695     }
29696     
29697 });
29698
29699  
29700
29701  /**
29702  *
29703  * This is based on 
29704  * http://masonry.desandro.com
29705  *
29706  * The idea is to render all the bricks based on vertical width...
29707  *
29708  * The original code extends 'outlayer' - we might need to use that....
29709  * 
29710  */
29711
29712
29713 /**
29714  * @class Roo.bootstrap.LayoutMasonryAuto
29715  * @extends Roo.bootstrap.Component
29716  * Bootstrap Layout Masonry class
29717  * 
29718  * @constructor
29719  * Create a new Element
29720  * @param {Object} config The config object
29721  */
29722
29723 Roo.bootstrap.LayoutMasonryAuto = function(config){
29724     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
29725 };
29726
29727 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
29728     
29729       /**
29730      * @cfg {Boolean} isFitWidth  - resize the width..
29731      */   
29732     isFitWidth : false,  // options..
29733     /**
29734      * @cfg {Boolean} isOriginLeft = left align?
29735      */   
29736     isOriginLeft : true,
29737     /**
29738      * @cfg {Boolean} isOriginTop = top align?
29739      */   
29740     isOriginTop : false,
29741     /**
29742      * @cfg {Boolean} isLayoutInstant = no animation?
29743      */   
29744     isLayoutInstant : false, // needed?
29745     /**
29746      * @cfg {Boolean} isResizingContainer = not sure if this is used..
29747      */   
29748     isResizingContainer : true,
29749     /**
29750      * @cfg {Number} columnWidth  width of the columns 
29751      */   
29752     
29753     columnWidth : 0,
29754     
29755     /**
29756      * @cfg {Number} maxCols maximum number of columns
29757      */   
29758     
29759     maxCols: 0,
29760     /**
29761      * @cfg {Number} padHeight padding below box..
29762      */   
29763     
29764     padHeight : 10, 
29765     
29766     /**
29767      * @cfg {Boolean} isAutoInitial defalut true
29768      */   
29769     
29770     isAutoInitial : true, 
29771     
29772     // private?
29773     gutter : 0,
29774     
29775     containerWidth: 0,
29776     initialColumnWidth : 0,
29777     currentSize : null,
29778     
29779     colYs : null, // array.
29780     maxY : 0,
29781     padWidth: 10,
29782     
29783     
29784     tag: 'div',
29785     cls: '',
29786     bricks: null, //CompositeElement
29787     cols : 0, // array?
29788     // element : null, // wrapped now this.el
29789     _isLayoutInited : null, 
29790     
29791     
29792     getAutoCreate : function(){
29793         
29794         var cfg = {
29795             tag: this.tag,
29796             cls: 'blog-masonary-wrapper ' + this.cls,
29797             cn : {
29798                 cls : 'mas-boxes masonary'
29799             }
29800         };
29801         
29802         return cfg;
29803     },
29804     
29805     getChildContainer: function( )
29806     {
29807         if (this.boxesEl) {
29808             return this.boxesEl;
29809         }
29810         
29811         this.boxesEl = this.el.select('.mas-boxes').first();
29812         
29813         return this.boxesEl;
29814     },
29815     
29816     
29817     initEvents : function()
29818     {
29819         var _this = this;
29820         
29821         if(this.isAutoInitial){
29822             Roo.log('hook children rendered');
29823             this.on('childrenrendered', function() {
29824                 Roo.log('children rendered');
29825                 _this.initial();
29826             } ,this);
29827         }
29828         
29829     },
29830     
29831     initial : function()
29832     {
29833         this.reloadItems();
29834
29835         this.currentSize = this.el.getBox(true);
29836
29837         /// was window resize... - let's see if this works..
29838         Roo.EventManager.onWindowResize(this.resize, this); 
29839
29840         if(!this.isAutoInitial){
29841             this.layout();
29842             return;
29843         }
29844         
29845         this.layout.defer(500,this);
29846     },
29847     
29848     reloadItems: function()
29849     {
29850         this.bricks = this.el.select('.masonry-brick', true);
29851         
29852         this.bricks.each(function(b) {
29853             //Roo.log(b.getSize());
29854             if (!b.attr('originalwidth')) {
29855                 b.attr('originalwidth',  b.getSize().width);
29856             }
29857             
29858         });
29859         
29860         Roo.log(this.bricks.elements.length);
29861     },
29862     
29863     resize : function()
29864     {
29865         Roo.log('resize');
29866         var cs = this.el.getBox(true);
29867         
29868         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
29869             Roo.log("no change in with or X");
29870             return;
29871         }
29872         this.currentSize = cs;
29873         this.layout();
29874     },
29875     
29876     layout : function()
29877     {
29878          Roo.log('layout');
29879         this._resetLayout();
29880         //this._manageStamps();
29881       
29882         // don't animate first layout
29883         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
29884         this.layoutItems( isInstant );
29885       
29886         // flag for initalized
29887         this._isLayoutInited = true;
29888     },
29889     
29890     layoutItems : function( isInstant )
29891     {
29892         //var items = this._getItemsForLayout( this.items );
29893         // original code supports filtering layout items.. we just ignore it..
29894         
29895         this._layoutItems( this.bricks , isInstant );
29896       
29897         this._postLayout();
29898     },
29899     _layoutItems : function ( items , isInstant)
29900     {
29901        //this.fireEvent( 'layout', this, items );
29902     
29903
29904         if ( !items || !items.elements.length ) {
29905           // no items, emit event with empty array
29906             return;
29907         }
29908
29909         var queue = [];
29910         items.each(function(item) {
29911             Roo.log("layout item");
29912             Roo.log(item);
29913             // get x/y object from method
29914             var position = this._getItemLayoutPosition( item );
29915             // enqueue
29916             position.item = item;
29917             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
29918             queue.push( position );
29919         }, this);
29920       
29921         this._processLayoutQueue( queue );
29922     },
29923     /** Sets position of item in DOM
29924     * @param {Element} item
29925     * @param {Number} x - horizontal position
29926     * @param {Number} y - vertical position
29927     * @param {Boolean} isInstant - disables transitions
29928     */
29929     _processLayoutQueue : function( queue )
29930     {
29931         for ( var i=0, len = queue.length; i < len; i++ ) {
29932             var obj = queue[i];
29933             obj.item.position('absolute');
29934             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
29935         }
29936     },
29937       
29938     
29939     /**
29940     * Any logic you want to do after each layout,
29941     * i.e. size the container
29942     */
29943     _postLayout : function()
29944     {
29945         this.resizeContainer();
29946     },
29947     
29948     resizeContainer : function()
29949     {
29950         if ( !this.isResizingContainer ) {
29951             return;
29952         }
29953         var size = this._getContainerSize();
29954         if ( size ) {
29955             this.el.setSize(size.width,size.height);
29956             this.boxesEl.setSize(size.width,size.height);
29957         }
29958     },
29959     
29960     
29961     
29962     _resetLayout : function()
29963     {
29964         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
29965         this.colWidth = this.el.getWidth();
29966         //this.gutter = this.el.getWidth(); 
29967         
29968         this.measureColumns();
29969
29970         // reset column Y
29971         var i = this.cols;
29972         this.colYs = [];
29973         while (i--) {
29974             this.colYs.push( 0 );
29975         }
29976     
29977         this.maxY = 0;
29978     },
29979
29980     measureColumns : function()
29981     {
29982         this.getContainerWidth();
29983       // if columnWidth is 0, default to outerWidth of first item
29984         if ( !this.columnWidth ) {
29985             var firstItem = this.bricks.first();
29986             Roo.log(firstItem);
29987             this.columnWidth  = this.containerWidth;
29988             if (firstItem && firstItem.attr('originalwidth') ) {
29989                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
29990             }
29991             // columnWidth fall back to item of first element
29992             Roo.log("set column width?");
29993                         this.initialColumnWidth = this.columnWidth  ;
29994
29995             // if first elem has no width, default to size of container
29996             
29997         }
29998         
29999         
30000         if (this.initialColumnWidth) {
30001             this.columnWidth = this.initialColumnWidth;
30002         }
30003         
30004         
30005             
30006         // column width is fixed at the top - however if container width get's smaller we should
30007         // reduce it...
30008         
30009         // this bit calcs how man columns..
30010             
30011         var columnWidth = this.columnWidth += this.gutter;
30012       
30013         // calculate columns
30014         var containerWidth = this.containerWidth + this.gutter;
30015         
30016         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
30017         // fix rounding errors, typically with gutters
30018         var excess = columnWidth - containerWidth % columnWidth;
30019         
30020         
30021         // if overshoot is less than a pixel, round up, otherwise floor it
30022         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
30023         cols = Math[ mathMethod ]( cols );
30024         this.cols = Math.max( cols, 1 );
30025         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30026         
30027          // padding positioning..
30028         var totalColWidth = this.cols * this.columnWidth;
30029         var padavail = this.containerWidth - totalColWidth;
30030         // so for 2 columns - we need 3 'pads'
30031         
30032         var padNeeded = (1+this.cols) * this.padWidth;
30033         
30034         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
30035         
30036         this.columnWidth += padExtra
30037         //this.padWidth = Math.floor(padavail /  ( this.cols));
30038         
30039         // adjust colum width so that padding is fixed??
30040         
30041         // we have 3 columns ... total = width * 3
30042         // we have X left over... that should be used by 
30043         
30044         //if (this.expandC) {
30045             
30046         //}
30047         
30048         
30049         
30050     },
30051     
30052     getContainerWidth : function()
30053     {
30054        /* // container is parent if fit width
30055         var container = this.isFitWidth ? this.element.parentNode : this.element;
30056         // check that this.size and size are there
30057         // IE8 triggers resize on body size change, so they might not be
30058         
30059         var size = getSize( container );  //FIXME
30060         this.containerWidth = size && size.innerWidth; //FIXME
30061         */
30062          
30063         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30064         
30065     },
30066     
30067     _getItemLayoutPosition : function( item )  // what is item?
30068     {
30069         // we resize the item to our columnWidth..
30070       
30071         item.setWidth(this.columnWidth);
30072         item.autoBoxAdjust  = false;
30073         
30074         var sz = item.getSize();
30075  
30076         // how many columns does this brick span
30077         var remainder = this.containerWidth % this.columnWidth;
30078         
30079         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
30080         // round if off by 1 pixel, otherwise use ceil
30081         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
30082         colSpan = Math.min( colSpan, this.cols );
30083         
30084         // normally this should be '1' as we dont' currently allow multi width columns..
30085         
30086         var colGroup = this._getColGroup( colSpan );
30087         // get the minimum Y value from the columns
30088         var minimumY = Math.min.apply( Math, colGroup );
30089         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30090         
30091         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
30092          
30093         // position the brick
30094         var position = {
30095             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
30096             y: this.currentSize.y + minimumY + this.padHeight
30097         };
30098         
30099         Roo.log(position);
30100         // apply setHeight to necessary columns
30101         var setHeight = minimumY + sz.height + this.padHeight;
30102         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
30103         
30104         var setSpan = this.cols + 1 - colGroup.length;
30105         for ( var i = 0; i < setSpan; i++ ) {
30106           this.colYs[ shortColIndex + i ] = setHeight ;
30107         }
30108       
30109         return position;
30110     },
30111     
30112     /**
30113      * @param {Number} colSpan - number of columns the element spans
30114      * @returns {Array} colGroup
30115      */
30116     _getColGroup : function( colSpan )
30117     {
30118         if ( colSpan < 2 ) {
30119           // if brick spans only one column, use all the column Ys
30120           return this.colYs;
30121         }
30122       
30123         var colGroup = [];
30124         // how many different places could this brick fit horizontally
30125         var groupCount = this.cols + 1 - colSpan;
30126         // for each group potential horizontal position
30127         for ( var i = 0; i < groupCount; i++ ) {
30128           // make an array of colY values for that one group
30129           var groupColYs = this.colYs.slice( i, i + colSpan );
30130           // and get the max value of the array
30131           colGroup[i] = Math.max.apply( Math, groupColYs );
30132         }
30133         return colGroup;
30134     },
30135     /*
30136     _manageStamp : function( stamp )
30137     {
30138         var stampSize =  stamp.getSize();
30139         var offset = stamp.getBox();
30140         // get the columns that this stamp affects
30141         var firstX = this.isOriginLeft ? offset.x : offset.right;
30142         var lastX = firstX + stampSize.width;
30143         var firstCol = Math.floor( firstX / this.columnWidth );
30144         firstCol = Math.max( 0, firstCol );
30145         
30146         var lastCol = Math.floor( lastX / this.columnWidth );
30147         // lastCol should not go over if multiple of columnWidth #425
30148         lastCol -= lastX % this.columnWidth ? 0 : 1;
30149         lastCol = Math.min( this.cols - 1, lastCol );
30150         
30151         // set colYs to bottom of the stamp
30152         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
30153             stampSize.height;
30154             
30155         for ( var i = firstCol; i <= lastCol; i++ ) {
30156           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
30157         }
30158     },
30159     */
30160     
30161     _getContainerSize : function()
30162     {
30163         this.maxY = Math.max.apply( Math, this.colYs );
30164         var size = {
30165             height: this.maxY
30166         };
30167       
30168         if ( this.isFitWidth ) {
30169             size.width = this._getContainerFitWidth();
30170         }
30171       
30172         return size;
30173     },
30174     
30175     _getContainerFitWidth : function()
30176     {
30177         var unusedCols = 0;
30178         // count unused columns
30179         var i = this.cols;
30180         while ( --i ) {
30181           if ( this.colYs[i] !== 0 ) {
30182             break;
30183           }
30184           unusedCols++;
30185         }
30186         // fit container to columns that have been used
30187         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
30188     },
30189     
30190     needsResizeLayout : function()
30191     {
30192         var previousWidth = this.containerWidth;
30193         this.getContainerWidth();
30194         return previousWidth !== this.containerWidth;
30195     }
30196  
30197 });
30198
30199  
30200
30201  /*
30202  * - LGPL
30203  *
30204  * element
30205  * 
30206  */
30207
30208 /**
30209  * @class Roo.bootstrap.MasonryBrick
30210  * @extends Roo.bootstrap.Component
30211  * Bootstrap MasonryBrick class
30212  * 
30213  * @constructor
30214  * Create a new MasonryBrick
30215  * @param {Object} config The config object
30216  */
30217
30218 Roo.bootstrap.MasonryBrick = function(config){
30219     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
30220     
30221     this.addEvents({
30222         // raw events
30223         /**
30224          * @event click
30225          * When a MasonryBrick is clcik
30226          * @param {Roo.bootstrap.MasonryBrick} this
30227          * @param {Roo.EventObject} e
30228          */
30229         "click" : true
30230     });
30231 };
30232
30233 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
30234     
30235     /**
30236      * @cfg {String} title
30237      */   
30238     title : '',
30239     /**
30240      * @cfg {String} html
30241      */   
30242     html : '',
30243     /**
30244      * @cfg {String} bgimage
30245      */   
30246     bgimage : '',
30247     /**
30248      * @cfg {String} videourl
30249      */   
30250     videourl : '',
30251     /**
30252      * @cfg {String} cls
30253      */   
30254     cls : '',
30255     /**
30256      * @cfg {String} href
30257      */   
30258     href : '',
30259     /**
30260      * @cfg {String} (xs|sm|md|md-left|md-right|tall|wide) size
30261      */   
30262     size : 'xs',
30263     
30264     /**
30265      * @cfg {String} (center|bottom) placetitle
30266      */   
30267     placetitle : '',
30268     
30269     getAutoCreate : function()
30270     {
30271         var cls = 'masonry-brick';
30272         
30273         if(this.href.length){
30274             cls += ' masonry-brick-link';
30275         }
30276         
30277         if(this.bgimage.length){
30278             cls += ' masonry-brick-image';
30279         }
30280         
30281         if(this.size){
30282             cls += ' masonry-' + this.size + '-brick';
30283         }
30284         
30285         if(this.placetitle.length){
30286             
30287             switch (this.placetitle) {
30288                 case 'center' :
30289                     cls += ' masonry-center-title';
30290                     break;
30291                 case 'bottom' :
30292                     cls += ' masonry-bottom-title';
30293                     break;
30294                 default:
30295                     break;
30296             }
30297             
30298         } else {
30299             if(!this.html.length && !this.bgimage.length){
30300                 cls += ' masonry-center-title';
30301             }
30302
30303             if(!this.html.length && this.bgimage.length){
30304                 cls += ' masonry-bottom-title';
30305             }
30306         }
30307         
30308         if(this.cls){
30309             cls += ' ' + this.cls;
30310         }
30311         
30312         var cfg = {
30313             tag: (this.href.length) ? 'a' : 'div',
30314             cls: cls,
30315             cn: [
30316                 {
30317                     tag: 'div',
30318                     cls: 'masonry-brick-paragraph',
30319                     cn: []
30320                 }
30321             ]
30322         };
30323         
30324         if(this.href.length){
30325             cfg.href = this.href;
30326         }
30327         
30328         var cn = cfg.cn[0].cn;
30329         
30330         if(this.title.length){
30331             cn.push({
30332                 tag: 'h4',
30333                 cls: 'masonry-brick-title',
30334                 html: this.title
30335             });
30336         }
30337         
30338         if(this.html.length){
30339             cn.push({
30340                 tag: 'p',
30341                 cls: 'masonry-brick-text',
30342                 html: this.html
30343             });
30344         }  
30345         if (!this.title.length && !this.html.length) {
30346             cfg.cn[0].cls += ' hide';
30347         }
30348         
30349         if(this.bgimage.length){
30350             cfg.cn.push({
30351                 tag: 'img',
30352                 cls: 'masonry-brick-image-view',
30353                 src: this.bgimage
30354             });
30355         }
30356         if(this.videourl.length){
30357             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
30358             // youtube support only?
30359             cfg.cn.push({
30360                 tag: 'iframe',
30361                 cls: 'masonry-brick-image-view',
30362                 src: vurl,
30363                 frameborder : 0,
30364                 allowfullscreen : true
30365             });
30366             
30367             
30368         }
30369         return cfg;
30370         
30371     },
30372     
30373     initEvents: function() 
30374     {
30375         switch (this.size) {
30376             case 'xs' :
30377 //                this.intSize = 1;
30378                 this.x = 1;
30379                 this.y = 1;
30380                 break;
30381             case 'sm' :
30382 //                this.intSize = 2;
30383                 this.x = 2;
30384                 this.y = 2;
30385                 break;
30386             case 'md' :
30387             case 'md-left' :
30388             case 'md-right' :
30389 //                this.intSize = 3;
30390                 this.x = 3;
30391                 this.y = 3;
30392                 break;
30393             case 'tall' :
30394 //                this.intSize = 3;
30395                 this.x = 2;
30396                 this.y = 3;
30397                 break;
30398             case 'wide' :
30399 //                this.intSize = 3;
30400                 this.x = 3;
30401                 this.y = 2;
30402                 break;
30403             case 'wide-thin' :
30404 //                this.intSize = 3;
30405                 this.x = 3;
30406                 this.y = 1;
30407                 break;
30408                         
30409             default :
30410                 break;
30411         }
30412         
30413         
30414         
30415         if(Roo.isTouch){
30416             this.el.on('touchstart', this.onTouchStart, this);
30417             this.el.on('touchmove', this.onTouchMove, this);
30418             this.el.on('touchend', this.onTouchEnd, this);
30419             this.el.on('contextmenu', this.onContextMenu, this);
30420         } else {
30421             this.el.on('mouseenter'  ,this.enter, this);
30422             this.el.on('mouseleave', this.leave, this);
30423         }
30424         
30425         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
30426             this.parent().bricks.push(this);   
30427         }
30428         
30429     },
30430     
30431     onClick: function(e, el)
30432     {
30433         if(!Roo.isTouch){
30434             return;
30435         }
30436         
30437         var time = this.endTimer - this.startTimer;
30438         
30439         //alert(time);
30440         
30441         if(time < 1000){
30442             return;
30443         }
30444         
30445         e.preventDefault();
30446     },
30447     
30448     enter: function(e, el)
30449     {
30450         e.preventDefault();
30451         
30452         if(this.bgimage.length && this.html.length){
30453             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30454         }
30455     },
30456     
30457     leave: function(e, el)
30458     {
30459         e.preventDefault();
30460         
30461         if(this.bgimage.length && this.html.length){
30462             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30463         }
30464     },
30465     
30466     onTouchStart: function(e, el)
30467     {
30468 //        e.preventDefault();
30469         
30470         this.touchmoved = false;
30471         
30472         if(!this.bgimage.length || !this.html.length){
30473             return;
30474         }
30475         
30476         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
30477         
30478         this.timer = new Date().getTime();
30479         
30480     },
30481     
30482     onTouchMove: function(e, el)
30483     {
30484         this.touchmoved = true;
30485     },
30486     
30487     onContextMenu : function(e,el)
30488     {
30489         e.preventDefault();
30490         e.stopPropagation();
30491         return false;
30492     },
30493     
30494     onTouchEnd: function(e, el)
30495     {
30496 //        e.preventDefault();
30497         
30498         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
30499         
30500             this.leave(e,el);
30501             
30502             return;
30503         }
30504         
30505         if(!this.bgimage.length || !this.html.length){
30506             
30507             if(this.href.length){
30508                 window.location.href = this.href;
30509             }
30510             
30511             return;
30512         }
30513         
30514         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
30515         
30516         window.location.href = this.href;
30517     }
30518     
30519 });
30520
30521  
30522
30523  /*
30524  * - LGPL
30525  *
30526  * element
30527  * 
30528  */
30529
30530 /**
30531  * @class Roo.bootstrap.Brick
30532  * @extends Roo.bootstrap.Component
30533  * Bootstrap Brick class
30534  * 
30535  * @constructor
30536  * Create a new Brick
30537  * @param {Object} config The config object
30538  */
30539
30540 Roo.bootstrap.Brick = function(config){
30541     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
30542     
30543     this.addEvents({
30544         // raw events
30545         /**
30546          * @event click
30547          * When a Brick is click
30548          * @param {Roo.bootstrap.Brick} this
30549          * @param {Roo.EventObject} e
30550          */
30551         "click" : true
30552     });
30553 };
30554
30555 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
30556     
30557     /**
30558      * @cfg {String} title
30559      */   
30560     title : '',
30561     /**
30562      * @cfg {String} html
30563      */   
30564     html : '',
30565     /**
30566      * @cfg {String} bgimage
30567      */   
30568     bgimage : '',
30569     /**
30570      * @cfg {String} cls
30571      */   
30572     cls : '',
30573     /**
30574      * @cfg {String} href
30575      */   
30576     href : '',
30577     /**
30578      * @cfg {String} video
30579      */   
30580     video : '',
30581     /**
30582      * @cfg {Boolean} square
30583      */   
30584     square : true,
30585     
30586     getAutoCreate : function()
30587     {
30588         var cls = 'roo-brick';
30589         
30590         if(this.href.length){
30591             cls += ' roo-brick-link';
30592         }
30593         
30594         if(this.bgimage.length){
30595             cls += ' roo-brick-image';
30596         }
30597         
30598         if(!this.html.length && !this.bgimage.length){
30599             cls += ' roo-brick-center-title';
30600         }
30601         
30602         if(!this.html.length && this.bgimage.length){
30603             cls += ' roo-brick-bottom-title';
30604         }
30605         
30606         if(this.cls){
30607             cls += ' ' + this.cls;
30608         }
30609         
30610         var cfg = {
30611             tag: (this.href.length) ? 'a' : 'div',
30612             cls: cls,
30613             cn: [
30614                 {
30615                     tag: 'div',
30616                     cls: 'roo-brick-paragraph',
30617                     cn: []
30618                 }
30619             ]
30620         };
30621         
30622         if(this.href.length){
30623             cfg.href = this.href;
30624         }
30625         
30626         var cn = cfg.cn[0].cn;
30627         
30628         if(this.title.length){
30629             cn.push({
30630                 tag: 'h4',
30631                 cls: 'roo-brick-title',
30632                 html: this.title
30633             });
30634         }
30635         
30636         if(this.html.length){
30637             cn.push({
30638                 tag: 'p',
30639                 cls: 'roo-brick-text',
30640                 html: this.html
30641             });
30642         } else {
30643             cn.cls += ' hide';
30644         }
30645         
30646         if(this.bgimage.length){
30647             cfg.cn.push({
30648                 tag: 'img',
30649                 cls: 'roo-brick-image-view',
30650                 src: this.bgimage
30651             });
30652         }
30653         
30654         return cfg;
30655     },
30656     
30657     initEvents: function() 
30658     {
30659         if(this.title.length || this.html.length){
30660             this.el.on('mouseenter'  ,this.enter, this);
30661             this.el.on('mouseleave', this.leave, this);
30662         }
30663         
30664         
30665         Roo.EventManager.onWindowResize(this.resize, this); 
30666         
30667         this.resize();
30668     },
30669     
30670     resize : function()
30671     {
30672         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
30673         
30674         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
30675 //        paragraph.setHeight(paragraph.getWidth());
30676         
30677         if(this.bgimage.length){
30678             var image = this.el.select('.roo-brick-image-view', true).first();
30679             image.setWidth(paragraph.getWidth());
30680             image.setHeight(paragraph.getWidth());
30681         }
30682         
30683     },
30684     
30685     enter: function(e, el)
30686     {
30687         e.preventDefault();
30688         
30689         if(this.bgimage.length){
30690             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
30691             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
30692         }
30693     },
30694     
30695     leave: function(e, el)
30696     {
30697         e.preventDefault();
30698         
30699         if(this.bgimage.length){
30700             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
30701             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
30702         }
30703     }
30704     
30705 });
30706
30707  
30708
30709  /*
30710  * Based on:
30711  * Ext JS Library 1.1.1
30712  * Copyright(c) 2006-2007, Ext JS, LLC.
30713  *
30714  * Originally Released Under LGPL - original licence link has changed is not relivant.
30715  *
30716  * Fork - LGPL
30717  * <script type="text/javascript">
30718  */
30719
30720
30721 /**
30722  * @class Roo.bootstrap.SplitBar
30723  * @extends Roo.util.Observable
30724  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
30725  * <br><br>
30726  * Usage:
30727  * <pre><code>
30728 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
30729                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
30730 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
30731 split.minSize = 100;
30732 split.maxSize = 600;
30733 split.animate = true;
30734 split.on('moved', splitterMoved);
30735 </code></pre>
30736  * @constructor
30737  * Create a new SplitBar
30738  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
30739  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
30740  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30741  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
30742                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
30743                         position of the SplitBar).
30744  */
30745 Roo.bootstrap.SplitBar = function(cfg){
30746     
30747     /** @private */
30748     
30749     //{
30750     //  dragElement : elm
30751     //  resizingElement: el,
30752         // optional..
30753     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
30754     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
30755         // existingProxy ???
30756     //}
30757     
30758     this.el = Roo.get(cfg.dragElement, true);
30759     this.el.dom.unselectable = "on";
30760     /** @private */
30761     this.resizingEl = Roo.get(cfg.resizingElement, true);
30762
30763     /**
30764      * @private
30765      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
30766      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
30767      * @type Number
30768      */
30769     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
30770     
30771     /**
30772      * The minimum size of the resizing element. (Defaults to 0)
30773      * @type Number
30774      */
30775     this.minSize = 0;
30776     
30777     /**
30778      * The maximum size of the resizing element. (Defaults to 2000)
30779      * @type Number
30780      */
30781     this.maxSize = 2000;
30782     
30783     /**
30784      * Whether to animate the transition to the new size
30785      * @type Boolean
30786      */
30787     this.animate = false;
30788     
30789     /**
30790      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
30791      * @type Boolean
30792      */
30793     this.useShim = false;
30794     
30795     /** @private */
30796     this.shim = null;
30797     
30798     if(!cfg.existingProxy){
30799         /** @private */
30800         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
30801     }else{
30802         this.proxy = Roo.get(cfg.existingProxy).dom;
30803     }
30804     /** @private */
30805     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
30806     
30807     /** @private */
30808     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
30809     
30810     /** @private */
30811     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
30812     
30813     /** @private */
30814     this.dragSpecs = {};
30815     
30816     /**
30817      * @private The adapter to use to positon and resize elements
30818      */
30819     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
30820     this.adapter.init(this);
30821     
30822     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30823         /** @private */
30824         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
30825         this.el.addClass("roo-splitbar-h");
30826     }else{
30827         /** @private */
30828         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
30829         this.el.addClass("roo-splitbar-v");
30830     }
30831     
30832     this.addEvents({
30833         /**
30834          * @event resize
30835          * Fires when the splitter is moved (alias for {@link #event-moved})
30836          * @param {Roo.bootstrap.SplitBar} this
30837          * @param {Number} newSize the new width or height
30838          */
30839         "resize" : true,
30840         /**
30841          * @event moved
30842          * Fires when the splitter is moved
30843          * @param {Roo.bootstrap.SplitBar} this
30844          * @param {Number} newSize the new width or height
30845          */
30846         "moved" : true,
30847         /**
30848          * @event beforeresize
30849          * Fires before the splitter is dragged
30850          * @param {Roo.bootstrap.SplitBar} this
30851          */
30852         "beforeresize" : true,
30853
30854         "beforeapply" : true
30855     });
30856
30857     Roo.util.Observable.call(this);
30858 };
30859
30860 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
30861     onStartProxyDrag : function(x, y){
30862         this.fireEvent("beforeresize", this);
30863         if(!this.overlay){
30864             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
30865             o.unselectable();
30866             o.enableDisplayMode("block");
30867             // all splitbars share the same overlay
30868             Roo.bootstrap.SplitBar.prototype.overlay = o;
30869         }
30870         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
30871         this.overlay.show();
30872         Roo.get(this.proxy).setDisplayed("block");
30873         var size = this.adapter.getElementSize(this);
30874         this.activeMinSize = this.getMinimumSize();;
30875         this.activeMaxSize = this.getMaximumSize();;
30876         var c1 = size - this.activeMinSize;
30877         var c2 = Math.max(this.activeMaxSize - size, 0);
30878         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30879             this.dd.resetConstraints();
30880             this.dd.setXConstraint(
30881                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
30882                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
30883             );
30884             this.dd.setYConstraint(0, 0);
30885         }else{
30886             this.dd.resetConstraints();
30887             this.dd.setXConstraint(0, 0);
30888             this.dd.setYConstraint(
30889                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
30890                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
30891             );
30892          }
30893         this.dragSpecs.startSize = size;
30894         this.dragSpecs.startPoint = [x, y];
30895         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
30896     },
30897     
30898     /** 
30899      * @private Called after the drag operation by the DDProxy
30900      */
30901     onEndProxyDrag : function(e){
30902         Roo.get(this.proxy).setDisplayed(false);
30903         var endPoint = Roo.lib.Event.getXY(e);
30904         if(this.overlay){
30905             this.overlay.hide();
30906         }
30907         var newSize;
30908         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
30909             newSize = this.dragSpecs.startSize + 
30910                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
30911                     endPoint[0] - this.dragSpecs.startPoint[0] :
30912                     this.dragSpecs.startPoint[0] - endPoint[0]
30913                 );
30914         }else{
30915             newSize = this.dragSpecs.startSize + 
30916                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
30917                     endPoint[1] - this.dragSpecs.startPoint[1] :
30918                     this.dragSpecs.startPoint[1] - endPoint[1]
30919                 );
30920         }
30921         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
30922         if(newSize != this.dragSpecs.startSize){
30923             if(this.fireEvent('beforeapply', this, newSize) !== false){
30924                 this.adapter.setElementSize(this, newSize);
30925                 this.fireEvent("moved", this, newSize);
30926                 this.fireEvent("resize", this, newSize);
30927             }
30928         }
30929     },
30930     
30931     /**
30932      * Get the adapter this SplitBar uses
30933      * @return The adapter object
30934      */
30935     getAdapter : function(){
30936         return this.adapter;
30937     },
30938     
30939     /**
30940      * Set the adapter this SplitBar uses
30941      * @param {Object} adapter A SplitBar adapter object
30942      */
30943     setAdapter : function(adapter){
30944         this.adapter = adapter;
30945         this.adapter.init(this);
30946     },
30947     
30948     /**
30949      * Gets the minimum size for the resizing element
30950      * @return {Number} The minimum size
30951      */
30952     getMinimumSize : function(){
30953         return this.minSize;
30954     },
30955     
30956     /**
30957      * Sets the minimum size for the resizing element
30958      * @param {Number} minSize The minimum size
30959      */
30960     setMinimumSize : function(minSize){
30961         this.minSize = minSize;
30962     },
30963     
30964     /**
30965      * Gets the maximum size for the resizing element
30966      * @return {Number} The maximum size
30967      */
30968     getMaximumSize : function(){
30969         return this.maxSize;
30970     },
30971     
30972     /**
30973      * Sets the maximum size for the resizing element
30974      * @param {Number} maxSize The maximum size
30975      */
30976     setMaximumSize : function(maxSize){
30977         this.maxSize = maxSize;
30978     },
30979     
30980     /**
30981      * Sets the initialize size for the resizing element
30982      * @param {Number} size The initial size
30983      */
30984     setCurrentSize : function(size){
30985         var oldAnimate = this.animate;
30986         this.animate = false;
30987         this.adapter.setElementSize(this, size);
30988         this.animate = oldAnimate;
30989     },
30990     
30991     /**
30992      * Destroy this splitbar. 
30993      * @param {Boolean} removeEl True to remove the element
30994      */
30995     destroy : function(removeEl){
30996         if(this.shim){
30997             this.shim.remove();
30998         }
30999         this.dd.unreg();
31000         this.proxy.parentNode.removeChild(this.proxy);
31001         if(removeEl){
31002             this.el.remove();
31003         }
31004     }
31005 });
31006
31007 /**
31008  * @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.
31009  */
31010 Roo.bootstrap.SplitBar.createProxy = function(dir){
31011     var proxy = new Roo.Element(document.createElement("div"));
31012     proxy.unselectable();
31013     var cls = 'roo-splitbar-proxy';
31014     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
31015     document.body.appendChild(proxy.dom);
31016     return proxy.dom;
31017 };
31018
31019 /** 
31020  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
31021  * Default Adapter. It assumes the splitter and resizing element are not positioned
31022  * elements and only gets/sets the width of the element. Generally used for table based layouts.
31023  */
31024 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
31025 };
31026
31027 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
31028     // do nothing for now
31029     init : function(s){
31030     
31031     },
31032     /**
31033      * Called before drag operations to get the current size of the resizing element. 
31034      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31035      */
31036      getElementSize : function(s){
31037         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31038             return s.resizingEl.getWidth();
31039         }else{
31040             return s.resizingEl.getHeight();
31041         }
31042     },
31043     
31044     /**
31045      * Called after drag operations to set the size of the resizing element.
31046      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
31047      * @param {Number} newSize The new size to set
31048      * @param {Function} onComplete A function to be invoked when resizing is complete
31049      */
31050     setElementSize : function(s, newSize, onComplete){
31051         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
31052             if(!s.animate){
31053                 s.resizingEl.setWidth(newSize);
31054                 if(onComplete){
31055                     onComplete(s, newSize);
31056                 }
31057             }else{
31058                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
31059             }
31060         }else{
31061             
31062             if(!s.animate){
31063                 s.resizingEl.setHeight(newSize);
31064                 if(onComplete){
31065                     onComplete(s, newSize);
31066                 }
31067             }else{
31068                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
31069             }
31070         }
31071     }
31072 };
31073
31074 /** 
31075  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
31076  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
31077  * Adapter that  moves the splitter element to align with the resized sizing element. 
31078  * Used with an absolute positioned SplitBar.
31079  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
31080  * document.body, make sure you assign an id to the body element.
31081  */
31082 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
31083     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
31084     this.container = Roo.get(container);
31085 };
31086
31087 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
31088     init : function(s){
31089         this.basic.init(s);
31090     },
31091     
31092     getElementSize : function(s){
31093         return this.basic.getElementSize(s);
31094     },
31095     
31096     setElementSize : function(s, newSize, onComplete){
31097         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
31098     },
31099     
31100     moveSplitter : function(s){
31101         var yes = Roo.bootstrap.SplitBar;
31102         switch(s.placement){
31103             case yes.LEFT:
31104                 s.el.setX(s.resizingEl.getRight());
31105                 break;
31106             case yes.RIGHT:
31107                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
31108                 break;
31109             case yes.TOP:
31110                 s.el.setY(s.resizingEl.getBottom());
31111                 break;
31112             case yes.BOTTOM:
31113                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
31114                 break;
31115         }
31116     }
31117 };
31118
31119 /**
31120  * Orientation constant - Create a vertical SplitBar
31121  * @static
31122  * @type Number
31123  */
31124 Roo.bootstrap.SplitBar.VERTICAL = 1;
31125
31126 /**
31127  * Orientation constant - Create a horizontal SplitBar
31128  * @static
31129  * @type Number
31130  */
31131 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
31132
31133 /**
31134  * Placement constant - The resizing element is to the left of the splitter element
31135  * @static
31136  * @type Number
31137  */
31138 Roo.bootstrap.SplitBar.LEFT = 1;
31139
31140 /**
31141  * Placement constant - The resizing element is to the right of the splitter element
31142  * @static
31143  * @type Number
31144  */
31145 Roo.bootstrap.SplitBar.RIGHT = 2;
31146
31147 /**
31148  * Placement constant - The resizing element is positioned above the splitter element
31149  * @static
31150  * @type Number
31151  */
31152 Roo.bootstrap.SplitBar.TOP = 3;
31153
31154 /**
31155  * Placement constant - The resizing element is positioned under splitter element
31156  * @static
31157  * @type Number
31158  */
31159 Roo.bootstrap.SplitBar.BOTTOM = 4;
31160 Roo.namespace("Roo.bootstrap.layout");/*
31161  * Based on:
31162  * Ext JS Library 1.1.1
31163  * Copyright(c) 2006-2007, Ext JS, LLC.
31164  *
31165  * Originally Released Under LGPL - original licence link has changed is not relivant.
31166  *
31167  * Fork - LGPL
31168  * <script type="text/javascript">
31169  */
31170  
31171 /**
31172  * @class Roo.bootstrap.layout.Manager
31173  * @extends Roo.bootstrap.Component
31174  * Base class for layout managers.
31175  */
31176 Roo.bootstrap.layout.Manager = function(config)
31177 {
31178     Roo.bootstrap.layout.Manager.superclass.constructor.call(this);
31179     
31180     
31181      
31182     
31183     
31184     /** false to disable window resize monitoring @type Boolean */
31185     this.monitorWindowResize = true;
31186     this.regions = {};
31187     this.addEvents({
31188         /**
31189          * @event layout
31190          * Fires when a layout is performed. 
31191          * @param {Roo.LayoutManager} this
31192          */
31193         "layout" : true,
31194         /**
31195          * @event regionresized
31196          * Fires when the user resizes a region. 
31197          * @param {Roo.LayoutRegion} region The resized region
31198          * @param {Number} newSize The new size (width for east/west, height for north/south)
31199          */
31200         "regionresized" : true,
31201         /**
31202          * @event regioncollapsed
31203          * Fires when a region is collapsed. 
31204          * @param {Roo.LayoutRegion} region The collapsed region
31205          */
31206         "regioncollapsed" : true,
31207         /**
31208          * @event regionexpanded
31209          * Fires when a region is expanded.  
31210          * @param {Roo.LayoutRegion} region The expanded region
31211          */
31212         "regionexpanded" : true
31213     });
31214     this.updating = false;
31215     
31216     if (config.el) {
31217         this.el = Roo.get(config.el);
31218         this.initEvents();
31219     }
31220     
31221 };
31222
31223 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
31224     
31225     
31226     regions : null,
31227     
31228     monitorWindowResize : true,
31229     
31230     
31231     updating : false,
31232     
31233     
31234     onRender : function(ct, position)
31235     {
31236         if(!this.el){
31237             this.el = Roo.get(ct);
31238             this.initEvents();
31239         }
31240     },
31241     
31242     
31243     initEvents: function()
31244     {
31245         
31246         
31247         // ie scrollbar fix
31248         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
31249             document.body.scroll = "no";
31250         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
31251             this.el.position('relative');
31252         }
31253         this.id = this.el.id;
31254         this.el.addClass("roo-layout-container");
31255         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
31256         if(this.el.dom != document.body ) {
31257             this.el.on('resize', this.layout,this);
31258             this.el.on('show', this.layout,this);
31259         }
31260
31261     },
31262     
31263     /**
31264      * Returns true if this layout is currently being updated
31265      * @return {Boolean}
31266      */
31267     isUpdating : function(){
31268         return this.updating; 
31269     },
31270     
31271     /**
31272      * Suspend the LayoutManager from doing auto-layouts while
31273      * making multiple add or remove calls
31274      */
31275     beginUpdate : function(){
31276         this.updating = true;    
31277     },
31278     
31279     /**
31280      * Restore auto-layouts and optionally disable the manager from performing a layout
31281      * @param {Boolean} noLayout true to disable a layout update 
31282      */
31283     endUpdate : function(noLayout){
31284         this.updating = false;
31285         if(!noLayout){
31286             this.layout();
31287         }    
31288     },
31289     
31290     layout: function(){
31291         // abstract...
31292     },
31293     
31294     onRegionResized : function(region, newSize){
31295         this.fireEvent("regionresized", region, newSize);
31296         this.layout();
31297     },
31298     
31299     onRegionCollapsed : function(region){
31300         this.fireEvent("regioncollapsed", region);
31301     },
31302     
31303     onRegionExpanded : function(region){
31304         this.fireEvent("regionexpanded", region);
31305     },
31306         
31307     /**
31308      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
31309      * performs box-model adjustments.
31310      * @return {Object} The size as an object {width: (the width), height: (the height)}
31311      */
31312     getViewSize : function()
31313     {
31314         var size;
31315         if(this.el.dom != document.body){
31316             size = this.el.getSize();
31317         }else{
31318             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
31319         }
31320         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
31321         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
31322         return size;
31323     },
31324     
31325     /**
31326      * Returns the Element this layout is bound to.
31327      * @return {Roo.Element}
31328      */
31329     getEl : function(){
31330         return this.el;
31331     },
31332     
31333     /**
31334      * Returns the specified region.
31335      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
31336      * @return {Roo.LayoutRegion}
31337      */
31338     getRegion : function(target){
31339         return this.regions[target.toLowerCase()];
31340     },
31341     
31342     onWindowResize : function(){
31343         if(this.monitorWindowResize){
31344             this.layout();
31345         }
31346     }
31347 });/*
31348  * Based on:
31349  * Ext JS Library 1.1.1
31350  * Copyright(c) 2006-2007, Ext JS, LLC.
31351  *
31352  * Originally Released Under LGPL - original licence link has changed is not relivant.
31353  *
31354  * Fork - LGPL
31355  * <script type="text/javascript">
31356  */
31357 /**
31358  * @class Roo.bootstrap.layout.Border
31359  * @extends Roo.bootstrap.layout.Manager
31360  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
31361  * please see: examples/bootstrap/nested.html<br><br>
31362  
31363 <b>The container the layout is rendered into can be either the body element or any other element.
31364 If it is not the body element, the container needs to either be an absolute positioned element,
31365 or you will need to add "position:relative" to the css of the container.  You will also need to specify
31366 the container size if it is not the body element.</b>
31367
31368 * @constructor
31369 * Create a new Border
31370 * @param {Object} config Configuration options
31371  */
31372 Roo.bootstrap.layout.Border = function(config){
31373     config = config || {};
31374     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
31375     
31376     
31377     
31378     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
31379         if(config[region]){
31380             config[region].region = region;
31381             this.addRegion(config[region]);
31382         }
31383     },this);
31384     
31385 };
31386
31387 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
31388
31389 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
31390     /**
31391      * Creates and adds a new region if it doesn't already exist.
31392      * @param {String} target The target region key (north, south, east, west or center).
31393      * @param {Object} config The regions config object
31394      * @return {BorderLayoutRegion} The new region
31395      */
31396     addRegion : function(config)
31397     {
31398         if(!this.regions[config.region]){
31399             var r = this.factory(config);
31400             this.bindRegion(r);
31401         }
31402         return this.regions[config.region];
31403     },
31404
31405     // private (kinda)
31406     bindRegion : function(r){
31407         this.regions[r.config.region] = r;
31408         
31409         r.on("visibilitychange",    this.layout, this);
31410         r.on("paneladded",          this.layout, this);
31411         r.on("panelremoved",        this.layout, this);
31412         r.on("invalidated",         this.layout, this);
31413         r.on("resized",             this.onRegionResized, this);
31414         r.on("collapsed",           this.onRegionCollapsed, this);
31415         r.on("expanded",            this.onRegionExpanded, this);
31416     },
31417
31418     /**
31419      * Performs a layout update.
31420      */
31421     layout : function()
31422     {
31423         if(this.updating) {
31424             return;
31425         }
31426         var size = this.getViewSize();
31427         var w = size.width;
31428         var h = size.height;
31429         var centerW = w;
31430         var centerH = h;
31431         var centerY = 0;
31432         var centerX = 0;
31433         //var x = 0, y = 0;
31434
31435         var rs = this.regions;
31436         var north = rs["north"];
31437         var south = rs["south"]; 
31438         var west = rs["west"];
31439         var east = rs["east"];
31440         var center = rs["center"];
31441         //if(this.hideOnLayout){ // not supported anymore
31442             //c.el.setStyle("display", "none");
31443         //}
31444         if(north && north.isVisible()){
31445             var b = north.getBox();
31446             var m = north.getMargins();
31447             b.width = w - (m.left+m.right);
31448             b.x = m.left;
31449             b.y = m.top;
31450             centerY = b.height + b.y + m.bottom;
31451             centerH -= centerY;
31452             north.updateBox(this.safeBox(b));
31453         }
31454         if(south && south.isVisible()){
31455             var b = south.getBox();
31456             var m = south.getMargins();
31457             b.width = w - (m.left+m.right);
31458             b.x = m.left;
31459             var totalHeight = (b.height + m.top + m.bottom);
31460             b.y = h - totalHeight + m.top;
31461             centerH -= totalHeight;
31462             south.updateBox(this.safeBox(b));
31463         }
31464         if(west && west.isVisible()){
31465             var b = west.getBox();
31466             var m = west.getMargins();
31467             b.height = centerH - (m.top+m.bottom);
31468             b.x = m.left;
31469             b.y = centerY + m.top;
31470             var totalWidth = (b.width + m.left + m.right);
31471             centerX += totalWidth;
31472             centerW -= totalWidth;
31473             west.updateBox(this.safeBox(b));
31474         }
31475         if(east && east.isVisible()){
31476             var b = east.getBox();
31477             var m = east.getMargins();
31478             b.height = centerH - (m.top+m.bottom);
31479             var totalWidth = (b.width + m.left + m.right);
31480             b.x = w - totalWidth + m.left;
31481             b.y = centerY + m.top;
31482             centerW -= totalWidth;
31483             east.updateBox(this.safeBox(b));
31484         }
31485         if(center){
31486             var m = center.getMargins();
31487             var centerBox = {
31488                 x: centerX + m.left,
31489                 y: centerY + m.top,
31490                 width: centerW - (m.left+m.right),
31491                 height: centerH - (m.top+m.bottom)
31492             };
31493             //if(this.hideOnLayout){
31494                 //center.el.setStyle("display", "block");
31495             //}
31496             center.updateBox(this.safeBox(centerBox));
31497         }
31498         this.el.repaint();
31499         this.fireEvent("layout", this);
31500     },
31501
31502     // private
31503     safeBox : function(box){
31504         box.width = Math.max(0, box.width);
31505         box.height = Math.max(0, box.height);
31506         return box;
31507     },
31508
31509     /**
31510      * Adds a ContentPanel (or subclass) to this layout.
31511      * @param {String} target The target region key (north, south, east, west or center).
31512      * @param {Roo.ContentPanel} panel The panel to add
31513      * @return {Roo.ContentPanel} The added panel
31514      */
31515     add : function(target, panel){
31516          
31517         target = target.toLowerCase();
31518         return this.regions[target].add(panel);
31519     },
31520
31521     /**
31522      * Remove a ContentPanel (or subclass) to this layout.
31523      * @param {String} target The target region key (north, south, east, west or center).
31524      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
31525      * @return {Roo.ContentPanel} The removed panel
31526      */
31527     remove : function(target, panel){
31528         target = target.toLowerCase();
31529         return this.regions[target].remove(panel);
31530     },
31531
31532     /**
31533      * Searches all regions for a panel with the specified id
31534      * @param {String} panelId
31535      * @return {Roo.ContentPanel} The panel or null if it wasn't found
31536      */
31537     findPanel : function(panelId){
31538         var rs = this.regions;
31539         for(var target in rs){
31540             if(typeof rs[target] != "function"){
31541                 var p = rs[target].getPanel(panelId);
31542                 if(p){
31543                     return p;
31544                 }
31545             }
31546         }
31547         return null;
31548     },
31549
31550     /**
31551      * Searches all regions for a panel with the specified id and activates (shows) it.
31552      * @param {String/ContentPanel} panelId The panels id or the panel itself
31553      * @return {Roo.ContentPanel} The shown panel or null
31554      */
31555     showPanel : function(panelId) {
31556       var rs = this.regions;
31557       for(var target in rs){
31558          var r = rs[target];
31559          if(typeof r != "function"){
31560             if(r.hasPanel(panelId)){
31561                return r.showPanel(panelId);
31562             }
31563          }
31564       }
31565       return null;
31566    },
31567
31568    /**
31569      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
31570      * @param {Roo.state.Provider} provider (optional) An alternate state provider
31571      */
31572    /*
31573     restoreState : function(provider){
31574         if(!provider){
31575             provider = Roo.state.Manager;
31576         }
31577         var sm = new Roo.LayoutStateManager();
31578         sm.init(this, provider);
31579     },
31580 */
31581  
31582  
31583     /**
31584      * Adds a xtype elements to the layout.
31585      * <pre><code>
31586
31587 layout.addxtype({
31588        xtype : 'ContentPanel',
31589        region: 'west',
31590        items: [ .... ]
31591    }
31592 );
31593
31594 layout.addxtype({
31595         xtype : 'NestedLayoutPanel',
31596         region: 'west',
31597         layout: {
31598            center: { },
31599            west: { }   
31600         },
31601         items : [ ... list of content panels or nested layout panels.. ]
31602    }
31603 );
31604 </code></pre>
31605      * @param {Object} cfg Xtype definition of item to add.
31606      */
31607     addxtype : function(cfg)
31608     {
31609         // basically accepts a pannel...
31610         // can accept a layout region..!?!?
31611         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
31612         
31613         
31614         // theory?  children can only be panels??
31615         
31616         //if (!cfg.xtype.match(/Panel$/)) {
31617         //    return false;
31618         //}
31619         var ret = false;
31620         
31621         if (typeof(cfg.region) == 'undefined') {
31622             Roo.log("Failed to add Panel, region was not set");
31623             Roo.log(cfg);
31624             return false;
31625         }
31626         var region = cfg.region;
31627         delete cfg.region;
31628         
31629           
31630         var xitems = [];
31631         if (cfg.items) {
31632             xitems = cfg.items;
31633             delete cfg.items;
31634         }
31635         var nb = false;
31636         
31637         switch(cfg.xtype) 
31638         {
31639             case 'Content':  // ContentPanel (el, cfg)
31640             case 'Scroll':  // ContentPanel (el, cfg)
31641             case 'View': 
31642                 cfg.autoCreate = true;
31643                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31644                 //} else {
31645                 //    var el = this.el.createChild();
31646                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
31647                 //}
31648                 
31649                 this.add(region, ret);
31650                 break;
31651             
31652             /*
31653             case 'TreePanel': // our new panel!
31654                 cfg.el = this.el.createChild();
31655                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31656                 this.add(region, ret);
31657                 break;
31658             */
31659             
31660             case 'Nest': 
31661                 // create a new Layout (which is  a Border Layout...
31662                 
31663                 var clayout = cfg.layout;
31664                 clayout.el  = this.el.createChild();
31665                 clayout.items   = clayout.items  || [];
31666                 
31667                 delete cfg.layout;
31668                 
31669                 // replace this exitems with the clayout ones..
31670                 xitems = clayout.items;
31671                  
31672                 // force background off if it's in center...
31673                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
31674                     cfg.background = false;
31675                 }
31676                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
31677                 
31678                 
31679                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31680                 //console.log('adding nested layout panel '  + cfg.toSource());
31681                 this.add(region, ret);
31682                 nb = {}; /// find first...
31683                 break;
31684             
31685             case 'Grid':
31686                 
31687                 // needs grid and region
31688                 
31689                 //var el = this.getRegion(region).el.createChild();
31690                 /*
31691                  *var el = this.el.createChild();
31692                 // create the grid first...
31693                 cfg.grid.container = el;
31694                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
31695                 */
31696                 
31697                 if (region == 'center' && this.active ) {
31698                     cfg.background = false;
31699                 }
31700                 
31701                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
31702                 
31703                 this.add(region, ret);
31704                 /*
31705                 if (cfg.background) {
31706                     // render grid on panel activation (if panel background)
31707                     ret.on('activate', function(gp) {
31708                         if (!gp.grid.rendered) {
31709                     //        gp.grid.render(el);
31710                         }
31711                     });
31712                 } else {
31713                   //  cfg.grid.render(el);
31714                 }
31715                 */
31716                 break;
31717            
31718            
31719             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
31720                 // it was the old xcomponent building that caused this before.
31721                 // espeically if border is the top element in the tree.
31722                 ret = this;
31723                 break; 
31724                 
31725                     
31726                 
31727                 
31728                 
31729             default:
31730                 /*
31731                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
31732                     
31733                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
31734                     this.add(region, ret);
31735                 } else {
31736                 */
31737                     Roo.log(cfg);
31738                     throw "Can not add '" + cfg.xtype + "' to Border";
31739                     return null;
31740              
31741                                 
31742              
31743         }
31744         this.beginUpdate();
31745         // add children..
31746         var region = '';
31747         var abn = {};
31748         Roo.each(xitems, function(i)  {
31749             region = nb && i.region ? i.region : false;
31750             
31751             var add = ret.addxtype(i);
31752            
31753             if (region) {
31754                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
31755                 if (!i.background) {
31756                     abn[region] = nb[region] ;
31757                 }
31758             }
31759             
31760         });
31761         this.endUpdate();
31762
31763         // make the last non-background panel active..
31764         //if (nb) { Roo.log(abn); }
31765         if (nb) {
31766             
31767             for(var r in abn) {
31768                 region = this.getRegion(r);
31769                 if (region) {
31770                     // tried using nb[r], but it does not work..
31771                      
31772                     region.showPanel(abn[r]);
31773                    
31774                 }
31775             }
31776         }
31777         return ret;
31778         
31779     },
31780     
31781     
31782 // private
31783     factory : function(cfg)
31784     {
31785         
31786         var validRegions = Roo.bootstrap.layout.Border.regions;
31787
31788         var target = cfg.region;
31789         cfg.mgr = this;
31790         
31791         var r = Roo.bootstrap.layout;
31792         Roo.log(target);
31793         switch(target){
31794             case "north":
31795                 return new r.North(cfg);
31796             case "south":
31797                 return new r.South(cfg);
31798             case "east":
31799                 return new r.East(cfg);
31800             case "west":
31801                 return new r.West(cfg);
31802             case "center":
31803                 return new r.Center(cfg);
31804         }
31805         throw 'Layout region "'+target+'" not supported.';
31806     }
31807     
31808     
31809 });
31810  /*
31811  * Based on:
31812  * Ext JS Library 1.1.1
31813  * Copyright(c) 2006-2007, Ext JS, LLC.
31814  *
31815  * Originally Released Under LGPL - original licence link has changed is not relivant.
31816  *
31817  * Fork - LGPL
31818  * <script type="text/javascript">
31819  */
31820  
31821 /**
31822  * @class Roo.bootstrap.layout.Basic
31823  * @extends Roo.util.Observable
31824  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
31825  * and does not have a titlebar, tabs or any other features. All it does is size and position 
31826  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
31827  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
31828  * @cfg {string}   region  the region that it inhabits..
31829  * @cfg {bool}   skipConfig skip config?
31830  * 
31831
31832  */
31833 Roo.bootstrap.layout.Basic = function(config){
31834     
31835     this.mgr = config.mgr;
31836     
31837     this.position = config.region;
31838     
31839     var skipConfig = config.skipConfig;
31840     
31841     this.events = {
31842         /**
31843          * @scope Roo.BasicLayoutRegion
31844          */
31845         
31846         /**
31847          * @event beforeremove
31848          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
31849          * @param {Roo.LayoutRegion} this
31850          * @param {Roo.ContentPanel} panel The panel
31851          * @param {Object} e The cancel event object
31852          */
31853         "beforeremove" : true,
31854         /**
31855          * @event invalidated
31856          * Fires when the layout for this region is changed.
31857          * @param {Roo.LayoutRegion} this
31858          */
31859         "invalidated" : true,
31860         /**
31861          * @event visibilitychange
31862          * Fires when this region is shown or hidden 
31863          * @param {Roo.LayoutRegion} this
31864          * @param {Boolean} visibility true or false
31865          */
31866         "visibilitychange" : true,
31867         /**
31868          * @event paneladded
31869          * Fires when a panel is added. 
31870          * @param {Roo.LayoutRegion} this
31871          * @param {Roo.ContentPanel} panel The panel
31872          */
31873         "paneladded" : true,
31874         /**
31875          * @event panelremoved
31876          * Fires when a panel is removed. 
31877          * @param {Roo.LayoutRegion} this
31878          * @param {Roo.ContentPanel} panel The panel
31879          */
31880         "panelremoved" : true,
31881         /**
31882          * @event beforecollapse
31883          * Fires when this region before collapse.
31884          * @param {Roo.LayoutRegion} this
31885          */
31886         "beforecollapse" : true,
31887         /**
31888          * @event collapsed
31889          * Fires when this region is collapsed.
31890          * @param {Roo.LayoutRegion} this
31891          */
31892         "collapsed" : true,
31893         /**
31894          * @event expanded
31895          * Fires when this region is expanded.
31896          * @param {Roo.LayoutRegion} this
31897          */
31898         "expanded" : true,
31899         /**
31900          * @event slideshow
31901          * Fires when this region is slid into view.
31902          * @param {Roo.LayoutRegion} this
31903          */
31904         "slideshow" : true,
31905         /**
31906          * @event slidehide
31907          * Fires when this region slides out of view. 
31908          * @param {Roo.LayoutRegion} this
31909          */
31910         "slidehide" : true,
31911         /**
31912          * @event panelactivated
31913          * Fires when a panel is activated. 
31914          * @param {Roo.LayoutRegion} this
31915          * @param {Roo.ContentPanel} panel The activated panel
31916          */
31917         "panelactivated" : true,
31918         /**
31919          * @event resized
31920          * Fires when the user resizes this region. 
31921          * @param {Roo.LayoutRegion} this
31922          * @param {Number} newSize The new size (width for east/west, height for north/south)
31923          */
31924         "resized" : true
31925     };
31926     /** A collection of panels in this region. @type Roo.util.MixedCollection */
31927     this.panels = new Roo.util.MixedCollection();
31928     this.panels.getKey = this.getPanelId.createDelegate(this);
31929     this.box = null;
31930     this.activePanel = null;
31931     // ensure listeners are added...
31932     
31933     if (config.listeners || config.events) {
31934         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
31935             listeners : config.listeners || {},
31936             events : config.events || {}
31937         });
31938     }
31939     
31940     if(skipConfig !== true){
31941         this.applyConfig(config);
31942     }
31943 };
31944
31945 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
31946 {
31947     getPanelId : function(p){
31948         return p.getId();
31949     },
31950     
31951     applyConfig : function(config){
31952         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
31953         this.config = config;
31954         
31955     },
31956     
31957     /**
31958      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
31959      * the width, for horizontal (north, south) the height.
31960      * @param {Number} newSize The new width or height
31961      */
31962     resizeTo : function(newSize){
31963         var el = this.el ? this.el :
31964                  (this.activePanel ? this.activePanel.getEl() : null);
31965         if(el){
31966             switch(this.position){
31967                 case "east":
31968                 case "west":
31969                     el.setWidth(newSize);
31970                     this.fireEvent("resized", this, newSize);
31971                 break;
31972                 case "north":
31973                 case "south":
31974                     el.setHeight(newSize);
31975                     this.fireEvent("resized", this, newSize);
31976                 break;                
31977             }
31978         }
31979     },
31980     
31981     getBox : function(){
31982         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
31983     },
31984     
31985     getMargins : function(){
31986         return this.margins;
31987     },
31988     
31989     updateBox : function(box){
31990         this.box = box;
31991         var el = this.activePanel.getEl();
31992         el.dom.style.left = box.x + "px";
31993         el.dom.style.top = box.y + "px";
31994         this.activePanel.setSize(box.width, box.height);
31995     },
31996     
31997     /**
31998      * Returns the container element for this region.
31999      * @return {Roo.Element}
32000      */
32001     getEl : function(){
32002         return this.activePanel;
32003     },
32004     
32005     /**
32006      * Returns true if this region is currently visible.
32007      * @return {Boolean}
32008      */
32009     isVisible : function(){
32010         return this.activePanel ? true : false;
32011     },
32012     
32013     setActivePanel : function(panel){
32014         panel = this.getPanel(panel);
32015         if(this.activePanel && this.activePanel != panel){
32016             this.activePanel.setActiveState(false);
32017             this.activePanel.getEl().setLeftTop(-10000,-10000);
32018         }
32019         this.activePanel = panel;
32020         panel.setActiveState(true);
32021         if(this.box){
32022             panel.setSize(this.box.width, this.box.height);
32023         }
32024         this.fireEvent("panelactivated", this, panel);
32025         this.fireEvent("invalidated");
32026     },
32027     
32028     /**
32029      * Show the specified panel.
32030      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
32031      * @return {Roo.ContentPanel} The shown panel or null
32032      */
32033     showPanel : function(panel){
32034         panel = this.getPanel(panel);
32035         if(panel){
32036             this.setActivePanel(panel);
32037         }
32038         return panel;
32039     },
32040     
32041     /**
32042      * Get the active panel for this region.
32043      * @return {Roo.ContentPanel} The active panel or null
32044      */
32045     getActivePanel : function(){
32046         return this.activePanel;
32047     },
32048     
32049     /**
32050      * Add the passed ContentPanel(s)
32051      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32052      * @return {Roo.ContentPanel} The panel added (if only one was added)
32053      */
32054     add : function(panel){
32055         if(arguments.length > 1){
32056             for(var i = 0, len = arguments.length; i < len; i++) {
32057                 this.add(arguments[i]);
32058             }
32059             return null;
32060         }
32061         if(this.hasPanel(panel)){
32062             this.showPanel(panel);
32063             return panel;
32064         }
32065         var el = panel.getEl();
32066         if(el.dom.parentNode != this.mgr.el.dom){
32067             this.mgr.el.dom.appendChild(el.dom);
32068         }
32069         if(panel.setRegion){
32070             panel.setRegion(this);
32071         }
32072         this.panels.add(panel);
32073         el.setStyle("position", "absolute");
32074         if(!panel.background){
32075             this.setActivePanel(panel);
32076             if(this.config.initialSize && this.panels.getCount()==1){
32077                 this.resizeTo(this.config.initialSize);
32078             }
32079         }
32080         this.fireEvent("paneladded", this, panel);
32081         return panel;
32082     },
32083     
32084     /**
32085      * Returns true if the panel is in this region.
32086      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32087      * @return {Boolean}
32088      */
32089     hasPanel : function(panel){
32090         if(typeof panel == "object"){ // must be panel obj
32091             panel = panel.getId();
32092         }
32093         return this.getPanel(panel) ? true : false;
32094     },
32095     
32096     /**
32097      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32098      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32099      * @param {Boolean} preservePanel Overrides the config preservePanel option
32100      * @return {Roo.ContentPanel} The panel that was removed
32101      */
32102     remove : function(panel, preservePanel){
32103         panel = this.getPanel(panel);
32104         if(!panel){
32105             return null;
32106         }
32107         var e = {};
32108         this.fireEvent("beforeremove", this, panel, e);
32109         if(e.cancel === true){
32110             return null;
32111         }
32112         var panelId = panel.getId();
32113         this.panels.removeKey(panelId);
32114         return panel;
32115     },
32116     
32117     /**
32118      * Returns the panel specified or null if it's not in this region.
32119      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
32120      * @return {Roo.ContentPanel}
32121      */
32122     getPanel : function(id){
32123         if(typeof id == "object"){ // must be panel obj
32124             return id;
32125         }
32126         return this.panels.get(id);
32127     },
32128     
32129     /**
32130      * Returns this regions position (north/south/east/west/center).
32131      * @return {String} 
32132      */
32133     getPosition: function(){
32134         return this.position;    
32135     }
32136 });/*
32137  * Based on:
32138  * Ext JS Library 1.1.1
32139  * Copyright(c) 2006-2007, Ext JS, LLC.
32140  *
32141  * Originally Released Under LGPL - original licence link has changed is not relivant.
32142  *
32143  * Fork - LGPL
32144  * <script type="text/javascript">
32145  */
32146  
32147 /**
32148  * @class Roo.bootstrap.layout.Region
32149  * @extends Roo.bootstrap.layout.Basic
32150  * This class represents a region in a layout manager.
32151  
32152  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
32153  * @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})
32154  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
32155  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
32156  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
32157  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
32158  * @cfg {String}    title           The title for the region (overrides panel titles)
32159  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
32160  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
32161  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
32162  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
32163  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
32164  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
32165  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
32166  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
32167  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
32168  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
32169
32170  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
32171  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
32172  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
32173  * @cfg {Number}    width           For East/West panels
32174  * @cfg {Number}    height          For North/South panels
32175  * @cfg {Boolean}   split           To show the splitter
32176  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
32177  * 
32178  * @cfg {string}   cls             Extra CSS classes to add to region
32179  * 
32180  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
32181  * @cfg {string}   region  the region that it inhabits..
32182  *
32183
32184  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
32185  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
32186
32187  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
32188  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
32189  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
32190  */
32191 Roo.bootstrap.layout.Region = function(config)
32192 {
32193     this.applyConfig(config);
32194
32195     var mgr = config.mgr;
32196     var pos = config.region;
32197     config.skipConfig = true;
32198     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
32199     
32200     if (mgr.el) {
32201         this.onRender(mgr.el);   
32202     }
32203      
32204     this.visible = true;
32205     this.collapsed = false;
32206 };
32207
32208 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
32209
32210     position: '', // set by wrapper (eg. north/south etc..)
32211
32212     createBody : function(){
32213         /** This region's body element 
32214         * @type Roo.Element */
32215         this.bodyEl = this.el.createChild({
32216                 tag: "div",
32217                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
32218         });
32219     },
32220
32221     onRender: function(ctr, pos)
32222     {
32223         var dh = Roo.DomHelper;
32224         /** This region's container element 
32225         * @type Roo.Element */
32226         this.el = dh.append(ctr.dom, {
32227                 tag: "div",
32228                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
32229             }, true);
32230         /** This region's title element 
32231         * @type Roo.Element */
32232     
32233         this.titleEl = dh.append(this.el.dom,
32234             {
32235                     tag: "div",
32236                     unselectable: "on",
32237                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
32238                     children:[
32239                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
32240                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
32241                     ]}, true);
32242         
32243         this.titleEl.enableDisplayMode();
32244         /** This region's title text element 
32245         * @type HTMLElement */
32246         this.titleTextEl = this.titleEl.dom.firstChild;
32247         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
32248         /*
32249         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
32250         this.closeBtn.enableDisplayMode();
32251         this.closeBtn.on("click", this.closeClicked, this);
32252         this.closeBtn.hide();
32253     */
32254         this.createBody(this.config);
32255         if(this.config.hideWhenEmpty){
32256             this.hide();
32257             this.on("paneladded", this.validateVisibility, this);
32258             this.on("panelremoved", this.validateVisibility, this);
32259         }
32260         if(this.autoScroll){
32261             this.bodyEl.setStyle("overflow", "auto");
32262         }else{
32263             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
32264         }
32265         //if(c.titlebar !== false){
32266             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
32267                 this.titleEl.hide();
32268             }else{
32269                 this.titleEl.show();
32270                 if(this.config.title){
32271                     this.titleTextEl.innerHTML = this.config.title;
32272                 }
32273             }
32274         //}
32275         if(this.config.collapsed){
32276             this.collapse(true);
32277         }
32278         if(this.config.hidden){
32279             this.hide();
32280         }
32281     },
32282     
32283     applyConfig : function(c)
32284     {
32285         /*
32286          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
32287             var dh = Roo.DomHelper;
32288             if(c.titlebar !== false){
32289                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
32290                 this.collapseBtn.on("click", this.collapse, this);
32291                 this.collapseBtn.enableDisplayMode();
32292                 /*
32293                 if(c.showPin === true || this.showPin){
32294                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
32295                     this.stickBtn.enableDisplayMode();
32296                     this.stickBtn.on("click", this.expand, this);
32297                     this.stickBtn.hide();
32298                 }
32299                 
32300             }
32301             */
32302             /** This region's collapsed element
32303             * @type Roo.Element */
32304             /*
32305              *
32306             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
32307                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
32308             ]}, true);
32309             
32310             if(c.floatable !== false){
32311                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
32312                this.collapsedEl.on("click", this.collapseClick, this);
32313             }
32314
32315             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
32316                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
32317                    id: "message", unselectable: "on", style:{"float":"left"}});
32318                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
32319              }
32320             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
32321             this.expandBtn.on("click", this.expand, this);
32322             
32323         }
32324         
32325         if(this.collapseBtn){
32326             this.collapseBtn.setVisible(c.collapsible == true);
32327         }
32328         
32329         this.cmargins = c.cmargins || this.cmargins ||
32330                          (this.position == "west" || this.position == "east" ?
32331                              {top: 0, left: 2, right:2, bottom: 0} :
32332                              {top: 2, left: 0, right:0, bottom: 2});
32333         */
32334         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
32335         
32336         
32337         this.bottomTabs = c.tabPosition != "top";
32338         
32339         this.autoScroll = c.autoScroll || false;
32340         
32341         
32342        
32343         
32344         this.duration = c.duration || .30;
32345         this.slideDuration = c.slideDuration || .45;
32346         this.config = c;
32347        
32348     },
32349     /**
32350      * Returns true if this region is currently visible.
32351      * @return {Boolean}
32352      */
32353     isVisible : function(){
32354         return this.visible;
32355     },
32356
32357     /**
32358      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
32359      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
32360      */
32361     //setCollapsedTitle : function(title){
32362     //    title = title || "&#160;";
32363      //   if(this.collapsedTitleTextEl){
32364       //      this.collapsedTitleTextEl.innerHTML = title;
32365        // }
32366     //},
32367
32368     getBox : function(){
32369         var b;
32370       //  if(!this.collapsed){
32371             b = this.el.getBox(false, true);
32372        // }else{
32373           //  b = this.collapsedEl.getBox(false, true);
32374         //}
32375         return b;
32376     },
32377
32378     getMargins : function(){
32379         return this.margins;
32380         //return this.collapsed ? this.cmargins : this.margins;
32381     },
32382 /*
32383     highlight : function(){
32384         this.el.addClass("x-layout-panel-dragover");
32385     },
32386
32387     unhighlight : function(){
32388         this.el.removeClass("x-layout-panel-dragover");
32389     },
32390 */
32391     updateBox : function(box)
32392     {
32393         this.box = box;
32394         if(!this.collapsed){
32395             this.el.dom.style.left = box.x + "px";
32396             this.el.dom.style.top = box.y + "px";
32397             this.updateBody(box.width, box.height);
32398         }else{
32399             this.collapsedEl.dom.style.left = box.x + "px";
32400             this.collapsedEl.dom.style.top = box.y + "px";
32401             this.collapsedEl.setSize(box.width, box.height);
32402         }
32403         if(this.tabs){
32404             this.tabs.autoSizeTabs();
32405         }
32406     },
32407
32408     updateBody : function(w, h)
32409     {
32410         if(w !== null){
32411             this.el.setWidth(w);
32412             w -= this.el.getBorderWidth("rl");
32413             if(this.config.adjustments){
32414                 w += this.config.adjustments[0];
32415             }
32416         }
32417         if(h !== null){
32418             this.el.setHeight(h);
32419             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
32420             h -= this.el.getBorderWidth("tb");
32421             if(this.config.adjustments){
32422                 h += this.config.adjustments[1];
32423             }
32424             this.bodyEl.setHeight(h);
32425             if(this.tabs){
32426                 h = this.tabs.syncHeight(h);
32427             }
32428         }
32429         if(this.panelSize){
32430             w = w !== null ? w : this.panelSize.width;
32431             h = h !== null ? h : this.panelSize.height;
32432         }
32433         if(this.activePanel){
32434             var el = this.activePanel.getEl();
32435             w = w !== null ? w : el.getWidth();
32436             h = h !== null ? h : el.getHeight();
32437             this.panelSize = {width: w, height: h};
32438             this.activePanel.setSize(w, h);
32439         }
32440         if(Roo.isIE && this.tabs){
32441             this.tabs.el.repaint();
32442         }
32443     },
32444
32445     /**
32446      * Returns the container element for this region.
32447      * @return {Roo.Element}
32448      */
32449     getEl : function(){
32450         return this.el;
32451     },
32452
32453     /**
32454      * Hides this region.
32455      */
32456     hide : function(){
32457         //if(!this.collapsed){
32458             this.el.dom.style.left = "-2000px";
32459             this.el.hide();
32460         //}else{
32461          //   this.collapsedEl.dom.style.left = "-2000px";
32462          //   this.collapsedEl.hide();
32463        // }
32464         this.visible = false;
32465         this.fireEvent("visibilitychange", this, false);
32466     },
32467
32468     /**
32469      * Shows this region if it was previously hidden.
32470      */
32471     show : function(){
32472         //if(!this.collapsed){
32473             this.el.show();
32474         //}else{
32475         //    this.collapsedEl.show();
32476        // }
32477         this.visible = true;
32478         this.fireEvent("visibilitychange", this, true);
32479     },
32480 /*
32481     closeClicked : function(){
32482         if(this.activePanel){
32483             this.remove(this.activePanel);
32484         }
32485     },
32486
32487     collapseClick : function(e){
32488         if(this.isSlid){
32489            e.stopPropagation();
32490            this.slideIn();
32491         }else{
32492            e.stopPropagation();
32493            this.slideOut();
32494         }
32495     },
32496 */
32497     /**
32498      * Collapses this region.
32499      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
32500      */
32501     /*
32502     collapse : function(skipAnim, skipCheck = false){
32503         if(this.collapsed) {
32504             return;
32505         }
32506         
32507         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
32508             
32509             this.collapsed = true;
32510             if(this.split){
32511                 this.split.el.hide();
32512             }
32513             if(this.config.animate && skipAnim !== true){
32514                 this.fireEvent("invalidated", this);
32515                 this.animateCollapse();
32516             }else{
32517                 this.el.setLocation(-20000,-20000);
32518                 this.el.hide();
32519                 this.collapsedEl.show();
32520                 this.fireEvent("collapsed", this);
32521                 this.fireEvent("invalidated", this);
32522             }
32523         }
32524         
32525     },
32526 */
32527     animateCollapse : function(){
32528         // overridden
32529     },
32530
32531     /**
32532      * Expands this region if it was previously collapsed.
32533      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
32534      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
32535      */
32536     /*
32537     expand : function(e, skipAnim){
32538         if(e) {
32539             e.stopPropagation();
32540         }
32541         if(!this.collapsed || this.el.hasActiveFx()) {
32542             return;
32543         }
32544         if(this.isSlid){
32545             this.afterSlideIn();
32546             skipAnim = true;
32547         }
32548         this.collapsed = false;
32549         if(this.config.animate && skipAnim !== true){
32550             this.animateExpand();
32551         }else{
32552             this.el.show();
32553             if(this.split){
32554                 this.split.el.show();
32555             }
32556             this.collapsedEl.setLocation(-2000,-2000);
32557             this.collapsedEl.hide();
32558             this.fireEvent("invalidated", this);
32559             this.fireEvent("expanded", this);
32560         }
32561     },
32562 */
32563     animateExpand : function(){
32564         // overridden
32565     },
32566
32567     initTabs : function()
32568     {
32569         this.bodyEl.setStyle("overflow", "hidden");
32570         var ts = new Roo.bootstrap.panel.Tabs({
32571                 el: this.bodyEl.dom,
32572                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
32573                 disableTooltips: this.config.disableTabTips,
32574                 toolbar : this.config.toolbar
32575             });
32576         
32577         if(this.config.hideTabs){
32578             ts.stripWrap.setDisplayed(false);
32579         }
32580         this.tabs = ts;
32581         ts.resizeTabs = this.config.resizeTabs === true;
32582         ts.minTabWidth = this.config.minTabWidth || 40;
32583         ts.maxTabWidth = this.config.maxTabWidth || 250;
32584         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
32585         ts.monitorResize = false;
32586         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32587         ts.bodyEl.addClass('roo-layout-tabs-body');
32588         this.panels.each(this.initPanelAsTab, this);
32589     },
32590
32591     initPanelAsTab : function(panel){
32592         var ti = this.tabs.addTab(
32593                     panel.getEl().id,
32594                     panel.getTitle(), null,
32595                     this.config.closeOnTab && panel.isClosable()
32596             );
32597         if(panel.tabTip !== undefined){
32598             ti.setTooltip(panel.tabTip);
32599         }
32600         ti.on("activate", function(){
32601               this.setActivePanel(panel);
32602         }, this);
32603         
32604         if(this.config.closeOnTab){
32605             ti.on("beforeclose", function(t, e){
32606                 e.cancel = true;
32607                 this.remove(panel);
32608             }, this);
32609         }
32610         return ti;
32611     },
32612
32613     updatePanelTitle : function(panel, title)
32614     {
32615         if(this.activePanel == panel){
32616             this.updateTitle(title);
32617         }
32618         if(this.tabs){
32619             var ti = this.tabs.getTab(panel.getEl().id);
32620             ti.setText(title);
32621             if(panel.tabTip !== undefined){
32622                 ti.setTooltip(panel.tabTip);
32623             }
32624         }
32625     },
32626
32627     updateTitle : function(title){
32628         if(this.titleTextEl && !this.config.title){
32629             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
32630         }
32631     },
32632
32633     setActivePanel : function(panel)
32634     {
32635         panel = this.getPanel(panel);
32636         if(this.activePanel && this.activePanel != panel){
32637             this.activePanel.setActiveState(false);
32638         }
32639         this.activePanel = panel;
32640         panel.setActiveState(true);
32641         if(this.panelSize){
32642             panel.setSize(this.panelSize.width, this.panelSize.height);
32643         }
32644         if(this.closeBtn){
32645             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
32646         }
32647         this.updateTitle(panel.getTitle());
32648         if(this.tabs){
32649             this.fireEvent("invalidated", this);
32650         }
32651         this.fireEvent("panelactivated", this, panel);
32652     },
32653
32654     /**
32655      * Shows the specified panel.
32656      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
32657      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
32658      */
32659     showPanel : function(panel)
32660     {
32661         panel = this.getPanel(panel);
32662         if(panel){
32663             if(this.tabs){
32664                 var tab = this.tabs.getTab(panel.getEl().id);
32665                 if(tab.isHidden()){
32666                     this.tabs.unhideTab(tab.id);
32667                 }
32668                 tab.activate();
32669             }else{
32670                 this.setActivePanel(panel);
32671             }
32672         }
32673         return panel;
32674     },
32675
32676     /**
32677      * Get the active panel for this region.
32678      * @return {Roo.ContentPanel} The active panel or null
32679      */
32680     getActivePanel : function(){
32681         return this.activePanel;
32682     },
32683
32684     validateVisibility : function(){
32685         if(this.panels.getCount() < 1){
32686             this.updateTitle("&#160;");
32687             this.closeBtn.hide();
32688             this.hide();
32689         }else{
32690             if(!this.isVisible()){
32691                 this.show();
32692             }
32693         }
32694     },
32695
32696     /**
32697      * Adds the passed ContentPanel(s) to this region.
32698      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
32699      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
32700      */
32701     add : function(panel){
32702         if(arguments.length > 1){
32703             for(var i = 0, len = arguments.length; i < len; i++) {
32704                 this.add(arguments[i]);
32705             }
32706             return null;
32707         }
32708         if(this.hasPanel(panel)){
32709             this.showPanel(panel);
32710             return panel;
32711         }
32712         panel.setRegion(this);
32713         this.panels.add(panel);
32714         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
32715             this.bodyEl.dom.appendChild(panel.getEl().dom);
32716             if(panel.background !== true){
32717                 this.setActivePanel(panel);
32718             }
32719             this.fireEvent("paneladded", this, panel);
32720             return panel;
32721         }
32722         if(!this.tabs){
32723             this.initTabs();
32724         }else{
32725             this.initPanelAsTab(panel);
32726         }
32727         
32728         
32729         if(panel.background !== true){
32730             this.tabs.activate(panel.getEl().id);
32731         }
32732         this.fireEvent("paneladded", this, panel);
32733         return panel;
32734     },
32735
32736     /**
32737      * Hides the tab for the specified panel.
32738      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32739      */
32740     hidePanel : function(panel){
32741         if(this.tabs && (panel = this.getPanel(panel))){
32742             this.tabs.hideTab(panel.getEl().id);
32743         }
32744     },
32745
32746     /**
32747      * Unhides the tab for a previously hidden panel.
32748      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32749      */
32750     unhidePanel : function(panel){
32751         if(this.tabs && (panel = this.getPanel(panel))){
32752             this.tabs.unhideTab(panel.getEl().id);
32753         }
32754     },
32755
32756     clearPanels : function(){
32757         while(this.panels.getCount() > 0){
32758              this.remove(this.panels.first());
32759         }
32760     },
32761
32762     /**
32763      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
32764      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
32765      * @param {Boolean} preservePanel Overrides the config preservePanel option
32766      * @return {Roo.ContentPanel} The panel that was removed
32767      */
32768     remove : function(panel, preservePanel)
32769     {
32770         panel = this.getPanel(panel);
32771         if(!panel){
32772             return null;
32773         }
32774         var e = {};
32775         this.fireEvent("beforeremove", this, panel, e);
32776         if(e.cancel === true){
32777             return null;
32778         }
32779         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
32780         var panelId = panel.getId();
32781         this.panels.removeKey(panelId);
32782         if(preservePanel){
32783             document.body.appendChild(panel.getEl().dom);
32784         }
32785         if(this.tabs){
32786             this.tabs.removeTab(panel.getEl().id);
32787         }else if (!preservePanel){
32788             this.bodyEl.dom.removeChild(panel.getEl().dom);
32789         }
32790         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
32791             var p = this.panels.first();
32792             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
32793             tempEl.appendChild(p.getEl().dom);
32794             this.bodyEl.update("");
32795             this.bodyEl.dom.appendChild(p.getEl().dom);
32796             tempEl = null;
32797             this.updateTitle(p.getTitle());
32798             this.tabs = null;
32799             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
32800             this.setActivePanel(p);
32801         }
32802         panel.setRegion(null);
32803         if(this.activePanel == panel){
32804             this.activePanel = null;
32805         }
32806         if(this.config.autoDestroy !== false && preservePanel !== true){
32807             try{panel.destroy();}catch(e){}
32808         }
32809         this.fireEvent("panelremoved", this, panel);
32810         return panel;
32811     },
32812
32813     /**
32814      * Returns the TabPanel component used by this region
32815      * @return {Roo.TabPanel}
32816      */
32817     getTabs : function(){
32818         return this.tabs;
32819     },
32820
32821     createTool : function(parentEl, className){
32822         var btn = Roo.DomHelper.append(parentEl, {
32823             tag: "div",
32824             cls: "x-layout-tools-button",
32825             children: [ {
32826                 tag: "div",
32827                 cls: "roo-layout-tools-button-inner " + className,
32828                 html: "&#160;"
32829             }]
32830         }, true);
32831         btn.addClassOnOver("roo-layout-tools-button-over");
32832         return btn;
32833     }
32834 });/*
32835  * Based on:
32836  * Ext JS Library 1.1.1
32837  * Copyright(c) 2006-2007, Ext JS, LLC.
32838  *
32839  * Originally Released Under LGPL - original licence link has changed is not relivant.
32840  *
32841  * Fork - LGPL
32842  * <script type="text/javascript">
32843  */
32844  
32845
32846
32847 /**
32848  * @class Roo.SplitLayoutRegion
32849  * @extends Roo.LayoutRegion
32850  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
32851  */
32852 Roo.bootstrap.layout.Split = function(config){
32853     this.cursor = config.cursor;
32854     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
32855 };
32856
32857 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
32858 {
32859     splitTip : "Drag to resize.",
32860     collapsibleSplitTip : "Drag to resize. Double click to hide.",
32861     useSplitTips : false,
32862
32863     applyConfig : function(config){
32864         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
32865     },
32866     
32867     onRender : function(ctr,pos) {
32868         
32869         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
32870         if(!this.config.split){
32871             return;
32872         }
32873         if(!this.split){
32874             
32875             var splitEl = Roo.DomHelper.append(ctr.dom,  {
32876                             tag: "div",
32877                             id: this.el.id + "-split",
32878                             cls: "roo-layout-split roo-layout-split-"+this.position,
32879                             html: "&#160;"
32880             });
32881             /** The SplitBar for this region 
32882             * @type Roo.SplitBar */
32883             // does not exist yet...
32884             Roo.log([this.position, this.orientation]);
32885             
32886             this.split = new Roo.bootstrap.SplitBar({
32887                 dragElement : splitEl,
32888                 resizingElement: this.el,
32889                 orientation : this.orientation
32890             });
32891             
32892             this.split.on("moved", this.onSplitMove, this);
32893             this.split.useShim = this.config.useShim === true;
32894             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
32895             if(this.useSplitTips){
32896                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
32897             }
32898             //if(config.collapsible){
32899             //    this.split.el.on("dblclick", this.collapse,  this);
32900             //}
32901         }
32902         if(typeof this.config.minSize != "undefined"){
32903             this.split.minSize = this.config.minSize;
32904         }
32905         if(typeof this.config.maxSize != "undefined"){
32906             this.split.maxSize = this.config.maxSize;
32907         }
32908         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
32909             this.hideSplitter();
32910         }
32911         
32912     },
32913
32914     getHMaxSize : function(){
32915          var cmax = this.config.maxSize || 10000;
32916          var center = this.mgr.getRegion("center");
32917          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
32918     },
32919
32920     getVMaxSize : function(){
32921          var cmax = this.config.maxSize || 10000;
32922          var center = this.mgr.getRegion("center");
32923          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
32924     },
32925
32926     onSplitMove : function(split, newSize){
32927         this.fireEvent("resized", this, newSize);
32928     },
32929     
32930     /** 
32931      * Returns the {@link Roo.SplitBar} for this region.
32932      * @return {Roo.SplitBar}
32933      */
32934     getSplitBar : function(){
32935         return this.split;
32936     },
32937     
32938     hide : function(){
32939         this.hideSplitter();
32940         Roo.bootstrap.layout.Split.superclass.hide.call(this);
32941     },
32942
32943     hideSplitter : function(){
32944         if(this.split){
32945             this.split.el.setLocation(-2000,-2000);
32946             this.split.el.hide();
32947         }
32948     },
32949
32950     show : function(){
32951         if(this.split){
32952             this.split.el.show();
32953         }
32954         Roo.bootstrap.layout.Split.superclass.show.call(this);
32955     },
32956     
32957     beforeSlide: function(){
32958         if(Roo.isGecko){// firefox overflow auto bug workaround
32959             this.bodyEl.clip();
32960             if(this.tabs) {
32961                 this.tabs.bodyEl.clip();
32962             }
32963             if(this.activePanel){
32964                 this.activePanel.getEl().clip();
32965                 
32966                 if(this.activePanel.beforeSlide){
32967                     this.activePanel.beforeSlide();
32968                 }
32969             }
32970         }
32971     },
32972     
32973     afterSlide : function(){
32974         if(Roo.isGecko){// firefox overflow auto bug workaround
32975             this.bodyEl.unclip();
32976             if(this.tabs) {
32977                 this.tabs.bodyEl.unclip();
32978             }
32979             if(this.activePanel){
32980                 this.activePanel.getEl().unclip();
32981                 if(this.activePanel.afterSlide){
32982                     this.activePanel.afterSlide();
32983                 }
32984             }
32985         }
32986     },
32987
32988     initAutoHide : function(){
32989         if(this.autoHide !== false){
32990             if(!this.autoHideHd){
32991                 var st = new Roo.util.DelayedTask(this.slideIn, this);
32992                 this.autoHideHd = {
32993                     "mouseout": function(e){
32994                         if(!e.within(this.el, true)){
32995                             st.delay(500);
32996                         }
32997                     },
32998                     "mouseover" : function(e){
32999                         st.cancel();
33000                     },
33001                     scope : this
33002                 };
33003             }
33004             this.el.on(this.autoHideHd);
33005         }
33006     },
33007
33008     clearAutoHide : function(){
33009         if(this.autoHide !== false){
33010             this.el.un("mouseout", this.autoHideHd.mouseout);
33011             this.el.un("mouseover", this.autoHideHd.mouseover);
33012         }
33013     },
33014
33015     clearMonitor : function(){
33016         Roo.get(document).un("click", this.slideInIf, this);
33017     },
33018
33019     // these names are backwards but not changed for compat
33020     slideOut : function(){
33021         if(this.isSlid || this.el.hasActiveFx()){
33022             return;
33023         }
33024         this.isSlid = true;
33025         if(this.collapseBtn){
33026             this.collapseBtn.hide();
33027         }
33028         this.closeBtnState = this.closeBtn.getStyle('display');
33029         this.closeBtn.hide();
33030         if(this.stickBtn){
33031             this.stickBtn.show();
33032         }
33033         this.el.show();
33034         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
33035         this.beforeSlide();
33036         this.el.setStyle("z-index", 10001);
33037         this.el.slideIn(this.getSlideAnchor(), {
33038             callback: function(){
33039                 this.afterSlide();
33040                 this.initAutoHide();
33041                 Roo.get(document).on("click", this.slideInIf, this);
33042                 this.fireEvent("slideshow", this);
33043             },
33044             scope: this,
33045             block: true
33046         });
33047     },
33048
33049     afterSlideIn : function(){
33050         this.clearAutoHide();
33051         this.isSlid = false;
33052         this.clearMonitor();
33053         this.el.setStyle("z-index", "");
33054         if(this.collapseBtn){
33055             this.collapseBtn.show();
33056         }
33057         this.closeBtn.setStyle('display', this.closeBtnState);
33058         if(this.stickBtn){
33059             this.stickBtn.hide();
33060         }
33061         this.fireEvent("slidehide", this);
33062     },
33063
33064     slideIn : function(cb){
33065         if(!this.isSlid || this.el.hasActiveFx()){
33066             Roo.callback(cb);
33067             return;
33068         }
33069         this.isSlid = false;
33070         this.beforeSlide();
33071         this.el.slideOut(this.getSlideAnchor(), {
33072             callback: function(){
33073                 this.el.setLeftTop(-10000, -10000);
33074                 this.afterSlide();
33075                 this.afterSlideIn();
33076                 Roo.callback(cb);
33077             },
33078             scope: this,
33079             block: true
33080         });
33081     },
33082     
33083     slideInIf : function(e){
33084         if(!e.within(this.el)){
33085             this.slideIn();
33086         }
33087     },
33088
33089     animateCollapse : function(){
33090         this.beforeSlide();
33091         this.el.setStyle("z-index", 20000);
33092         var anchor = this.getSlideAnchor();
33093         this.el.slideOut(anchor, {
33094             callback : function(){
33095                 this.el.setStyle("z-index", "");
33096                 this.collapsedEl.slideIn(anchor, {duration:.3});
33097                 this.afterSlide();
33098                 this.el.setLocation(-10000,-10000);
33099                 this.el.hide();
33100                 this.fireEvent("collapsed", this);
33101             },
33102             scope: this,
33103             block: true
33104         });
33105     },
33106
33107     animateExpand : function(){
33108         this.beforeSlide();
33109         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
33110         this.el.setStyle("z-index", 20000);
33111         this.collapsedEl.hide({
33112             duration:.1
33113         });
33114         this.el.slideIn(this.getSlideAnchor(), {
33115             callback : function(){
33116                 this.el.setStyle("z-index", "");
33117                 this.afterSlide();
33118                 if(this.split){
33119                     this.split.el.show();
33120                 }
33121                 this.fireEvent("invalidated", this);
33122                 this.fireEvent("expanded", this);
33123             },
33124             scope: this,
33125             block: true
33126         });
33127     },
33128
33129     anchors : {
33130         "west" : "left",
33131         "east" : "right",
33132         "north" : "top",
33133         "south" : "bottom"
33134     },
33135
33136     sanchors : {
33137         "west" : "l",
33138         "east" : "r",
33139         "north" : "t",
33140         "south" : "b"
33141     },
33142
33143     canchors : {
33144         "west" : "tl-tr",
33145         "east" : "tr-tl",
33146         "north" : "tl-bl",
33147         "south" : "bl-tl"
33148     },
33149
33150     getAnchor : function(){
33151         return this.anchors[this.position];
33152     },
33153
33154     getCollapseAnchor : function(){
33155         return this.canchors[this.position];
33156     },
33157
33158     getSlideAnchor : function(){
33159         return this.sanchors[this.position];
33160     },
33161
33162     getAlignAdj : function(){
33163         var cm = this.cmargins;
33164         switch(this.position){
33165             case "west":
33166                 return [0, 0];
33167             break;
33168             case "east":
33169                 return [0, 0];
33170             break;
33171             case "north":
33172                 return [0, 0];
33173             break;
33174             case "south":
33175                 return [0, 0];
33176             break;
33177         }
33178     },
33179
33180     getExpandAdj : function(){
33181         var c = this.collapsedEl, cm = this.cmargins;
33182         switch(this.position){
33183             case "west":
33184                 return [-(cm.right+c.getWidth()+cm.left), 0];
33185             break;
33186             case "east":
33187                 return [cm.right+c.getWidth()+cm.left, 0];
33188             break;
33189             case "north":
33190                 return [0, -(cm.top+cm.bottom+c.getHeight())];
33191             break;
33192             case "south":
33193                 return [0, cm.top+cm.bottom+c.getHeight()];
33194             break;
33195         }
33196     }
33197 });/*
33198  * Based on:
33199  * Ext JS Library 1.1.1
33200  * Copyright(c) 2006-2007, Ext JS, LLC.
33201  *
33202  * Originally Released Under LGPL - original licence link has changed is not relivant.
33203  *
33204  * Fork - LGPL
33205  * <script type="text/javascript">
33206  */
33207 /*
33208  * These classes are private internal classes
33209  */
33210 Roo.bootstrap.layout.Center = function(config){
33211     config.region = "center";
33212     Roo.bootstrap.layout.Region.call(this, config);
33213     this.visible = true;
33214     this.minWidth = config.minWidth || 20;
33215     this.minHeight = config.minHeight || 20;
33216 };
33217
33218 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
33219     hide : function(){
33220         // center panel can't be hidden
33221     },
33222     
33223     show : function(){
33224         // center panel can't be hidden
33225     },
33226     
33227     getMinWidth: function(){
33228         return this.minWidth;
33229     },
33230     
33231     getMinHeight: function(){
33232         return this.minHeight;
33233     }
33234 });
33235
33236
33237
33238
33239  
33240
33241
33242
33243
33244
33245 Roo.bootstrap.layout.North = function(config)
33246 {
33247     config.region = 'north';
33248     config.cursor = 'n-resize';
33249     
33250     Roo.bootstrap.layout.Split.call(this, config);
33251     
33252     
33253     if(this.split){
33254         this.split.placement = Roo.bootstrap.SplitBar.TOP;
33255         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33256         this.split.el.addClass("roo-layout-split-v");
33257     }
33258     var size = config.initialSize || config.height;
33259     if(typeof size != "undefined"){
33260         this.el.setHeight(size);
33261     }
33262 };
33263 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
33264 {
33265     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33266     
33267     
33268     
33269     getBox : function(){
33270         if(this.collapsed){
33271             return this.collapsedEl.getBox();
33272         }
33273         var box = this.el.getBox();
33274         if(this.split){
33275             box.height += this.split.el.getHeight();
33276         }
33277         return box;
33278     },
33279     
33280     updateBox : function(box){
33281         if(this.split && !this.collapsed){
33282             box.height -= this.split.el.getHeight();
33283             this.split.el.setLeft(box.x);
33284             this.split.el.setTop(box.y+box.height);
33285             this.split.el.setWidth(box.width);
33286         }
33287         if(this.collapsed){
33288             this.updateBody(box.width, null);
33289         }
33290         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33291     }
33292 });
33293
33294
33295
33296
33297
33298 Roo.bootstrap.layout.South = function(config){
33299     config.region = 'south';
33300     config.cursor = 's-resize';
33301     Roo.bootstrap.layout.Split.call(this, config);
33302     if(this.split){
33303         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
33304         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
33305         this.split.el.addClass("roo-layout-split-v");
33306     }
33307     var size = config.initialSize || config.height;
33308     if(typeof size != "undefined"){
33309         this.el.setHeight(size);
33310     }
33311 };
33312
33313 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
33314     orientation: Roo.bootstrap.SplitBar.VERTICAL,
33315     getBox : function(){
33316         if(this.collapsed){
33317             return this.collapsedEl.getBox();
33318         }
33319         var box = this.el.getBox();
33320         if(this.split){
33321             var sh = this.split.el.getHeight();
33322             box.height += sh;
33323             box.y -= sh;
33324         }
33325         return box;
33326     },
33327     
33328     updateBox : function(box){
33329         if(this.split && !this.collapsed){
33330             var sh = this.split.el.getHeight();
33331             box.height -= sh;
33332             box.y += sh;
33333             this.split.el.setLeft(box.x);
33334             this.split.el.setTop(box.y-sh);
33335             this.split.el.setWidth(box.width);
33336         }
33337         if(this.collapsed){
33338             this.updateBody(box.width, null);
33339         }
33340         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33341     }
33342 });
33343
33344 Roo.bootstrap.layout.East = function(config){
33345     config.region = "east";
33346     config.cursor = "e-resize";
33347     Roo.bootstrap.layout.Split.call(this, config);
33348     if(this.split){
33349         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
33350         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33351         this.split.el.addClass("roo-layout-split-h");
33352     }
33353     var size = config.initialSize || config.width;
33354     if(typeof size != "undefined"){
33355         this.el.setWidth(size);
33356     }
33357 };
33358 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
33359     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33360     getBox : function(){
33361         if(this.collapsed){
33362             return this.collapsedEl.getBox();
33363         }
33364         var box = this.el.getBox();
33365         if(this.split){
33366             var sw = this.split.el.getWidth();
33367             box.width += sw;
33368             box.x -= sw;
33369         }
33370         return box;
33371     },
33372
33373     updateBox : function(box){
33374         if(this.split && !this.collapsed){
33375             var sw = this.split.el.getWidth();
33376             box.width -= sw;
33377             this.split.el.setLeft(box.x);
33378             this.split.el.setTop(box.y);
33379             this.split.el.setHeight(box.height);
33380             box.x += sw;
33381         }
33382         if(this.collapsed){
33383             this.updateBody(null, box.height);
33384         }
33385         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33386     }
33387 });
33388
33389 Roo.bootstrap.layout.West = function(config){
33390     config.region = "west";
33391     config.cursor = "w-resize";
33392     
33393     Roo.bootstrap.layout.Split.call(this, config);
33394     if(this.split){
33395         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
33396         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
33397         this.split.el.addClass("roo-layout-split-h");
33398     }
33399     
33400 };
33401 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
33402     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
33403     
33404     onRender: function(ctr, pos)
33405     {
33406         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
33407         var size = this.config.initialSize || this.config.width;
33408         if(typeof size != "undefined"){
33409             this.el.setWidth(size);
33410         }
33411     },
33412     
33413     getBox : function(){
33414         if(this.collapsed){
33415             return this.collapsedEl.getBox();
33416         }
33417         var box = this.el.getBox();
33418         if(this.split){
33419             box.width += this.split.el.getWidth();
33420         }
33421         return box;
33422     },
33423     
33424     updateBox : function(box){
33425         if(this.split && !this.collapsed){
33426             var sw = this.split.el.getWidth();
33427             box.width -= sw;
33428             this.split.el.setLeft(box.x+box.width);
33429             this.split.el.setTop(box.y);
33430             this.split.el.setHeight(box.height);
33431         }
33432         if(this.collapsed){
33433             this.updateBody(null, box.height);
33434         }
33435         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
33436     }
33437 });
33438 Roo.namespace("Roo.bootstrap.panel");/*
33439  * Based on:
33440  * Ext JS Library 1.1.1
33441  * Copyright(c) 2006-2007, Ext JS, LLC.
33442  *
33443  * Originally Released Under LGPL - original licence link has changed is not relivant.
33444  *
33445  * Fork - LGPL
33446  * <script type="text/javascript">
33447  */
33448 /**
33449  * @class Roo.ContentPanel
33450  * @extends Roo.util.Observable
33451  * A basic ContentPanel element.
33452  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
33453  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
33454  * @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
33455  * @cfg {Boolean}   closable      True if the panel can be closed/removed
33456  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
33457  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
33458  * @cfg {Toolbar}   toolbar       A toolbar for this panel
33459  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
33460  * @cfg {String} title          The title for this panel
33461  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
33462  * @cfg {String} url            Calls {@link #setUrl} with this value
33463  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
33464  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
33465  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
33466  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
33467
33468  * @constructor
33469  * Create a new ContentPanel.
33470  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
33471  * @param {String/Object} config A string to set only the title or a config object
33472  * @param {String} content (optional) Set the HTML content for this panel
33473  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
33474  */
33475 Roo.bootstrap.panel.Content = function( config){
33476     
33477     var el = config.el;
33478     var content = config.content;
33479
33480     if(config.autoCreate){ // xtype is available if this is called from factory
33481         el = Roo.id();
33482     }
33483     this.el = Roo.get(el);
33484     if(!this.el && config && config.autoCreate){
33485         if(typeof config.autoCreate == "object"){
33486             if(!config.autoCreate.id){
33487                 config.autoCreate.id = config.id||el;
33488             }
33489             this.el = Roo.DomHelper.append(document.body,
33490                         config.autoCreate, true);
33491         }else{
33492             var elcfg =  {   tag: "div",
33493                             cls: "roo-layout-inactive-content",
33494                             id: config.id||el
33495                             };
33496             if (config.html) {
33497                 elcfg.html = config.html;
33498                 
33499             }
33500                         
33501             this.el = Roo.DomHelper.append(document.body, elcfg , true);
33502         }
33503     } 
33504     this.closable = false;
33505     this.loaded = false;
33506     this.active = false;
33507    
33508       
33509     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
33510         
33511         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
33512         
33513         this.wrapEl = this.el.wrap();
33514         var ti = [];
33515         if (config.toolbar.items) {
33516             ti = config.toolbar.items ;
33517             delete config.toolbar.items ;
33518         }
33519         
33520         var nitems = [];
33521         this.toolbar.render(this.wrapEl, 'before');
33522         for(var i =0;i < ti.length;i++) {
33523           //  Roo.log(['add child', items[i]]);
33524             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33525         }
33526         this.toolbar.items = nitems;
33527         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
33528         delete config.toolbar;
33529         
33530     }
33531     /*
33532     // xtype created footer. - not sure if will work as we normally have to render first..
33533     if (this.footer && !this.footer.el && this.footer.xtype) {
33534         if (!this.wrapEl) {
33535             this.wrapEl = this.el.wrap();
33536         }
33537     
33538         this.footer.container = this.wrapEl.createChild();
33539          
33540         this.footer = Roo.factory(this.footer, Roo);
33541         
33542     }
33543     */
33544     
33545      if(typeof config == "string"){
33546         this.title = config;
33547     }else{
33548         Roo.apply(this, config);
33549     }
33550     
33551     if(this.resizeEl){
33552         this.resizeEl = Roo.get(this.resizeEl, true);
33553     }else{
33554         this.resizeEl = this.el;
33555     }
33556     // handle view.xtype
33557     
33558  
33559     
33560     
33561     this.addEvents({
33562         /**
33563          * @event activate
33564          * Fires when this panel is activated. 
33565          * @param {Roo.ContentPanel} this
33566          */
33567         "activate" : true,
33568         /**
33569          * @event deactivate
33570          * Fires when this panel is activated. 
33571          * @param {Roo.ContentPanel} this
33572          */
33573         "deactivate" : true,
33574
33575         /**
33576          * @event resize
33577          * Fires when this panel is resized if fitToFrame is true.
33578          * @param {Roo.ContentPanel} this
33579          * @param {Number} width The width after any component adjustments
33580          * @param {Number} height The height after any component adjustments
33581          */
33582         "resize" : true,
33583         
33584          /**
33585          * @event render
33586          * Fires when this tab is created
33587          * @param {Roo.ContentPanel} this
33588          */
33589         "render" : true
33590         
33591         
33592         
33593     });
33594     
33595
33596     
33597     
33598     if(this.autoScroll){
33599         this.resizeEl.setStyle("overflow", "auto");
33600     } else {
33601         // fix randome scrolling
33602         //this.el.on('scroll', function() {
33603         //    Roo.log('fix random scolling');
33604         //    this.scrollTo('top',0); 
33605         //});
33606     }
33607     content = content || this.content;
33608     if(content){
33609         this.setContent(content);
33610     }
33611     if(config && config.url){
33612         this.setUrl(this.url, this.params, this.loadOnce);
33613     }
33614     
33615     
33616     
33617     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
33618     
33619     if (this.view && typeof(this.view.xtype) != 'undefined') {
33620         this.view.el = this.el.appendChild(document.createElement("div"));
33621         this.view = Roo.factory(this.view); 
33622         this.view.render  &&  this.view.render(false, '');  
33623     }
33624     
33625     
33626     this.fireEvent('render', this);
33627 };
33628
33629 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
33630     tabTip:'',
33631     setRegion : function(region){
33632         this.region = region;
33633         if(region){
33634            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
33635         }else{
33636            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
33637         } 
33638     },
33639     
33640     /**
33641      * Returns the toolbar for this Panel if one was configured. 
33642      * @return {Roo.Toolbar} 
33643      */
33644     getToolbar : function(){
33645         return this.toolbar;
33646     },
33647     
33648     setActiveState : function(active){
33649         this.active = active;
33650         if(!active){
33651             this.fireEvent("deactivate", this);
33652         }else{
33653             this.fireEvent("activate", this);
33654         }
33655     },
33656     /**
33657      * Updates this panel's element
33658      * @param {String} content The new content
33659      * @param {Boolean} loadScripts (optional) true to look for and process scripts
33660     */
33661     setContent : function(content, loadScripts){
33662         this.el.update(content, loadScripts);
33663     },
33664
33665     ignoreResize : function(w, h){
33666         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
33667             return true;
33668         }else{
33669             this.lastSize = {width: w, height: h};
33670             return false;
33671         }
33672     },
33673     /**
33674      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
33675      * @return {Roo.UpdateManager} The UpdateManager
33676      */
33677     getUpdateManager : function(){
33678         return this.el.getUpdateManager();
33679     },
33680      /**
33681      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
33682      * @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:
33683 <pre><code>
33684 panel.load({
33685     url: "your-url.php",
33686     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
33687     callback: yourFunction,
33688     scope: yourObject, //(optional scope)
33689     discardUrl: false,
33690     nocache: false,
33691     text: "Loading...",
33692     timeout: 30,
33693     scripts: false
33694 });
33695 </code></pre>
33696      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
33697      * 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.
33698      * @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}
33699      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
33700      * @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.
33701      * @return {Roo.ContentPanel} this
33702      */
33703     load : function(){
33704         var um = this.el.getUpdateManager();
33705         um.update.apply(um, arguments);
33706         return this;
33707     },
33708
33709
33710     /**
33711      * 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.
33712      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
33713      * @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)
33714      * @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)
33715      * @return {Roo.UpdateManager} The UpdateManager
33716      */
33717     setUrl : function(url, params, loadOnce){
33718         if(this.refreshDelegate){
33719             this.removeListener("activate", this.refreshDelegate);
33720         }
33721         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
33722         this.on("activate", this.refreshDelegate);
33723         return this.el.getUpdateManager();
33724     },
33725     
33726     _handleRefresh : function(url, params, loadOnce){
33727         if(!loadOnce || !this.loaded){
33728             var updater = this.el.getUpdateManager();
33729             updater.update(url, params, this._setLoaded.createDelegate(this));
33730         }
33731     },
33732     
33733     _setLoaded : function(){
33734         this.loaded = true;
33735     }, 
33736     
33737     /**
33738      * Returns this panel's id
33739      * @return {String} 
33740      */
33741     getId : function(){
33742         return this.el.id;
33743     },
33744     
33745     /** 
33746      * Returns this panel's element - used by regiosn to add.
33747      * @return {Roo.Element} 
33748      */
33749     getEl : function(){
33750         return this.wrapEl || this.el;
33751     },
33752     
33753    
33754     
33755     adjustForComponents : function(width, height)
33756     {
33757         //Roo.log('adjustForComponents ');
33758         if(this.resizeEl != this.el){
33759             width -= this.el.getFrameWidth('lr');
33760             height -= this.el.getFrameWidth('tb');
33761         }
33762         if(this.toolbar){
33763             var te = this.toolbar.getEl();
33764             height -= te.getHeight();
33765             te.setWidth(width);
33766         }
33767         if(this.footer){
33768             var te = this.footer.getEl();
33769             Roo.log("footer:" + te.getHeight());
33770             
33771             height -= te.getHeight();
33772             te.setWidth(width);
33773         }
33774         
33775         
33776         if(this.adjustments){
33777             width += this.adjustments[0];
33778             height += this.adjustments[1];
33779         }
33780         return {"width": width, "height": height};
33781     },
33782     
33783     setSize : function(width, height){
33784         if(this.fitToFrame && !this.ignoreResize(width, height)){
33785             if(this.fitContainer && this.resizeEl != this.el){
33786                 this.el.setSize(width, height);
33787             }
33788             var size = this.adjustForComponents(width, height);
33789             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
33790             this.fireEvent('resize', this, size.width, size.height);
33791         }
33792     },
33793     
33794     /**
33795      * Returns this panel's title
33796      * @return {String} 
33797      */
33798     getTitle : function(){
33799         return this.title;
33800     },
33801     
33802     /**
33803      * Set this panel's title
33804      * @param {String} title
33805      */
33806     setTitle : function(title){
33807         this.title = title;
33808         if(this.region){
33809             this.region.updatePanelTitle(this, title);
33810         }
33811     },
33812     
33813     /**
33814      * Returns true is this panel was configured to be closable
33815      * @return {Boolean} 
33816      */
33817     isClosable : function(){
33818         return this.closable;
33819     },
33820     
33821     beforeSlide : function(){
33822         this.el.clip();
33823         this.resizeEl.clip();
33824     },
33825     
33826     afterSlide : function(){
33827         this.el.unclip();
33828         this.resizeEl.unclip();
33829     },
33830     
33831     /**
33832      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
33833      *   Will fail silently if the {@link #setUrl} method has not been called.
33834      *   This does not activate the panel, just updates its content.
33835      */
33836     refresh : function(){
33837         if(this.refreshDelegate){
33838            this.loaded = false;
33839            this.refreshDelegate();
33840         }
33841     },
33842     
33843     /**
33844      * Destroys this panel
33845      */
33846     destroy : function(){
33847         this.el.removeAllListeners();
33848         var tempEl = document.createElement("span");
33849         tempEl.appendChild(this.el.dom);
33850         tempEl.innerHTML = "";
33851         this.el.remove();
33852         this.el = null;
33853     },
33854     
33855     /**
33856      * form - if the content panel contains a form - this is a reference to it.
33857      * @type {Roo.form.Form}
33858      */
33859     form : false,
33860     /**
33861      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
33862      *    This contains a reference to it.
33863      * @type {Roo.View}
33864      */
33865     view : false,
33866     
33867       /**
33868      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
33869      * <pre><code>
33870
33871 layout.addxtype({
33872        xtype : 'Form',
33873        items: [ .... ]
33874    }
33875 );
33876
33877 </code></pre>
33878      * @param {Object} cfg Xtype definition of item to add.
33879      */
33880     
33881     
33882     getChildContainer: function () {
33883         return this.getEl();
33884     }
33885     
33886     
33887     /*
33888         var  ret = new Roo.factory(cfg);
33889         return ret;
33890         
33891         
33892         // add form..
33893         if (cfg.xtype.match(/^Form$/)) {
33894             
33895             var el;
33896             //if (this.footer) {
33897             //    el = this.footer.container.insertSibling(false, 'before');
33898             //} else {
33899                 el = this.el.createChild();
33900             //}
33901
33902             this.form = new  Roo.form.Form(cfg);
33903             
33904             
33905             if ( this.form.allItems.length) {
33906                 this.form.render(el.dom);
33907             }
33908             return this.form;
33909         }
33910         // should only have one of theses..
33911         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
33912             // views.. should not be just added - used named prop 'view''
33913             
33914             cfg.el = this.el.appendChild(document.createElement("div"));
33915             // factory?
33916             
33917             var ret = new Roo.factory(cfg);
33918              
33919              ret.render && ret.render(false, ''); // render blank..
33920             this.view = ret;
33921             return ret;
33922         }
33923         return false;
33924     }
33925     \*/
33926 });
33927  
33928 /**
33929  * @class Roo.bootstrap.panel.Grid
33930  * @extends Roo.bootstrap.panel.Content
33931  * @constructor
33932  * Create a new GridPanel.
33933  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
33934  * @param {Object} config A the config object
33935   
33936  */
33937
33938
33939
33940 Roo.bootstrap.panel.Grid = function(config)
33941 {
33942     
33943       
33944     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
33945         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
33946
33947     config.el = this.wrapper;
33948     //this.el = this.wrapper;
33949     
33950       if (config.container) {
33951         // ctor'ed from a Border/panel.grid
33952         
33953         
33954         this.wrapper.setStyle("overflow", "hidden");
33955         this.wrapper.addClass('roo-grid-container');
33956
33957     }
33958     
33959     
33960     if(config.toolbar){
33961         var tool_el = this.wrapper.createChild();    
33962         this.toolbar = Roo.factory(config.toolbar);
33963         var ti = [];
33964         if (config.toolbar.items) {
33965             ti = config.toolbar.items ;
33966             delete config.toolbar.items ;
33967         }
33968         
33969         var nitems = [];
33970         this.toolbar.render(tool_el);
33971         for(var i =0;i < ti.length;i++) {
33972           //  Roo.log(['add child', items[i]]);
33973             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
33974         }
33975         this.toolbar.items = nitems;
33976         
33977         delete config.toolbar;
33978     }
33979     
33980     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
33981     config.grid.scrollBody = true;;
33982     config.grid.monitorWindowResize = false; // turn off autosizing
33983     config.grid.autoHeight = false;
33984     config.grid.autoWidth = false;
33985     
33986     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
33987     
33988     if (config.background) {
33989         // render grid on panel activation (if panel background)
33990         this.on('activate', function(gp) {
33991             if (!gp.grid.rendered) {
33992                 gp.grid.render(el);
33993                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
33994
33995             }
33996         });
33997             
33998     } else {
33999         this.grid.render(this.wrapper);
34000         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
34001
34002     }
34003     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
34004     // ??? needed ??? config.el = this.wrapper;
34005     
34006     
34007     
34008   
34009     // xtype created footer. - not sure if will work as we normally have to render first..
34010     if (this.footer && !this.footer.el && this.footer.xtype) {
34011         
34012         var ctr = this.grid.getView().getFooterPanel(true);
34013         this.footer.dataSource = this.grid.dataSource;
34014         this.footer = Roo.factory(this.footer, Roo);
34015         this.footer.render(ctr);
34016         
34017     }
34018     
34019     
34020     
34021     
34022      
34023 };
34024
34025 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
34026     getId : function(){
34027         return this.grid.id;
34028     },
34029     
34030     /**
34031      * Returns the grid for this panel
34032      * @return {Roo.bootstrap.Table} 
34033      */
34034     getGrid : function(){
34035         return this.grid;    
34036     },
34037     
34038     setSize : function(width, height){
34039         if(!this.ignoreResize(width, height)){
34040             var grid = this.grid;
34041             var size = this.adjustForComponents(width, height);
34042             var gridel = grid.getGridEl();
34043             gridel.setSize(size.width, size.height);
34044             /*
34045             var thd = grid.getGridEl().select('thead',true).first();
34046             var tbd = grid.getGridEl().select('tbody', true).first();
34047             if (tbd) {
34048                 tbd.setSize(width, height - thd.getHeight());
34049             }
34050             */
34051             grid.autoSize();
34052         }
34053     },
34054      
34055     
34056     
34057     beforeSlide : function(){
34058         this.grid.getView().scroller.clip();
34059     },
34060     
34061     afterSlide : function(){
34062         this.grid.getView().scroller.unclip();
34063     },
34064     
34065     destroy : function(){
34066         this.grid.destroy();
34067         delete this.grid;
34068         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
34069     }
34070 });
34071
34072 /**
34073  * @class Roo.bootstrap.panel.Nest
34074  * @extends Roo.bootstrap.panel.Content
34075  * @constructor
34076  * Create a new Panel, that can contain a layout.Border.
34077  * 
34078  * 
34079  * @param {Roo.BorderLayout} layout The layout for this panel
34080  * @param {String/Object} config A string to set only the title or a config object
34081  */
34082 Roo.bootstrap.panel.Nest = function(config)
34083 {
34084     // construct with only one argument..
34085     /* FIXME - implement nicer consturctors
34086     if (layout.layout) {
34087         config = layout;
34088         layout = config.layout;
34089         delete config.layout;
34090     }
34091     if (layout.xtype && !layout.getEl) {
34092         // then layout needs constructing..
34093         layout = Roo.factory(layout, Roo);
34094     }
34095     */
34096     
34097     config.el =  config.layout.getEl();
34098     
34099     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
34100     
34101     config.layout.monitorWindowResize = false; // turn off autosizing
34102     this.layout = config.layout;
34103     this.layout.getEl().addClass("roo-layout-nested-layout");
34104     
34105     
34106     
34107     
34108 };
34109
34110 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
34111
34112     setSize : function(width, height){
34113         if(!this.ignoreResize(width, height)){
34114             var size = this.adjustForComponents(width, height);
34115             var el = this.layout.getEl();
34116             el.setSize(size.width, size.height);
34117             var touch = el.dom.offsetWidth;
34118             this.layout.layout();
34119             // ie requires a double layout on the first pass
34120             if(Roo.isIE && !this.initialized){
34121                 this.initialized = true;
34122                 this.layout.layout();
34123             }
34124         }
34125     },
34126     
34127     // activate all subpanels if not currently active..
34128     
34129     setActiveState : function(active){
34130         this.active = active;
34131         if(!active){
34132             this.fireEvent("deactivate", this);
34133             return;
34134         }
34135         
34136         this.fireEvent("activate", this);
34137         // not sure if this should happen before or after..
34138         if (!this.layout) {
34139             return; // should not happen..
34140         }
34141         var reg = false;
34142         for (var r in this.layout.regions) {
34143             reg = this.layout.getRegion(r);
34144             if (reg.getActivePanel()) {
34145                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
34146                 reg.setActivePanel(reg.getActivePanel());
34147                 continue;
34148             }
34149             if (!reg.panels.length) {
34150                 continue;
34151             }
34152             reg.showPanel(reg.getPanel(0));
34153         }
34154         
34155         
34156         
34157         
34158     },
34159     
34160     /**
34161      * Returns the nested BorderLayout for this panel
34162      * @return {Roo.BorderLayout} 
34163      */
34164     getLayout : function(){
34165         return this.layout;
34166     },
34167     
34168      /**
34169      * Adds a xtype elements to the layout of the nested panel
34170      * <pre><code>
34171
34172 panel.addxtype({
34173        xtype : 'ContentPanel',
34174        region: 'west',
34175        items: [ .... ]
34176    }
34177 );
34178
34179 panel.addxtype({
34180         xtype : 'NestedLayoutPanel',
34181         region: 'west',
34182         layout: {
34183            center: { },
34184            west: { }   
34185         },
34186         items : [ ... list of content panels or nested layout panels.. ]
34187    }
34188 );
34189 </code></pre>
34190      * @param {Object} cfg Xtype definition of item to add.
34191      */
34192     addxtype : function(cfg) {
34193         return this.layout.addxtype(cfg);
34194     
34195     }
34196 });        /*
34197  * Based on:
34198  * Ext JS Library 1.1.1
34199  * Copyright(c) 2006-2007, Ext JS, LLC.
34200  *
34201  * Originally Released Under LGPL - original licence link has changed is not relivant.
34202  *
34203  * Fork - LGPL
34204  * <script type="text/javascript">
34205  */
34206 /**
34207  * @class Roo.TabPanel
34208  * @extends Roo.util.Observable
34209  * A lightweight tab container.
34210  * <br><br>
34211  * Usage:
34212  * <pre><code>
34213 // basic tabs 1, built from existing content
34214 var tabs = new Roo.TabPanel("tabs1");
34215 tabs.addTab("script", "View Script");
34216 tabs.addTab("markup", "View Markup");
34217 tabs.activate("script");
34218
34219 // more advanced tabs, built from javascript
34220 var jtabs = new Roo.TabPanel("jtabs");
34221 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
34222
34223 // set up the UpdateManager
34224 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
34225 var updater = tab2.getUpdateManager();
34226 updater.setDefaultUrl("ajax1.htm");
34227 tab2.on('activate', updater.refresh, updater, true);
34228
34229 // Use setUrl for Ajax loading
34230 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
34231 tab3.setUrl("ajax2.htm", null, true);
34232
34233 // Disabled tab
34234 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
34235 tab4.disable();
34236
34237 jtabs.activate("jtabs-1");
34238  * </code></pre>
34239  * @constructor
34240  * Create a new TabPanel.
34241  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
34242  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
34243  */
34244 Roo.bootstrap.panel.Tabs = function(config){
34245     /**
34246     * The container element for this TabPanel.
34247     * @type Roo.Element
34248     */
34249     this.el = Roo.get(config.el);
34250     delete config.el;
34251     if(config){
34252         if(typeof config == "boolean"){
34253             this.tabPosition = config ? "bottom" : "top";
34254         }else{
34255             Roo.apply(this, config);
34256         }
34257     }
34258     
34259     if(this.tabPosition == "bottom"){
34260         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34261         this.el.addClass("roo-tabs-bottom");
34262     }
34263     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
34264     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
34265     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
34266     if(Roo.isIE){
34267         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
34268     }
34269     if(this.tabPosition != "bottom"){
34270         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
34271          * @type Roo.Element
34272          */
34273         this.bodyEl = Roo.get(this.createBody(this.el.dom));
34274         this.el.addClass("roo-tabs-top");
34275     }
34276     this.items = [];
34277
34278     this.bodyEl.setStyle("position", "relative");
34279
34280     this.active = null;
34281     this.activateDelegate = this.activate.createDelegate(this);
34282
34283     this.addEvents({
34284         /**
34285          * @event tabchange
34286          * Fires when the active tab changes
34287          * @param {Roo.TabPanel} this
34288          * @param {Roo.TabPanelItem} activePanel The new active tab
34289          */
34290         "tabchange": true,
34291         /**
34292          * @event beforetabchange
34293          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
34294          * @param {Roo.TabPanel} this
34295          * @param {Object} e Set cancel to true on this object to cancel the tab change
34296          * @param {Roo.TabPanelItem} tab The tab being changed to
34297          */
34298         "beforetabchange" : true
34299     });
34300
34301     Roo.EventManager.onWindowResize(this.onResize, this);
34302     this.cpad = this.el.getPadding("lr");
34303     this.hiddenCount = 0;
34304
34305
34306     // toolbar on the tabbar support...
34307     if (this.toolbar) {
34308         alert("no toolbar support yet");
34309         this.toolbar  = false;
34310         /*
34311         var tcfg = this.toolbar;
34312         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
34313         this.toolbar = new Roo.Toolbar(tcfg);
34314         if (Roo.isSafari) {
34315             var tbl = tcfg.container.child('table', true);
34316             tbl.setAttribute('width', '100%');
34317         }
34318         */
34319         
34320     }
34321    
34322
34323
34324     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
34325 };
34326
34327 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
34328     /*
34329      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
34330      */
34331     tabPosition : "top",
34332     /*
34333      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
34334      */
34335     currentTabWidth : 0,
34336     /*
34337      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
34338      */
34339     minTabWidth : 40,
34340     /*
34341      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
34342      */
34343     maxTabWidth : 250,
34344     /*
34345      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
34346      */
34347     preferredTabWidth : 175,
34348     /*
34349      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
34350      */
34351     resizeTabs : false,
34352     /*
34353      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
34354      */
34355     monitorResize : true,
34356     /*
34357      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
34358      */
34359     toolbar : false,
34360
34361     /**
34362      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
34363      * @param {String} id The id of the div to use <b>or create</b>
34364      * @param {String} text The text for the tab
34365      * @param {String} content (optional) Content to put in the TabPanelItem body
34366      * @param {Boolean} closable (optional) True to create a close icon on the tab
34367      * @return {Roo.TabPanelItem} The created TabPanelItem
34368      */
34369     addTab : function(id, text, content, closable)
34370     {
34371         var item = new Roo.bootstrap.panel.TabItem({
34372             panel: this,
34373             id : id,
34374             text : text,
34375             closable : closable
34376         });
34377         this.addTabItem(item);
34378         if(content){
34379             item.setContent(content);
34380         }
34381         return item;
34382     },
34383
34384     /**
34385      * Returns the {@link Roo.TabPanelItem} with the specified id/index
34386      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
34387      * @return {Roo.TabPanelItem}
34388      */
34389     getTab : function(id){
34390         return this.items[id];
34391     },
34392
34393     /**
34394      * Hides the {@link Roo.TabPanelItem} with the specified id/index
34395      * @param {String/Number} id The id or index of the TabPanelItem to hide.
34396      */
34397     hideTab : function(id){
34398         var t = this.items[id];
34399         if(!t.isHidden()){
34400            t.setHidden(true);
34401            this.hiddenCount++;
34402            this.autoSizeTabs();
34403         }
34404     },
34405
34406     /**
34407      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
34408      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
34409      */
34410     unhideTab : function(id){
34411         var t = this.items[id];
34412         if(t.isHidden()){
34413            t.setHidden(false);
34414            this.hiddenCount--;
34415            this.autoSizeTabs();
34416         }
34417     },
34418
34419     /**
34420      * Adds an existing {@link Roo.TabPanelItem}.
34421      * @param {Roo.TabPanelItem} item The TabPanelItem to add
34422      */
34423     addTabItem : function(item){
34424         this.items[item.id] = item;
34425         this.items.push(item);
34426       //  if(this.resizeTabs){
34427     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
34428   //         this.autoSizeTabs();
34429 //        }else{
34430 //            item.autoSize();
34431        // }
34432     },
34433
34434     /**
34435      * Removes a {@link Roo.TabPanelItem}.
34436      * @param {String/Number} id The id or index of the TabPanelItem to remove.
34437      */
34438     removeTab : function(id){
34439         var items = this.items;
34440         var tab = items[id];
34441         if(!tab) { return; }
34442         var index = items.indexOf(tab);
34443         if(this.active == tab && items.length > 1){
34444             var newTab = this.getNextAvailable(index);
34445             if(newTab) {
34446                 newTab.activate();
34447             }
34448         }
34449         this.stripEl.dom.removeChild(tab.pnode.dom);
34450         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
34451             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
34452         }
34453         items.splice(index, 1);
34454         delete this.items[tab.id];
34455         tab.fireEvent("close", tab);
34456         tab.purgeListeners();
34457         this.autoSizeTabs();
34458     },
34459
34460     getNextAvailable : function(start){
34461         var items = this.items;
34462         var index = start;
34463         // look for a next tab that will slide over to
34464         // replace the one being removed
34465         while(index < items.length){
34466             var item = items[++index];
34467             if(item && !item.isHidden()){
34468                 return item;
34469             }
34470         }
34471         // if one isn't found select the previous tab (on the left)
34472         index = start;
34473         while(index >= 0){
34474             var item = items[--index];
34475             if(item && !item.isHidden()){
34476                 return item;
34477             }
34478         }
34479         return null;
34480     },
34481
34482     /**
34483      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
34484      * @param {String/Number} id The id or index of the TabPanelItem to disable.
34485      */
34486     disableTab : function(id){
34487         var tab = this.items[id];
34488         if(tab && this.active != tab){
34489             tab.disable();
34490         }
34491     },
34492
34493     /**
34494      * Enables a {@link Roo.TabPanelItem} that is disabled.
34495      * @param {String/Number} id The id or index of the TabPanelItem to enable.
34496      */
34497     enableTab : function(id){
34498         var tab = this.items[id];
34499         tab.enable();
34500     },
34501
34502     /**
34503      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
34504      * @param {String/Number} id The id or index of the TabPanelItem to activate.
34505      * @return {Roo.TabPanelItem} The TabPanelItem.
34506      */
34507     activate : function(id){
34508         var tab = this.items[id];
34509         if(!tab){
34510             return null;
34511         }
34512         if(tab == this.active || tab.disabled){
34513             return tab;
34514         }
34515         var e = {};
34516         this.fireEvent("beforetabchange", this, e, tab);
34517         if(e.cancel !== true && !tab.disabled){
34518             if(this.active){
34519                 this.active.hide();
34520             }
34521             this.active = this.items[id];
34522             this.active.show();
34523             this.fireEvent("tabchange", this, this.active);
34524         }
34525         return tab;
34526     },
34527
34528     /**
34529      * Gets the active {@link Roo.TabPanelItem}.
34530      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
34531      */
34532     getActiveTab : function(){
34533         return this.active;
34534     },
34535
34536     /**
34537      * Updates the tab body element to fit the height of the container element
34538      * for overflow scrolling
34539      * @param {Number} targetHeight (optional) Override the starting height from the elements height
34540      */
34541     syncHeight : function(targetHeight){
34542         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34543         var bm = this.bodyEl.getMargins();
34544         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
34545         this.bodyEl.setHeight(newHeight);
34546         return newHeight;
34547     },
34548
34549     onResize : function(){
34550         if(this.monitorResize){
34551             this.autoSizeTabs();
34552         }
34553     },
34554
34555     /**
34556      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
34557      */
34558     beginUpdate : function(){
34559         this.updating = true;
34560     },
34561
34562     /**
34563      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
34564      */
34565     endUpdate : function(){
34566         this.updating = false;
34567         this.autoSizeTabs();
34568     },
34569
34570     /**
34571      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
34572      */
34573     autoSizeTabs : function(){
34574         var count = this.items.length;
34575         var vcount = count - this.hiddenCount;
34576         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
34577             return;
34578         }
34579         var w = Math.max(this.el.getWidth() - this.cpad, 10);
34580         var availWidth = Math.floor(w / vcount);
34581         var b = this.stripBody;
34582         if(b.getWidth() > w){
34583             var tabs = this.items;
34584             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
34585             if(availWidth < this.minTabWidth){
34586                 /*if(!this.sleft){    // incomplete scrolling code
34587                     this.createScrollButtons();
34588                 }
34589                 this.showScroll();
34590                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
34591             }
34592         }else{
34593             if(this.currentTabWidth < this.preferredTabWidth){
34594                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
34595             }
34596         }
34597     },
34598
34599     /**
34600      * Returns the number of tabs in this TabPanel.
34601      * @return {Number}
34602      */
34603      getCount : function(){
34604          return this.items.length;
34605      },
34606
34607     /**
34608      * Resizes all the tabs to the passed width
34609      * @param {Number} The new width
34610      */
34611     setTabWidth : function(width){
34612         this.currentTabWidth = width;
34613         for(var i = 0, len = this.items.length; i < len; i++) {
34614                 if(!this.items[i].isHidden()) {
34615                 this.items[i].setWidth(width);
34616             }
34617         }
34618     },
34619
34620     /**
34621      * Destroys this TabPanel
34622      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
34623      */
34624     destroy : function(removeEl){
34625         Roo.EventManager.removeResizeListener(this.onResize, this);
34626         for(var i = 0, len = this.items.length; i < len; i++){
34627             this.items[i].purgeListeners();
34628         }
34629         if(removeEl === true){
34630             this.el.update("");
34631             this.el.remove();
34632         }
34633     },
34634     
34635     createStrip : function(container)
34636     {
34637         var strip = document.createElement("nav");
34638         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
34639         container.appendChild(strip);
34640         return strip;
34641     },
34642     
34643     createStripList : function(strip)
34644     {
34645         // div wrapper for retard IE
34646         // returns the "tr" element.
34647         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
34648         //'<div class="x-tabs-strip-wrap">'+
34649           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
34650           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
34651         return strip.firstChild; //.firstChild.firstChild.firstChild;
34652     },
34653     createBody : function(container)
34654     {
34655         var body = document.createElement("div");
34656         Roo.id(body, "tab-body");
34657         //Roo.fly(body).addClass("x-tabs-body");
34658         Roo.fly(body).addClass("tab-content");
34659         container.appendChild(body);
34660         return body;
34661     },
34662     createItemBody :function(bodyEl, id){
34663         var body = Roo.getDom(id);
34664         if(!body){
34665             body = document.createElement("div");
34666             body.id = id;
34667         }
34668         //Roo.fly(body).addClass("x-tabs-item-body");
34669         Roo.fly(body).addClass("tab-pane");
34670          bodyEl.insertBefore(body, bodyEl.firstChild);
34671         return body;
34672     },
34673     /** @private */
34674     createStripElements :  function(stripEl, text, closable)
34675     {
34676         var td = document.createElement("li"); // was td..
34677         stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
34678         //stripEl.appendChild(td);
34679         /*if(closable){
34680             td.className = "x-tabs-closable";
34681             if(!this.closeTpl){
34682                 this.closeTpl = new Roo.Template(
34683                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34684                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
34685                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
34686                 );
34687             }
34688             var el = this.closeTpl.overwrite(td, {"text": text});
34689             var close = el.getElementsByTagName("div")[0];
34690             var inner = el.getElementsByTagName("em")[0];
34691             return {"el": el, "close": close, "inner": inner};
34692         } else {
34693         */
34694         // not sure what this is..
34695             if(!this.tabTpl){
34696                 //this.tabTpl = new Roo.Template(
34697                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
34698                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
34699                 //);
34700                 this.tabTpl = new Roo.Template(
34701                    '<a href="#">' +
34702                    '<span unselectable="on"' +
34703                             (this.disableTooltips ? '' : ' title="{text}"') +
34704                             ' >{text}</span></span></a>'
34705                 );
34706                 
34707             }
34708             var el = this.tabTpl.overwrite(td, {"text": text});
34709             var inner = el.getElementsByTagName("span")[0];
34710             return {"el": el, "inner": inner};
34711         //}
34712     }
34713         
34714     
34715 });
34716
34717 /**
34718  * @class Roo.TabPanelItem
34719  * @extends Roo.util.Observable
34720  * Represents an individual item (tab plus body) in a TabPanel.
34721  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
34722  * @param {String} id The id of this TabPanelItem
34723  * @param {String} text The text for the tab of this TabPanelItem
34724  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
34725  */
34726 Roo.bootstrap.panel.TabItem = function(config){
34727     /**
34728      * The {@link Roo.TabPanel} this TabPanelItem belongs to
34729      * @type Roo.TabPanel
34730      */
34731     this.tabPanel = config.panel;
34732     /**
34733      * The id for this TabPanelItem
34734      * @type String
34735      */
34736     this.id = config.id;
34737     /** @private */
34738     this.disabled = false;
34739     /** @private */
34740     this.text = config.text;
34741     /** @private */
34742     this.loaded = false;
34743     this.closable = config.closable;
34744
34745     /**
34746      * The body element for this TabPanelItem.
34747      * @type Roo.Element
34748      */
34749     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
34750     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
34751     this.bodyEl.setStyle("display", "block");
34752     this.bodyEl.setStyle("zoom", "1");
34753     //this.hideAction();
34754
34755     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable);
34756     /** @private */
34757     this.el = Roo.get(els.el);
34758     this.inner = Roo.get(els.inner, true);
34759     this.textEl = Roo.get(this.el.dom.firstChild, true);
34760     this.pnode = Roo.get(els.el.parentNode, true);
34761     this.el.on("mousedown", this.onTabMouseDown, this);
34762     this.el.on("click", this.onTabClick, this);
34763     /** @private */
34764     if(config.closable){
34765         var c = Roo.get(els.close, true);
34766         c.dom.title = this.closeText;
34767         c.addClassOnOver("close-over");
34768         c.on("click", this.closeClick, this);
34769      }
34770
34771     this.addEvents({
34772          /**
34773          * @event activate
34774          * Fires when this tab becomes the active tab.
34775          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34776          * @param {Roo.TabPanelItem} this
34777          */
34778         "activate": true,
34779         /**
34780          * @event beforeclose
34781          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
34782          * @param {Roo.TabPanelItem} this
34783          * @param {Object} e Set cancel to true on this object to cancel the close.
34784          */
34785         "beforeclose": true,
34786         /**
34787          * @event close
34788          * Fires when this tab is closed.
34789          * @param {Roo.TabPanelItem} this
34790          */
34791          "close": true,
34792         /**
34793          * @event deactivate
34794          * Fires when this tab is no longer the active tab.
34795          * @param {Roo.TabPanel} tabPanel The parent TabPanel
34796          * @param {Roo.TabPanelItem} this
34797          */
34798          "deactivate" : true
34799     });
34800     this.hidden = false;
34801
34802     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
34803 };
34804
34805 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
34806            {
34807     purgeListeners : function(){
34808        Roo.util.Observable.prototype.purgeListeners.call(this);
34809        this.el.removeAllListeners();
34810     },
34811     /**
34812      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
34813      */
34814     show : function(){
34815         this.pnode.addClass("active");
34816         this.showAction();
34817         if(Roo.isOpera){
34818             this.tabPanel.stripWrap.repaint();
34819         }
34820         this.fireEvent("activate", this.tabPanel, this);
34821     },
34822
34823     /**
34824      * Returns true if this tab is the active tab.
34825      * @return {Boolean}
34826      */
34827     isActive : function(){
34828         return this.tabPanel.getActiveTab() == this;
34829     },
34830
34831     /**
34832      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
34833      */
34834     hide : function(){
34835         this.pnode.removeClass("active");
34836         this.hideAction();
34837         this.fireEvent("deactivate", this.tabPanel, this);
34838     },
34839
34840     hideAction : function(){
34841         this.bodyEl.hide();
34842         this.bodyEl.setStyle("position", "absolute");
34843         this.bodyEl.setLeft("-20000px");
34844         this.bodyEl.setTop("-20000px");
34845     },
34846
34847     showAction : function(){
34848         this.bodyEl.setStyle("position", "relative");
34849         this.bodyEl.setTop("");
34850         this.bodyEl.setLeft("");
34851         this.bodyEl.show();
34852     },
34853
34854     /**
34855      * Set the tooltip for the tab.
34856      * @param {String} tooltip The tab's tooltip
34857      */
34858     setTooltip : function(text){
34859         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
34860             this.textEl.dom.qtip = text;
34861             this.textEl.dom.removeAttribute('title');
34862         }else{
34863             this.textEl.dom.title = text;
34864         }
34865     },
34866
34867     onTabClick : function(e){
34868         e.preventDefault();
34869         this.tabPanel.activate(this.id);
34870     },
34871
34872     onTabMouseDown : function(e){
34873         e.preventDefault();
34874         this.tabPanel.activate(this.id);
34875     },
34876 /*
34877     getWidth : function(){
34878         return this.inner.getWidth();
34879     },
34880
34881     setWidth : function(width){
34882         var iwidth = width - this.pnode.getPadding("lr");
34883         this.inner.setWidth(iwidth);
34884         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
34885         this.pnode.setWidth(width);
34886     },
34887 */
34888     /**
34889      * Show or hide the tab
34890      * @param {Boolean} hidden True to hide or false to show.
34891      */
34892     setHidden : function(hidden){
34893         this.hidden = hidden;
34894         this.pnode.setStyle("display", hidden ? "none" : "");
34895     },
34896
34897     /**
34898      * Returns true if this tab is "hidden"
34899      * @return {Boolean}
34900      */
34901     isHidden : function(){
34902         return this.hidden;
34903     },
34904
34905     /**
34906      * Returns the text for this tab
34907      * @return {String}
34908      */
34909     getText : function(){
34910         return this.text;
34911     },
34912     /*
34913     autoSize : function(){
34914         //this.el.beginMeasure();
34915         this.textEl.setWidth(1);
34916         /*
34917          *  #2804 [new] Tabs in Roojs
34918          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
34919          */
34920         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
34921         //this.el.endMeasure();
34922     //},
34923
34924     /**
34925      * Sets the text for the tab (Note: this also sets the tooltip text)
34926      * @param {String} text The tab's text and tooltip
34927      */
34928     setText : function(text){
34929         this.text = text;
34930         this.textEl.update(text);
34931         this.setTooltip(text);
34932         //if(!this.tabPanel.resizeTabs){
34933         //    this.autoSize();
34934         //}
34935     },
34936     /**
34937      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
34938      */
34939     activate : function(){
34940         this.tabPanel.activate(this.id);
34941     },
34942
34943     /**
34944      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
34945      */
34946     disable : function(){
34947         if(this.tabPanel.active != this){
34948             this.disabled = true;
34949             this.pnode.addClass("disabled");
34950         }
34951     },
34952
34953     /**
34954      * Enables this TabPanelItem if it was previously disabled.
34955      */
34956     enable : function(){
34957         this.disabled = false;
34958         this.pnode.removeClass("disabled");
34959     },
34960
34961     /**
34962      * Sets the content for this TabPanelItem.
34963      * @param {String} content The content
34964      * @param {Boolean} loadScripts true to look for and load scripts
34965      */
34966     setContent : function(content, loadScripts){
34967         this.bodyEl.update(content, loadScripts);
34968     },
34969
34970     /**
34971      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
34972      * @return {Roo.UpdateManager} The UpdateManager
34973      */
34974     getUpdateManager : function(){
34975         return this.bodyEl.getUpdateManager();
34976     },
34977
34978     /**
34979      * Set a URL to be used to load the content for this TabPanelItem.
34980      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
34981      * @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)
34982      * @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)
34983      * @return {Roo.UpdateManager} The UpdateManager
34984      */
34985     setUrl : function(url, params, loadOnce){
34986         if(this.refreshDelegate){
34987             this.un('activate', this.refreshDelegate);
34988         }
34989         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
34990         this.on("activate", this.refreshDelegate);
34991         return this.bodyEl.getUpdateManager();
34992     },
34993
34994     /** @private */
34995     _handleRefresh : function(url, params, loadOnce){
34996         if(!loadOnce || !this.loaded){
34997             var updater = this.bodyEl.getUpdateManager();
34998             updater.update(url, params, this._setLoaded.createDelegate(this));
34999         }
35000     },
35001
35002     /**
35003      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
35004      *   Will fail silently if the setUrl method has not been called.
35005      *   This does not activate the panel, just updates its content.
35006      */
35007     refresh : function(){
35008         if(this.refreshDelegate){
35009            this.loaded = false;
35010            this.refreshDelegate();
35011         }
35012     },
35013
35014     /** @private */
35015     _setLoaded : function(){
35016         this.loaded = true;
35017     },
35018
35019     /** @private */
35020     closeClick : function(e){
35021         var o = {};
35022         e.stopEvent();
35023         this.fireEvent("beforeclose", this, o);
35024         if(o.cancel !== true){
35025             this.tabPanel.removeTab(this.id);
35026         }
35027     },
35028     /**
35029      * The text displayed in the tooltip for the close icon.
35030      * @type String
35031      */
35032     closeText : "Close this tab"
35033 });